diff --git a/AGENTS.md b/AGENTS.md index 27fb3f3..901353f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -176,6 +176,66 @@ The project is small enough that direct read is always the right choice regardle --- +## Senior engineering reflexes + +The rules above are the always-on core. The reflexes below are the full senior playbook — apply them proactively, without being asked, scaled to the project's language and risk. They are the canonical source: per-tool configs (`CLAUDE.md`, MiniMax `agent.md`, `.cursorrules`) should reference this file rather than re-state these rules. + +### Documentation & decisions + +- **ADR** (Architecture Decision Record) — documents a decision *already made*. Retrospective, in `docs/adr/NNNN-short-title.md`: Context · Decision · Rejected alternatives · Consequences. Triggers: new central pattern, lib choice, thread-model constraint, public-API change. +- **RFC** (Request for Comments) — requests feedback *before* a major change. Prospective, in `docs/rfcs/`: Motivation · Detailed proposal · Alternatives · Open questions · Review deadline. +- **`// See ADR-NNNN`** in code — when a block implements a documented decision, link it so a reader reaches the "why" without searching the docs. +- **Documentation proportional to size**: >10 source files → `CLAUDE.md`; >3,000 LOC → `ARCHITECTURE.md` (thread model, data flow, ownership, red zones); >5,000 LOC → `CONTRIBUTING.md` (conventions, how to add a module, PR checklist). +- **Domain glossary** — for any jargon-dense domain (audio, finance, medical, network, games), create `docs/glossary.md` defining terms *operationally* (precise definition + link to the implementing module + concrete in-project example). A dev without domain background introduces subtle bugs by misreading a technical term. +- **Data format versioning & migrations** — every persisted format carries an explicit version + one migration function per delta (`upgradeProjectV6toV7()`). Without migrations a refactor that changes the format makes all old files unreadable. +- **CHANGELOG.md** — on any project with releases, maintain it from Conventional Commits: `## [VERSION] - YYYY-MM-DD` with `### Added/Fixed/Changed/Removed`. + +### Testing + +- **Propose tests at service creation** — when a stateless service / pure business logic is created or extracted, proactively offer a test (don't wait to be asked). Stateless free functions are the highest-priority, easiest wins. +- **Test naming**: `Component_Scenario_ExpectedBehavior` (e.g. `ProjectReader_LoadCorruptedJson_DoesNotCrash`). +- **Three classified suites**: `*_Unit` (pre-commit + CI, zero I/O, <100ms) · `*_Integration` (CI nightly, mocked devices/files) · `*_Device`/`*_AudioDevice` (manual, real hardware). +- **Integration & golden tests** on deterministic outputs: golden (render a known output, compare checksum/RMS), replay (import → edit → undo → render → verify), session-load (load N historical projects → migrations still work). +- **Fuzz & property-based**: fuzz every parser of external data (libFuzzer / `cargo-fuzz`) — malformed input must fail cleanly, never corrupt state silently. Property-based test algorithms with math invariants (`proptest`/`quickcheck`/`rapidcheck`) — e.g. "audio output stays within [-1.0, 1.0] for any input". +- **Invariants as runtime asserts** — every critical invariant documented in ARCHITECTURE.md has a matching `assert()`/`debug_assert!()` in code. An unverified invariant is just a promise. Free in release, immediate detection in debug. +- **Zero-alloc CI check** — any real-time thread has a test asserting `heap_alloc_count == 0` after N iterations. An accidental allocation in a hot path is invisible until user reports ("crash after 2h"). + +### Concurrency & systems + +- **Ownership graph = DAG** — never an ownership cycle. Upward (child→parent) or lateral (sibling→sibling) references use `weak_ptr`/observer/callback, never a strong ref. Destruction order = reverse of construction. +- **Shutdown sequence** — in any multi-threaded system, document in ARCHITECTURE.md which thread is joined first, in what order queues drain, when OS handles are released. A service destroyed while the audio thread holds a reference = guaranteed crash. +- **Lock hierarchy** — document the mandatory acquisition order (e.g. `ProjectMutex → AudioGraphMutex → TrackMutex`). Never acquire a level-N lock while holding level-N+1. Prevents deadlocks; TSan detects violations. +- **Thread annotations** — comment every method with `// THREAD: audio | ui | any` so the model is explicit in code, not only in ARCHITECTURE.md. +- **RT threads** (audio callback, video decode) — no logging, no mutex, no I/O, no allocation. Communicate via a lock-free ring buffer: RT thread pushes `(EventId, timestamp, value)` with atomics; a low-priority thread drains to log/UI. Without it, "it crackles sometimes" reports are undebuggable. +- **Structured logging** — 4 levels (ERROR irrecoverable · WARN degraded · INFO session events · DEBUG off in release). Per-domain macros when justified (`LOG_AUDIO_WARN`). RT threads log only via the ring buffer above. + +### Safety & static analysis + +- **Error handling policy** — never swallow silently. Rust: `unwrap()`/`expect()` forbidden in prod except a proven invariant with `// SAFETY:`; prefer `?`/`map_err()`. C++: prefer return codes / `std::optional`/`std::expected` in critical code; never empty `catch(...)`. Errors at system boundaries (I/O, network, user parsing) always handled explicitly. +- **RAII (C++)** — no naked `new`/`delete`; `make_unique`/`make_shared`/stack. FFI opaque handles wrapped in a RAII type immediately (no naked handle circulating). +- **`using namespace` banned at file scope** — in headers (0 exceptions, fully qualify) and production `.cpp` (function scope or explicit alias `namespace fs = std::filesystem;` only). +- **Sanitizers** in dedicated CI builds: ASan (use-after-free, overflow) + UBSan (signed overflow, null deref) can combine; TSan (data races) separate build; MSan (uninit reads). Rust FFI modules: `cargo miri test` (nightly) catches UB at the `extern "C"` boundary that C++ sanitizers miss. +- **Clang-Tidy (C++)** — beyond cppcheck. Priority checks: `bugprone-use-after-move`, `bugprone-dangling-handle`, `performance-unnecessary-copy-initialization`, `modernize-use-override/make-unique`, `readability-function-size`. Ship a `.clang-tidy` + run in pre-commit/CI. +- **Hardware abstraction for testability** — any service touching OS resources consumes an interface, never the hardware directly. Priority interfaces: `IFileSystem`, `IClock` (timers/autosave), `IAudioSink`. Lets CI simulate disk errors / latency without real hardware. + +### Supply chain + +- **`cargo audit --deny warnings`** (RustSec CVE scan of `Cargo.lock`) and **`cargo-deny`** (crate bans, license policy, duplicate versions) on any serious Rust project. +- **`osv-scanner --recursive .`** or **`trivy fs .`** for vendored/system C++ deps (SDL3, ImGui, FFmpeg, codecs). C++ CVEs are rarer but graver (codec overflow = RCE). Nightly CI. +- **CODEOWNERS** — `.github/CODEOWNERS` assigning ownership by domain + mandatory reviewer on frozen cores / public APIs / CI. Create it even solo: it prepares a second dev with zero ambiguity. + +### Process & collaboration + +- **Code review checklist** (before approving any PR): Correctness · Security (secret/injection/missing validation) · Thread safety (shared data protected, atomics correct) · Resources (no leak) · Performance (no alloc in hot path, no avoidable O(n²)) · Readability (a senior understands it in 30s) · Tests (logic covered / no broken test). +- **Performance budgets** — document per subsystem and check in CI: audio callback <2ms · UI frame <16.6ms (60fps) · undo/redo <50ms · project load <3s · heavy ops (scan, waveform) async non-blocking. +- **Tech debt SLA** — build/clippy warning: immediate (don't commit) · race condition: 24h · architecture violation: 7 days · legacy TODO: next sprint. "Stop-the-line" on the first two. +- **Feature flags** — isolate unfinished/experimental code behind a runtime flag (preferred, `config.json`) or compile-time `#ifdef` with `// FEATURE: ... — remove when: ...`. `#if 0` is forbidden (that's dead code — delete it or use a real flag). +- **Public interface contracts** (exception to "comments = WHY only") — public interface headers document non-inferable contracts in one line: `// @pre Must NOT be called from audio thread`, `// @thread-safety lock-free, MT-safe`, `// @throws never (noexcept)`. +- **FFI conventions (C++ ↔ Rust)** — the most dangerous boundary. Every `extern "C"`: return an `int32_t`/`ResultCode` error code (never implicit); complex errors via a thread-local `get_last_error_str()`; ownership documented explicitly (`Box::into_raw()` → C++ `unique_ptr` with a deleter calling back into Rust; never `free()` C++-side on Rust-allocated memory). Capture conventions in an "Interop Error Handling + Memory Ownership" ADR. +- **Boy Scout rule** — when editing a file and you spot neighbouring debt fixable in <15 min (un-injected global, over-long function, untested helper), fix it in the same commit with a note. If >15 min: create a TODO/ticket and move on. + +--- + ## Pre-commit checklist Before marking any task done: diff --git a/PORTABILITY.md b/PORTABILITY.md new file mode 100644 index 0000000..427c926 --- /dev/null +++ b/PORTABILITY.md @@ -0,0 +1,128 @@ +# Portability — Transfer the full stack to a new machine or LLM + +This guide makes the whole AI-Native Dev Stack reproducible: clone the repo and +wire each AI agent (Claude Code, MiniMax/Mavis, Cursor, Codex, …) to the same +engineering method, hooks, and agents. Nothing here depends on a specific +machine except the few values explicitly listed as machine-local. + +## The 3-layer model (why some things live in the repo and some don't) + +Per-tool configs mix three natures that must not travel together: + +| Layer | What | Where it lives | Shared? | +|---|---|---|---| +| **1 — Engineering method** | The universal rules + senior reflexes | [`AGENTS.md`](AGENTS.md) (this repo) | ✅ canonical, shared to every agent | +| **2 — Tool mechanics** | Skills/commands of a given tool (gstack, graphify, MCP wiring) | Per-agent appendix (this repo's adapters + each tool's config) | ⚠️ per-agent | +| **3 — Personal / machine** | Vault paths, project list, machine PATH, "answer in French", wikilink rules | The agent's own config file (`~/.claude/CLAUDE.md`, Mavis `agent.md`) | ❌ never shared | + +**Rule:** Layer 1 has exactly one owner — `AGENTS.md`. Every tool config +*references* it (`@AGENTS.md` include) instead of re-stating the rules. That is +what keeps the three configs from diverging. If you copy the rules into a tool +config by hand, you have just created a fork that will rot. + +## What is in the repo (transferable) vs machine-local + +| In the repo (clone = you have it) | Machine-local (set up once per machine) | +|---|---| +| `AGENTS.md` — the complete method (Layer 1) | `~/.claude/CLAUDE.md` — Layer 3 + `@AGENTS.md` include | +| `routing-guide.md` — analysis/orchestration routing | Mavis `~/.mavis/agents/mavis/agent.md` — Layer 3 + method ref | +| `hooks/` — universal hooks + per-agent install notes | Obsidian vault (`config.sh` paths, API key in env) | +| `scripts/` — `setup-agents.sh`, `loc_gate.ps1`, `vault_sync*` | The anti-debt agent *link* (created by `setup-agents.sh`) | +| `stack/agents/anti-debt/` — the debt agent + adapters | The hook *registrations* (per-agent, formats differ) | +| `tools/ai_docs/`, `skills/`, `templates/`, `install.sh` | `tools/ai_docs/config.sh` (git-ignored, per-machine) | + +--- + +## New-machine bootstrap (the whole sequence) + +```bash +# 1. Clone +git clone https://github.com/Rwanbt/ai-native-dev-stack.git +cd ai-native-dev-stack + +# 2. Link the portable agents (anti-debt) into every detected agent root. +# Idempotent, OS-aware (symlink on Linux/macOS, junction on Windows). +bash scripts/setup-agents.sh + +# 3. Wire the engineering method into each agent config (Layer 1 include) — see per-agent below. + +# 4. (Per project) install the AI-docs maintenance stack into a target repo: +cd /path/to/your/project +bash /path/to/ai-native-dev-stack/install.sh +``` + +Then set the machine-local values: +- `OBSIDIAN_API_KEY` / `OBSIDIAN_API_URL` env vars (used by the memory hooks). +- `tools/ai_docs/config.sh` in each project (Obsidian vault path, graphify, Claude memory key). + +--- + +## Per-agent setup + +### Claude Code + +1. **Method (Layer 1)** — add near the top of `~/.claude/CLAUDE.md` (global) or a project `CLAUDE.md`: + ``` + @/absolute/path/to/ai-native-dev-stack/AGENTS.md + ``` + Keep only Layer 3 (personal/machine) and a Layer-2 appendix (gstack/graphify) in `CLAUDE.md` itself. +2. **Anti-debt agent** — `scripts/setup-agents.sh` links it to `~/.claude/skills/anti-debt`. Activate by loading `@~/.claude/skills/anti-debt/AGENT.md` and running its tools (see [adapter](stack/agents/anti-debt/adapters/claude-code/README.md)). Note: `/skill anti-debt:...` does **not** work (Claude discovers skills flat). +3. **Hooks** — register in `~/.claude/settings.json` (or project `.claude/settings.json`), absolute paths: + ```json + { + "hooks": { + "SessionStart": [{ "matcher": "", "hooks": [{ "type": "command", + "command": "node /abs/path/ai-native-dev-stack/hooks/session-start-memory/run.js" }]}], + "PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", + "command": "bash /abs/path/ai-native-dev-stack/hooks/posttool-ai-summary/run_hook.sh" }]}] + } + } + ``` + See [hooks/README.md](hooks/README.md) for the full list and per-hook notes. + +### MiniMax (Mavis) + +1. **Method (Layer 1)** — Mavis reads its agent's `agent.md`. Reference `AGENTS.md` from it (or include its content) and keep only Mavis-specific Layer 3 there. Do **not** maintain a hand-ported copy of the rules — point at `AGENTS.md`. +2. **Anti-debt agent** — `setup-agents.sh` links it to `~/.mavis/agents/anti-debt`. Run its tools directly (see [adapter](stack/agents/anti-debt/adapters/minimax-code/README.md)): + ```bash + python3 ~/.mavis/agents/anti-debt/skills/debt-scan/tools/scan_code.py + ``` +3. **Hooks** — Mavis supports SessionStart/SessionEnd natively. Create them from the `.md` definitions: + ```bash + mavis hook create session-start-memory --event SessionStart --type script --agent mavis + mavis hook create session-end-save --event SessionEnd --type script --agent mavis + # PreToolUse LOC gate: ~/.mavis/agents/mavis/hooks/pretool-loc-gate.md + ``` + See per-hook notes in [hooks/](hooks/). + +### Cursor + +- **Method** — Cursor reads `AGENTS.md` natively at the repo root (and in nested dirs). Cloning the repo into a project, or placing `AGENTS.md` at the project root, is enough. No include needed. +- **Hooks / anti-debt** — run the anti-debt tools manually; Cursor has no SessionStart hook equivalent (use `.cursorrules` for any always-on note). + +### Codex + +- **Method** — Codex auto-loads `AGENTS.md` at the project root. Same as Cursor. +- **Hooks** — `~/.codex/hooks.json`, same format as Claude Code. + +--- + +## Verifying a transfer + +```bash +# Method present and complete: +grep -c "Senior engineering reflexes" AGENTS.md # → 1 + +# Agents linked: +bash scripts/setup-agents.sh --dry-run # → "already linked correctly" per agent + +# Anti-debt runs through an agent path (Claude example): +python3 ~/.claude/skills/anti-debt/skills/debt-scan/tools/scan_code.py . + +# AI-docs stack in a project: +# In Claude Code: /verify-ai-docs → OPERATIONAL +``` + +A correct transfer means: each agent loads `AGENTS.md` (Layer 1), the anti-debt +agent is linked and runnable, the memory hooks are registered, and the only +hand-edited per-machine file is the agent's own Layer-3 config + `config.sh`. diff --git a/README.md b/README.md index fb150a0..59f3bd4 100644 --- a/README.md +++ b/README.md @@ -28,16 +28,20 @@ The usual workarounds (pasting files into context, writing long prompts) don't s A **self-maintained AI optimization stack** — a set of structured documents, scripts, and hooks that keeps the AI perpetually oriented without human intervention: ``` +Engineering method (AGENTS.md) ← single canonical source, shared to every LLM Per-module AI context files ← updated on every file edit Dependency graph (graphify) ← re-indexed on demand Domain rules (standalone) ← single file injected for critical code Obsidian memory vault ← persistent second brain across sessions Claude Code memory ← auto-generated session summaries Skills ecosystem ← domain-specific verification commands +Universal hooks ← session memory load/save, LOC gate (any agent) +Anti-debt agent ← deterministic tech-debt governance PostToolUse hook ← keeps everything in sync automatically ``` -One command audits the entire stack: `/verify-ai-docs` +One command audits the entire stack: `/verify-ai-docs`. Transfer it to any +machine or LLM via **[PORTABILITY.md](PORTABILITY.md)**. --- @@ -355,6 +359,24 @@ python tools/ai_docs/generate_all.py # 6. Verify → /verify-ai-docs should display OPERATIONAL ``` +### Whole-stack transfer (method + hooks + agents, any LLM) + +The steps above set up the per-project AI-docs stack. To transfer the **full +method** to a new machine or wire a new AI agent (Claude Code, MiniMax/Mavis, +Cursor, Codex) to the same rules, hooks, and the anti-debt agent: + +```bash +# Link the anti-debt agent into every detected AI agent (idempotent, OS-aware) +bash scripts/setup-agents.sh + +# Then wire the engineering method (@AGENTS.md include) + hooks per agent: +# → see PORTABILITY.md +``` + +The single source of the engineering method is [`AGENTS.md`](AGENTS.md) — every +tool config references it instead of re-stating the rules, so the configs never +diverge. Full guide: **[PORTABILITY.md](PORTABILITY.md)**. + --- ## Quality Standards — AI Optimization and Human Readability diff --git a/install.sh b/install.sh index 5d982d1..d35e028 100644 --- a/install.sh +++ b/install.sh @@ -4,15 +4,19 @@ # Usage: # bash install.sh [--project-root /path/to/project] [--with-gstack] [--skip-gstack] # -# What this does: +# What this does (per-project AI-docs stack): # 1. Copies scripts to tools/ai_docs/ -# 2. Copies the verify-ai-docs skill to .claude/skills/ -# 3. Installs gstack (global Claude Code skills by Garry Tan / YC) — optional -# 4. Creates config.sh from the template -# 5. Detects Python and validates it works -# 6. Generates all AI_SUMMARY.md files -# 7. Adds config.sh to .gitignore -# 8. Prints next steps +# 2. Copies the verify-ai-docs + verify-standards skills to .claude/skills/ +# 3. Copies AGENTS.md (the canonical engineering method) to the project root +# 4. Installs gstack (global Claude Code skills by Garry Tan / YC) — optional +# 5. Creates config.sh from the template +# 6. Detects Python and validates it works +# 7. Generates all AI_SUMMARY.md files +# +# This installer covers the PER-PROJECT stack. The GLOBAL, multi-agent setup +# (engineering-method include, universal hooks, the anti-debt agent link for +# Claude Code / MiniMax / Cursor / Codex) is described in PORTABILITY.md and +# scripted by scripts/setup-agents.sh. See the NEXT STEPS printed at the end. set -e @@ -219,7 +223,13 @@ echo "" echo "5. Verify the full stack:" echo " In Claude Code: /verify-ai-docs && /verify-standards" echo "" +echo "6. GLOBAL multi-agent setup (one-time per machine):" +echo " - Link the anti-debt agent into every AI agent:" +echo " bash $SCRIPT_DIR/scripts/setup-agents.sh" +echo " - Wire the engineering method + hooks per agent (Claude/MiniMax/Cursor/Codex):" +echo " see $SCRIPT_DIR/PORTABILITY.md" +echo "" if [ "$INSTALL_GSTACK" = "yes" ]; then - echo "6. Add the gstack skills block to your CLAUDE.md (see above)." + echo "7. Add the gstack skills block to your CLAUDE.md (see above)." echo "" fi diff --git a/scripts/setup-agents.sh b/scripts/setup-agents.sh new file mode 100644 index 0000000..0ff0565 --- /dev/null +++ b/scripts/setup-agents.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# setup-agents.sh — Link the stack's portable agents into each AI agent's root. +# +# The anti-debt agent must be linked as a WHOLE directory (its scanners +# reference ../tools and ../kg relatively, so linking skills/ alone breaks +# resolution). Path.resolve() follows links/junctions, so a single link works. +# +# Idempotent: skips a target that already points at the right place. +# Cross-platform: ln -s on Linux/macOS, a directory junction on Windows +# (junctions need no admin rights and Path.resolve() follows them). +# +# Usage: +# bash scripts/setup-agents.sh # link into every detected agent root +# bash scripts/setup-agents.sh --dry-run # show what would happen, do nothing +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +STACK_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +ANTI_DEBT_SRC="$STACK_ROOT/stack/agents/anti-debt" +DRY_RUN="" +[ "$1" = "--dry-run" ] && DRY_RUN="yes" + +# Agent roots: "