conductor(track): nagent_review_v3 §4 Project-local roots cluster
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user