- Add new how-to guide covering hash updates for git dependencies - Applies to any git dependency (e.g., nostr-sdk fork) - Add critical note in AGENTS.md linking to this guide - Emphasize that hash updates in both flake.nix and nix/module.nix are MANDATORY
9.4 KiB
AGENTS.md
This file provides guidance to agents when working with code in this repository.
Project Structure
Workspace with Two Rust Projects:
- Root:
ngit-grasp(main GRASP relay implementation) grasp-audit/: Separate subproject with ownCargo.tomlandflake.nix
Cannot build grasp-audit from root - must cd grasp-audit first.
Build & Test
Nix Flakes (Non-Standard)
CRITICAL: Use nix develop, NOT nix-shell (we use flake.nix, not shell.nix)
# ✅ Correct
cd grasp-audit
nix develop -c cargo build
nix develop -c cargo test
# ❌ Wrong
nix-shell
nix-shell --run "cargo build"
Testing ngit-grasp (Main Project)
ngit-grasp integration tests use the TestRelay fixture:
The TestRelay fixture automatically starts an instance of ngit-grasp itself and manages its lifecycle:
# Run all ngit-grasp tests (from project root)
cargo test
# Run integration tests only
cargo test --test '*'
# Run specific test file
cargo test --test nip01_compliance
How TestRelay works:
- Spawns
ngit-graspbinary on a random available port - Creates temporary directories for git and relay data
- Provides
url()anddomain()methods for test clients - Automatically cleans up on drop
Example test pattern:
use common::TestRelay;
#[tokio::test]
async fn test_something() {
let relay = TestRelay::start().await;
// relay.url() returns "ws://127.0.0.1:{port}"
// ... run test against ngit-grasp ...
relay.stop().await;
}
Summary: Which Test Command for What
| What you're testing | Command |
|---|---|
| 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 |
Running Single Test
# 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
Troubleshooting
Buffer Size Errors: If you see mpsc channel buffer size panics on first test run, this is usually transient. Simply run the tests again.
Port Conflicts:
Both TestRelay and test-ngit-relay.sh use random ports to avoid conflicts. If you see port errors, ensure no stale processes are running.
Code Patterns
nostr-sdk 0.43 Breaking Changes (vs 0.35)
Field access, not method calls:
// ❌ WRONG (0.35 API)
event.id()
event.tags()
for tag in &event.tags { }
// ✅ CORRECT (0.43 API)
event.id // Direct field access
event.tags // Direct field access
event.tags.iter() // Iterator method
Tag API changed:
// ❌ WRONG (0.35)
Tag::Generic(TagKind::Custom("clone".into()), vec![...])
// ✅ CORRECT (0.43)
Tag::custom(TagKind::custom("clone"), vec![...])
EventBuilder signature changed:
// ❌ WRONG (0.35)
EventBuilder::new(kind, content, &[tags])
// ✅ CORRECT (0.43)
EventBuilder::new(kind, content).tags(tags)
Audit Event Tagging (grasp-audit)
All audit events automatically include cleanup tags:
The grasp-audit system automatically adds three tags to every event for production cleanup and test isolation. These tags are added transparently via AuditEventBuilder::build() with 100% coverage through AuditClient::event_builder().
Automatic Tags (no manual intervention needed):
// These tags are automatically added to EVERY audit event:
["t", "grasp-audit-test-event"] // Identifies all audit test events
["t", "audit-{run_id}"] // Unique ID for this audit run (correlates events)
["t", "audit-cleanup-after-{unix_timestamp}"] // Unix timestamp for cleanup scheduling
Tag Format Details:
- Uses standard NIP-01
"t"(hashtag) tags for maximum compatibility - Unix timestamps (not ISO 8601) for easier database queries
- All tags added automatically when calling
client.event_builder().build() - No manual tag management required
Verifying Tags in Tests:
// Test that verifies automatic tag addition:
// See: grasp-audit/src/client.rs:273-302
#[test]
fn test_audit_tags_automatically_added() {
// Creates event and verifies all three tags are present
}
Testing Implications:
- All audit events are tagged for easy cleanup
- Use
run_idtag to correlate events from same audit run - Tags enable production relay cleanup scripts
- No special handling needed in test code - tags are automatic
Configuration Management
⚠️ CRITICAL: Keep Configuration in Sync Across All Four Sources
Configuration options must be consistent across four locations:
- Source code (
src/config.rs) - Defines actual config structs, env vars, and defaults - Documentation (
docs/reference/configuration.md) - User-facing reference for all options - NixOS module (
nix/module.nix) - NixOS deployment configuration - Example env file (
.env.example) - Template for development and Docker deployments
When adding/modifying ANY configuration option:
- Update
src/config.rs- Add/modify the field with proper env var name - Update
docs/reference/configuration.md- Document the option with examples and defaults - Update
nix/module.nix- Add/modify the NixOS option ininstanceOptions - Update
.env.example- Add the option with comments explaining usage and defaults - Verify consistency - Check env var names, defaults, and descriptions match exactly
Critical consistency checks:
- Environment variable names must match:
NGIT_*in code, docs, module, and .env.example - Default values must match across all four sources
- Option names should be consistent (snake_case in code, camelCase in NixOS)
- Descriptions should be similar (can be more detailed in docs)
Example: Adding a new config option
// 1. src/config.rs
#[arg(long, env = "NGIT_NEW_OPTION", default_value_t = 42)]
pub new_option: u32,
<!-- 2. docs/reference/configuration.md -->
#### `NGIT_NEW_OPTION`
**Description:** What this option does
**Type:** Integer
**Default:** `42`
**Required:** No
# 3. nix/module.nix (in instanceOptions)
newOption = mkOption {
type = types.int;
default = 42;
description = "What this option does";
};
# Also add to environment mapping in mkService:
environment = {
# ...
NGIT_NEW_OPTION = toString cfg.newOption;
};
# 4. .env.example
# What this option does
# CLI: --new-option <value>
# Default: 42
# NGIT_NEW_OPTION=42
Documentation
⚠️ CRITICAL: Keep Architecture Docs Updated
Architecture and design documents are LIVING DOCUMENTS. When implementation diverges from the documented plan:
- Update the doc IMMEDIATELY - Don't wait until "later"
- Document what was actually built, not what was originally planned
- Note why decisions changed - Future readers need this context
- Files to watch:
docs/explanation/architecture.md,docs/explanation/decisions.md
This was a key learning from GRASP-01: docs described plans, not implementation, causing confusion.
Diátaxis Framework Used:
docs/tutorials/- Learning-orienteddocs/how-to/- Task-orienteddocs/reference/- Information-orienteddocs/explanation/- Understanding-oriented
Session files go in work/ (gitignored except README.md)
- Archive valuable content to
docs/archive/YYYY-MM-DD-*.mdat session end - Delete temporary files
- Keep root clean (only README.md, AGENTS.md)
Critical Gotchas
- Workspace compilation: Can't
cargo buildfrom root for grasp-audit - Nix environment: Must use
nix develop, notnix-shell - nostr-sdk API: Fields not methods in 0.43
- Test isolation: Integration tests use
TestRelay(ngit-grasp) ortest-ngit-relay.sh(ngit-relay) - Work directory: All session docs go in
work/, NOT root - Archive naming: Use
YYYY-MM-DD-description.mdformat - test-ngit-relay.sh tests ngit-relay: This script tests the reference implementation, NOT ngit-grasp
- Configuration sync: Config changes MUST be updated in all four places:
src/config.rs,docs/reference/configuration.md,nix/module.nix, AND.env.example - Git dependencies: When updating
Cargo.tomlorCargo.lockwith ANY git dependency (not crates.io), you MUST update hashes in BOTHflake.nixandnix/module.nix. See docs/how-to/update-git-dependencies.md - this is MANDATORY and builds will fail if skipped
File Restrictions by Mode
Code mode can only edit files matching specific patterns (enforced by system):
- Example: Architect mode restricted to
\.md$files only - Attempting to edit restricted files causes FileRestrictionError
- Check mode configuration if edit attempts fail unexpectedly
Quick Reference
# Test ngit-grasp (main project)
cargo test
# Build grasp-audit
cd grasp-audit && nix develop -c cargo build
# Run grasp-audit unit tests
cd grasp-audit && nix develop -c cargo test --lib
# Check session files
ls work/ # Should only have README.md when clean