From ea8fa94e14b8c0848879b4aac498cc03c4384de2 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 20 Jun 2026 08:14:37 -0400 Subject: [PATCH] =?UTF-8?q?conductor(track):=20nagent=5Freview=5Fv3=20?= =?UTF-8?q?=C2=A74=20Project-local=20roots=20cluster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nagent_review_v3_20260619.md | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/conductor/tracks/nagent_review_20260608/nagent_review_v3_20260619.md b/conductor/tracks/nagent_review_20260608/nagent_review_v3_20260619.md index 0444fb7c..731c8df3 100644 --- a/conductor/tracks/nagent_review_20260608/nagent_review_v3_20260619.md +++ b/conductor/tracks/nagent_review_20260608/nagent_review_v3_20260619.md @@ -177,7 +177,76 @@ The case-study methodology cluster (§9) abstracts the harness pattern itself: t ## §4 Project-local roots -(filled in by Phase 5 — covers `54c8741`, `557dd39`, `0b9d1a2`, `023e23a`) +**Source:** nagent `54c8741`, `557dd39`, `0b9d1a2`, `023e23a` (`bin/helpers/nagent_cli.py:11-86` + `:109-141`, `bin/helpers/nagent_llm.py:55-72`, `bin/nagent:640-748` + `:2075-2295`, `.gitignore`, `README.md:344-372` + `:400-410` + `:812-832` + `:841-849`, `prompts/create-readme.md`, `issues/0001-foundations.md`). +**One-liner:** The default root moves into the project. Conversations, knowledge, per-file memory, and graduated tools now live at `{git-toplevel}/.nagent/` and can be committed and shared. Inputs resolve through four layers (install → user → project → root) with once-per-directory dedup; most specific layer shadows. +**Pattern(s) vs v2.3:** EXTENDS v2.3 Pattern 3 ("conversations are editable state") — conversations are now project-scoped by default, not user-scoped. EXTENDS v2.3 Pattern 7 ("repo history as data") — `.nagent/` contents are reviewable in the same pull request as the code they describe. NEW pattern: 4-layer resolution (install/user/project/root) with most-specific-shadowing for prompts, tools, and config. The rename `nagent-gc` → `nagent-distill` is not a typo; it codifies the operation's true semantic ("knowledge becomes capability, gated by review", per `prompts/create-readme.md:249`). +**Manual Slop implications:** Manual Slop already follows this pattern in spirit — `conductor/tracks/` is project-scoped (not `~/.manual_slop/tracks/`); `[conductor].dir` in `manual_slop.toml` allows per-project overrides (per `docs/guide_paths.md`). The .gitignore discipline ("only regenerable artifacts; everything else is the user's call to commit") is a model Manual Slop should adopt: `tests/artifacts/` is gitignored (regenerable); `conductor/tracks/` is committed (the user's review call). The dedup-when-running-from-inside-its-own-checkout invariant (`bin/nagent:657-668`) maps to Manual Slop's load path when running the dev build. +**Decision candidate:** NEW Candidate 20 (LOW). "Rename `nagent-gc` → `nagent-distill` in our documentation cross-references" — this is a documentation-only follow-up; no code change. The mental-model shift ("gc" → "distill") is worth surfacing in the project's `conductor/code_styleguides/knowledge_artifacts.md` styleguide. See `decisions.md` Candidate 20. +**Cross-refs:** none direct. §1 Campaigns (`campaigns/` lives inside the project-local root); §2 Conversation safety net (checkpoints inherit the same scoping); §3 Hooks (hooks are configured per-invocation, not per-root). +**Source-read citations:** +- `bin/helpers/nagent_cli.py:11-13` — `INSTALL_DIR` constant (54c8741) +- `bin/helpers/nagent_cli.py:15-44` — `user_root()`, `git_toplevel()`, `resolve_default_root()` (54c8741) +- `bin/helpers/nagent_cli.py:47-54` — `ensure_root_scaffold()` — creates root on first use + writes `.gitignore` for `splits/` only (54c8741) +- `bin/helpers/nagent_cli.py:57-69` — `resolve_prompt_path()` — 3-layer resolution (project root → user → install) (54c8741) +- `bin/helpers/nagent_cli.py:72-86` — `tool_search_dirs()` — 3-layer resolution with basename shadowing (54c8741) +- `bin/helpers/nagent_cli.py:109-141` — `collect_bin_tool_descriptions()` updated to accept multiple bin dirs (54c8741) +- `bin/helpers/nagent_llm.py:55-72` — `default_config_path()` — CLI → `NAGENT_CONFIG` → project `.nagent/config.json` → `~/.nagent/config.json` (54c8741) +- `bin/nagent:640-748` — `build_initial_context()` — 4-layer context resolution with once-per-directory dedup (54c8741) +- `bin/nagent:2220` — `root = resolve_default_root(args.root)` (54c8741) +- `bin/nagent:2227` — `ensure_root_scaffold(root)` for `--file-edit` (resolving a file-edit writes the index) (54c8741) +- `bin/nagent:2292-2295` — `ensure_root_scaffold(root)` for every path past root-write boundary (54c8741) +- `README.md:344-372` — 4-layer context teaching (557dd39) +- `README.md:400-410` — "Project memory is team memory" reduction (557dd39) +- `README.md:812-832` — file tree rename (54c8741) +- `README.md:841-849` — root + config resolution (557dd39) +- `prompts/create-readme.md` — Part III + Part IV rewrites (557dd39) +- `prompts/create-readme.md:249-251` — new reduction: "Proven playbooks stay prose... graduate them into self-describing tools" (from c1d2cad, surfaced in the project-local-roots teaching because `.nagent/bin/` is where graduated tools land) +- `.gitignore:3-4` — `t?` + `p?` (scratch file patterns) (0b9d1a2) +- `.gitignore:5` — `.nagent/` (nagent's own runtime state is per-machine, not source) (023e23a) +**Honest gaps in this cluster:** +- The `t?` and `p?` patterns at `.gitignore:3-4` (from `0b9d1a2`) are unexplained in the commit message. They are likely scratch files written by nagent (e.g., a temp conversation file `t12345`). A follow-up source-read should identify the producer; without that, the gitignore entry is load-bearing but opaque. +- The "once-per-directory dedup" at `bin/nagent:657-668` uses `Path.resolve()`. If the root is on a symlink or a network mount, resolve may behave unexpectedly across platforms. The dedup invariant is correct for the common case; edge cases are unverified. +- The "project-local" win only pays off when the user commits `.nagent/`. The README at `README.md:400-410` acknowledges this caveat ("conversations contain tool output — review before committing, like any other file") but does not enforce it. A hook or pre-commit guard could surface uncommitted conversations, but that is out of scope for the cluster. + +**Pattern deep-dive.** Project-local roots is a 4-piece composition: **resolve**, **scaffold**, **deduplicate**, **shadow**. `resolve_default_root()` implements the precedence (`--root` > git-toplevel > `~/.nagent`); `ensure_root_scaffold()` creates the root on first use with a minimal `.gitignore` (`splits/` only — every other artifact is the user's commit call); the dedup loop at `bin/nagent:657-668` includes a layer at most once even when directories overlap (running nagent from inside its own checkout, or root being `~/.nagent` outside a repo); the shadow semantics (`tool_search_dirs`, `resolve_prompt_path`, `default_config_path`) encode "most specific layer wins" with later iterations overwriting earlier in a dict. + +The rename `nagent-gc` → `nagent-distill` is the most subtle change in this cluster. The old name borrowed from "garbage collection" — the operation was framed as freeing space. The new name borrows from "distill" — the operation is framed as refining raw working state into reusable knowledge. The merge/graduate passes (from §1 Campaigns cluster, shipped in `f3ec090`) are an explicit consequence: a "gc" mental model would not naturally include a `--graduate` step (gc discards, distill refines). The README at `prompts/create-readme.md:249-251` makes the new reduction explicit: "Proven playbooks stay prose that must be re-read and re-trusted every time. Therefore: graduate them into self-describing tools and prompts — knowledge becomes capability, gated by review." + +A code-shape sketch using survey grammar: + +``` +resolve-root { root_arg, cwd } :: path {ssdl} [S] + if root_arg -> expand(root_arg) + elif git_toplevel(cwd) is not nil -> git_toplevel(cwd) / ".nagent" + else -> ~/.nagent + +resolve-prompt { root, name } :: path + for layer in [root.prompts, ~/.nagent/prompts, INSTALL.prompts] { + if layer/name is file -> return layer/name + } + +resolve-tools { root } :: [path] + by_name := {} + for dir in [INSTALL/bin, ~/.nagent/bin, root/bin] { + for path in dir if is_file { + by_name[path.name] := path + } + } + return sorted(by_name.values()) + +context-layers { install, user, project, root } :: [string] {ssdl} [S] + seen := {} + for dir in [install, user, project, root] { + if resolve(dir) in seen -> continue + seen += resolve(dir) + ctx := load_root_context(dir) + if ctx -> push ctx + } +``` + +The `{ssdl}` markers note the composition: root resolution is a single deterministic string concatenation; context-layer resolution is also a deterministic string assembly with dedup. The non-determinism is bounded to LLM-driven passes (harvest, checkpoint, graduate); the file-resolution paths are pure code. + +The "project memory is team memory" payoff (557dd39's Part IV addition) is the new argument the rename enables: a project's accumulated knowledge can be committed, reviewed, and arrived with via `git clone`. The manual-slop-equivalent argument already holds for `conductor/tracks/`; the nagent version generalizes it to all of `.nagent/`. ## §5 Provider expansion