build(workspace): unify grasp-audit into single workspace

grasp-audit had its own Cargo.lock and flake.nix while also being declared
a member of the root workspace. This dual model let the lockfiles drift:
the root resolved nostr-sdk 0.45.0-alpha.1 while grasp-audit's own lock was
stale at 0.43.0, despite both declaring the same version.

Collapse to a true single workspace:
- Remove grasp-audit/{Cargo.lock,flake.nix,flake.lock}; the root Cargo.lock
  and flake now cover both crates.
- Root flake exposes packages.ngit-grasp and packages.grasp-audit (built via
  cargo -p), with packages.default = ngit-grasp. Single dev shell gains
  gitlint.
- Drop the redundant grasp-audit/target/ (cargo resolves to the root target).
- Update .gitignore, AGENTS.md, README.md, and test-ngit-relay.sh comment to
  describe the single-workspace model (cargo build -p grasp-audit, no cd).

The coupling stays one-directional (ngit-grasp dev-deps use grasp-audit; the
reverse never existed), keeping the door open to split grasp-audit out later.
Not published to crates.io.

Verified: cargo build --workspace, cargo test -p {ngit-grasp,grasp-audit}
--lib (440 + 36 pass), nix build .#ngit-grasp and .#grasp-audit.
This commit is contained in:
DanConwayDev
2026-06-16 14:50:34 +00:00
parent 03b582cf69
commit 90bd544915
8 changed files with 77 additions and 2416 deletions
+1 -2
View File
@@ -1,9 +1,8 @@
# AI assistant context
.ai/
# Rust build artifacts
# Rust build artifacts (single workspace target at repo root)
target/
grasp-audit/target
# Working directory (session-specific temporary files)
work/*
+35 -12
View File
@@ -4,12 +4,30 @@ This file provides guidance to agents when working with code in this repository.
## Project Structure
**Workspace with Two Rust Projects:**
**Single Cargo Workspace with Two Crates:**
- Root: `ngit-grasp` (main GRASP relay implementation)
- `grasp-audit/`: Separate subproject with own `Cargo.toml` and `flake.nix`
- `grasp-audit/`: Audit/compliance tool crate, a member of the same workspace
Cannot build grasp-audit from root - must `cd grasp-audit` first.
Both crates share **one** `Cargo.lock`, **one** `flake.nix`, and **one** `target/`
directory at the repo root. There is no separate lockfile or flake inside
`grasp-audit/`. You can build either crate from anywhere in the workspace; cargo
resolves the workspace root automatically.
```bash
# Build everything
nix develop -c cargo build
# Build / test just the audit crate (from the repo root)
nix develop -c cargo build -p grasp-audit
nix develop -c cargo test -p grasp-audit
```
> grasp-audit has no dependency on ngit-grasp; the coupling is one-directional
> (ngit-grasp's dev-dependencies use grasp-audit). It is co-located only because
> the audit suite's release cadence is currently driven by ngit-grasp features.
> It is structured to be split into its own crate/repo if an external consumer
> ever starts driving its roadmap. Do **not** publish to crates.io yet.
## Build & Test
@@ -17,11 +35,16 @@ Cannot build grasp-audit from root - must `cd grasp-audit` first.
**CRITICAL:** Use `nix develop`, NOT `nix-shell` (we use flake.nix, not shell.nix)
The single root flake exposes both packages and one dev shell:
```bash
# ✅ Correct
cd grasp-audit
# ✅ Correct (one dev shell for the whole workspace)
nix develop -c cargo build
nix develop -c cargo test
nix develop -c cargo build -p grasp-audit
# Build either crate as a Nix package
nix build .#ngit-grasp
nix build .#grasp-audit
# ❌ Wrong
nix-shell
@@ -72,7 +95,7 @@ async fn test_something() {
| --------------------------- | ---------------------------------------------------------------------- |
| ngit-grasp (this project) | `cargo test` from project root |
| ngit-relay (reference impl) | `cd grasp-audit && nix develop -c bash test-ngit-relay.sh --mode test` |
| grasp-audit unit tests | `cd grasp-audit && nix develop -c cargo test --lib` |
| grasp-audit unit tests | `nix develop -c cargo test -p grasp-audit --lib` |
### Running Single Test
@@ -80,8 +103,8 @@ async fn test_something() {
# ngit-grasp test (from project root)
cargo test --test nip01_compliance test_websocket_connection -- --nocapture
# grasp-audit test (from grasp-audit/)
nix develop -c cargo test --lib specific_test_name -- --nocapture
# grasp-audit test (from anywhere in the workspace)
nix develop -c cargo test -p grasp-audit --lib specific_test_name -- --nocapture
```
### Troubleshooting
@@ -264,7 +287,7 @@ This was a key learning from GRASP-01: docs described plans, not implementation,
## Critical Gotchas
1. **Workspace compilation:** Can't `cargo build` from root for grasp-audit
1. **Single workspace:** One `Cargo.lock` and one `flake.nix` at the root cover both crates. Build the audit crate with `cargo build -p grasp-audit` (no `cd` needed).
2. **Nix environment:** Must use `nix develop`, not `nix-shell`
3. **nostr-sdk API:** Fields not methods in 0.43
4. **Test isolation:** Integration tests use `TestRelay` (ngit-grasp) or `test-ngit-relay.sh` (ngit-relay)
@@ -289,10 +312,10 @@ Code mode can only edit files matching specific patterns (enforced by system):
cargo test
# Build grasp-audit
cd grasp-audit && nix develop -c cargo build
nix develop -c cargo build -p grasp-audit
# Run grasp-audit unit tests
cd grasp-audit && nix develop -c cargo test --lib
nix develop -c cargo test -p grasp-audit --lib
# Check session files
ls work/ # Should only have README.md when clean
+4 -9
View File
@@ -582,19 +582,14 @@ nix develop -c cargo test --test nip01_compliance test_nip01_smoke
**2. GRASP Audit Tool (grasp-audit)**
The audit tool tests GRASP compliance of any relay (including ours or external ones).
It is a member of the same workspace, so run it with `-p grasp-audit` from anywhere.
```bash
# Enter grasp-audit directory
cd grasp-audit
# Run unit tests
nix develop -c cargo test
nix develop -c cargo test -p grasp-audit
# Test against any relay (including external ones)
nix develop -c cargo run -- --url wss://relay.example.com
# Or test against any external relay:
nix develop -c cargo run -- --url wss://relay.example.com
nix develop -c cargo run -p grasp-audit -- --url wss://relay.example.com
```
### Development Commands
@@ -670,7 +665,7 @@ ngit-grasp/
│ ├── tutorials/ # Getting started, first steps
│ └── reference/ # API docs, test strategy
├── tests/ # Integration tests (NIP-01, NIP-34, purgatory)
├── grasp-audit/ # Compliance audit subproject
├── grasp-audit/ # Compliance audit crate (workspace member)
└── README.md
```
+34 -7
View File
@@ -18,23 +18,29 @@
};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ rustToolchain pkg-config openssl git ];
# Single dev shell for the whole workspace (ngit-grasp + grasp-audit).
buildInputs = with pkgs; [ rustToolchain pkg-config openssl git gitlint ];
RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library";
shellHook = ''
echo "🚀 ngit-grasp development environment"
echo "🚀 ngit-grasp workspace development environment"
echo "Rust version: $(rustc --version)"
echo ""
echo "Quick commands:"
echo " cargo build - Build the project"
echo " cargo test - Run unit tests"
echo " cargo run - Run the relay"
echo " cargo build - Build the whole workspace"
echo " cargo test - Run ngit-grasp tests"
echo " cargo run - Run the relay"
echo " cargo build -p grasp-audit - Build the audit tool"
echo " cargo test -p grasp-audit - Run grasp-audit unit tests"
echo ""
'';
};
packages.default = pkgs.rustPlatform.buildRustPackage {
# Both crates are members of a single workspace sharing one Cargo.lock.
packages.default = self.packages.${system}.ngit-grasp;
packages.ngit-grasp = pkgs.rustPlatform.buildRustPackage {
pname = "ngit-grasp";
version = "1.1.0";
src = ./.;
@@ -42,6 +48,9 @@
lockFile = ./Cargo.lock;
};
# Only build/test the ngit-grasp package, not the whole workspace.
cargoBuildFlags = [ "-p" "ngit-grasp" ];
nativeBuildInputs = with pkgs; [ pkg-config git ];
buildInputs = with pkgs; [ openssl ];
@@ -49,7 +58,25 @@
# Run lib tests only; integration tests spawn a live relay and require
# network access not available in the Nix sandbox.
doCheck = true;
cargoTestFlags = [ "--lib" ];
cargoTestFlags = [ "-p" "ngit-grasp" "--lib" ];
};
packages.grasp-audit = pkgs.rustPlatform.buildRustPackage {
pname = "grasp-audit";
version = "0.1.0";
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
};
cargoBuildFlags = [ "-p" "grasp-audit" ];
nativeBuildInputs = with pkgs; [ pkg-config git ];
buildInputs = with pkgs; [ openssl ];
# Audit tests require a running Nostr relay; skip in the sandbox.
doCheck = false;
};
})) // {
# NixOS module for deployment
-2222
View File
File diff suppressed because it is too large Load Diff
-96
View File
@@ -1,96 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1781074563,
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1781580018,
"narHash": "sha256-BlTedbM77FmesD2ZqR73vhFy+y77UrhefV7IYw1pDsk=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8bceba21a1ebea535c27c4dc723a0d5a4db9e386",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-66
View File
@@ -1,66 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit system overlays; };
manifest = pkgs.lib.importTOML ./Cargo.toml;
in with pkgs; {
devShells.default = mkShell {
nativeBuildInputs = [
# Rust toolchain
rust-bin.stable.latest.default
# Build tools
pkg-config
# Development tools
gitlint
];
buildInputs = [
# Required dependencies
openssl
];
shellHook = ''
echo "🦀 GRASP Audit development environment loaded"
echo ""
echo "Available commands:"
echo " cargo build - Build the project"
echo " cargo test - Run unit tests"
echo " cargo test --ignored - Run integration tests (needs relay)"
echo " cargo run --example simple_audit - Run example"
echo ""
echo "Rust version: $(rustc --version)"
echo "Cargo version: $(cargo --version)"
echo ""
echo "For RUST_SRC_PATH (rust-analyzer):"
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
'';
};
# Create package for the CLI binary
packages.default = pkgs.rustPlatform.buildRustPackage {
pname = manifest.package.name;
version = manifest.package.version;
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
};
buildInputs = [
openssl
];
nativeBuildInputs = [
pkg-config
];
doCheck = false; # Tests require a running Nostr relay
};
});
}
+3 -2
View File
@@ -1,8 +1,9 @@
#!/bin/bash
set -e
# Change to script's directory to ensure cargo finds grasp-audit/Cargo.toml
# This allows the script to be run from any directory
# Change to script's directory so `cargo run`/`cargo test` select the
# grasp-audit package within the workspace (cargo resolves the workspace
# root automatically). This allows the script to be run from any directory.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"