From 639ed8aab3df864b0906243efc240a4c81496442 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:27:23 +1000 Subject: [PATCH 01/14] chore(porch): 1011 init pir --- .../status.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml diff --git a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml new file mode 100644 index 000000000..1a8d94b9a --- /dev/null +++ b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml @@ -0,0 +1,18 @@ +id: '1011' +title: agent-farm-inline-protocol-md- +protocol: pir +phase: plan +plan_phases: [] +current_plan_phase: null +gates: + plan-approval: + status: pending + dev-approval: + status: pending + pr: + status: pending +iteration: 1 +build_complete: false +history: [] +started_at: '2026-06-08T02:27:23.467Z' +updated_at: '2026-06-08T02:27:23.468Z' From a09f706ab74121c9f8a4aeee8e41e7a2f74b184a Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:30:56 +1000 Subject: [PATCH 02/14] [PIR #1011] Plan draft --- .../1011-agent-farm-inline-protocol-md-.md | 148 ++++++++++++++++++ codev/state/pir-1011_thread.md | 14 ++ 2 files changed, 162 insertions(+) create mode 100644 codev/plans/1011-agent-farm-inline-protocol-md-.md create mode 100644 codev/state/pir-1011_thread.md diff --git a/codev/plans/1011-agent-farm-inline-protocol-md-.md b/codev/plans/1011-agent-farm-inline-protocol-md-.md new file mode 100644 index 000000000..e1bc6c605 --- /dev/null +++ b/codev/plans/1011-agent-farm-inline-protocol-md-.md @@ -0,0 +1,148 @@ +# PIR Plan: Inline `protocol.md` into the builder prompt at spawn + +## Understanding + +Spec 618 correctly moved framework files (protocols, roles, resources) out of user +projects and into the embedded package skeleton, resolved at runtime via the four-tier +resolver (`.codev/` → `codev/` → cache → skeleton). The resolver works. + +The remaining bug is **consumer-side**: every protocol's `builder-prompt.md` tells the +builder to read `codev/protocols//protocol.md` — a literal path that does not exist +on disk in a fresh post-618 install (the file lives only in the embedded skeleton, reachable +through the resolver but *not* through a raw `cat`). So a freshly-spawned builder runs the +`cat`, gets "No such file", and wastes 1–3 minutes hunting before proceeding without the +protocol meta-doc (workflow overview, gate semantics, when-to-use guidance). + +The per-phase prompts (delivered via `porch next` JSON, resolver-mediated) already cover the +actionable per-phase work, so they are *not* affected. The gap is purely the one-time meta-doc. + +Verified in this worktree: + +- `codev-skeleton/protocols/*/builder-prompt.md` contain literal `codev/protocols//protocol.md` + instructions — confirmed in all 10 protocol templates (e.g. `spir/builder-prompt.md:30`, + `pir/builder-prompt.md:30` and `:90`). +- `loadBuilderPromptTemplate()` at `packages/codev/src/agent-farm/commands/spawn-roles.ts:99-108` + loads `builder-prompt.md` via `resolveCodevFile()` but never reads `protocol.md`. +- `resolveCodevFile()` (`packages/codev/src/lib/skeleton.ts`) reaches the embedded skeleton + correctly. The resolver is not the bug. + +## Proposed Change + +Extend `loadBuilderPromptTemplate()` to additionally resolve `protocols//protocol.md` +via `resolveCodevFile()` and append its full text to the returned template under a clearly +delimited heading. The builder then has the meta-doc in its initial prompt context for the +whole session — read once, never re-shipped per phase, and the builder never runs a shell +command that bypasses the resolver. + +Concretely, after loading the `builder-prompt.md` template and before returning it: + +```ts +let template = readFileSync(templatePath, 'utf-8'); + +const protocolDocPath = resolveCodevFile( + `protocols/${protocolName}/protocol.md`, + config.workspaceRoot, +); +if (protocolDocPath) { + template += + `\n\n---\n\n## Protocol Reference (full text)\n\n` + + readFileSync(protocolDocPath, 'utf-8'); +} else { + logger.debug(`No protocol.md found for ${protocolName}; spawning without inlined reference`); +} +return template; +``` + +### Locked plan-gate decisions + +1. **Delimiter / heading.** Append after a horizontal rule under an H2: + `\n\n---\n\n## Protocol Reference (full text)\n\n`. The `---` + distinct heading + keeps the meta-doc visually separate from the per-phase instructions above it, so the two + don't blur in the builder's context. (Matches the issue's proposed wording.) + +2. **Missing `protocol.md` → silently skip, no error**, with a `logger.debug` note. This is + safe because `validateProtocol()` runs *earlier* in the spawn flow and already `fatal()`s + if **both** `protocol.json` and `protocol.md` are absent. So reaching `loadBuilderPromptTemplate` + with a missing `protocol.md` implies `protocol.json` exists — a malformed-but-registered + protocol, not a typo. Spawning without the inline (rather than aborting) is the right + degradation. (Satisfies acceptance criterion #2.) + +3. **Unconditional — no config flag.** The inline cost is ~90–660 lines of markdown delivered + once at spawn; there is no scenario where a user wants the builder to *not* have its own + protocol doc. A flag would be dead configuration. + +### Where the inline happens relative to `renderTemplate()` + +`buildPromptFromTemplate()` passes the returned template through `renderTemplate()`, which does +handlebars substitution, collapses `\n{3,}` → `\n\n`, and trims. Inlining inside +`loadBuilderPromptTemplate()` (per the issue and acceptance criterion #1) means the appended +`protocol.md` also passes through `renderTemplate()`. This is **verified safe today**: a grep +confirms **zero `{{` occurrences across all 8 skeleton `protocol.md` files**, so the substitution +pass is a no-op on the appended text. The only transformation is the newline-collapse, which is +harmless for markdown (single blank lines between blocks are preserved). See Risks for the +forward-looking consideration. + +## Files to Change + +- `packages/codev/src/agent-farm/commands/spawn-roles.ts:99-108` — extend + `loadBuilderPromptTemplate()` to resolve and append `protocol.md` under the + `## Protocol Reference (full text)` delimiter; `logger.debug` when absent. (~12 LOC.) +- `packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts` — in the skeleton-fallback + `describe` block (which already builds a temp skeleton with `spir/builder-prompt.md`), add a + `protocol.md` to the temp skeleton and assert that `buildPromptFromTemplate(...)` output + contains both the rendered template text **and** the `## Protocol Reference (full text)` + heading plus the protocol.md body. Add a second assertion: when no `protocol.md` exists in + the skeleton, the prompt still builds and simply omits the reference section (covers criterion + #2). (~25 LOC of test.) + +No changes to: the resolver, porch, any CLI surface, per-phase prompts, or the +`builder-prompt.md` templates themselves (the literal `cat` instruction can stay — the inlined +copy makes it redundant rather than wrong, and rewriting 10 templates is out of scope per the +issue). + +## Risks & Alternatives Considered + +- **Risk: a future `protocol.md` containing `{{...}}`** (e.g. a protocol doc that documents the + prompt-templating syntax) would be mangled by `renderTemplate()`. + *Mitigation / chosen position:* none of the 8 current docs contain handlebars (verified), and + this is a documented invariant. If a protocol doc ever needs literal `{{`, the one-line fix is + to move the append from `loadBuilderPromptTemplate()` (pre-render) into + `buildPromptFromTemplate()` (post-render), delivering `protocol.md` verbatim. I am *not* doing + that now to keep the change minimal and matching acceptance criterion #1, but flagging it as + the known escape hatch. +- **Alternative: append post-render in `buildPromptFromTemplate()`.** More future-proof against + the handlebars risk, but spreads the prompt-assembly logic across two functions and diverges + from the issue's stated design (criterion #1 names `loadBuilderPromptTemplate`). Rejected as + premature; the escape hatch above covers it if ever needed. +- **Risk: prompt bloat.** SPIR's `protocol.md` is 657 lines (~2K tokens). Delivered once at + spawn, not per phase — acceptable, and far cheaper than the per-phase re-injection the issue + explicitly rejected. +- **Alternative: restore framework file copying on init/update** (the #738 "Option 2"). Out of + scope by the issue's own framing; reverses Spec 618. Not considered here. + +## Test Plan + +**Unit (automated, runs in `npm test` → `@cluesmith/codev`):** + +- New test in `spawn-roles.test.ts` skeleton-fallback block: temp skeleton gets a + `spir/protocol.md` with sentinel content; assert `buildPromptFromTemplate()` output contains + the rendered template, the `## Protocol Reference (full text)` heading, and the sentinel. +- New test: temp skeleton with **no** `spir/protocol.md`; assert the prompt builds without error + and does **not** contain the `## Protocol Reference` heading (covers criterion #2). +- All existing `spawn-roles.test.ts` cases continue to pass unchanged. + +**Build / typecheck:** `npm run build` from the worktree root (routes to +`pnpm --filter @cluesmith/codev build`). + +**Manual (for the human at the `dev-approval` gate):** + +- Inspect the generated prompt directly. Quickest path: run the new unit test and read the + asserted output, or add a throwaway `console.log` of `buildPromptFromTemplate(...)` for a + protocol and confirm the protocol meta-doc is appended under the delimiter. +- Optional end-to-end: in a scratch `codev init` project (no local `codev/protocols/`), spawn a + builder and confirm its initial prompt contains the protocol meta-doc — i.e. the builder no + longer needs to `cat` a non-existent file. (This is the symptom the fix exists for.) + +Note: `.codev/config.json` here has no `worktree.devCommand`, so `afx dev` is not applicable — +this is a spawn-time prompt-assembly change, not a running-app change. The `dev-approval` review +is "read the diff + run the unit test", which is appropriate for this class of change. diff --git a/codev/state/pir-1011_thread.md b/codev/state/pir-1011_thread.md new file mode 100644 index 000000000..65f76454f --- /dev/null +++ b/codev/state/pir-1011_thread.md @@ -0,0 +1,14 @@ +# PIR #1011 — inline protocol.md into builder prompt at spawn + +## Plan phase (2026-06-08) + +Read issue #1011, PIR protocol, and the target code. Investigation findings: + +- Target: `loadBuilderPromptTemplate()` in `packages/codev/src/agent-farm/commands/spawn-roles.ts:99-108`. +- The returned template flows through `renderTemplate()` in `buildPromptFromTemplate()` (handlebars substitution + `\n{3,}→\n\n` collapse + trim). +- Verified: **zero `{{` occurrences across all 8 `codev-skeleton/protocols/*/protocol.md`** files, so appending protocol.md before render is safe today (no accidental substitution). +- `validateProtocol()` already fatals earlier in the spawn flow if BOTH `protocol.json` and `protocol.md` are missing — so by the time we inline, a missing `protocol.md` means the json exists; silently skipping is correct. +- Tests live in `packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts`; the skeleton-fallback `describe` block (issue #706) is the natural home for the new inline-behavior test. +- Plan-gate decisions locked: (1) `---` + `## Protocol Reference (full text)` delimiter, (2) silently skip + debug log when protocol.md absent, (3) unconditional (no config flag). + +Plan written to `codev/plans/1011-agent-farm-inline-protocol-md-.md`. Awaiting plan-approval gate. From a7680c9ec0c5247aee68d79cb3bc3863f64eb235 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:31:06 +1000 Subject: [PATCH 03/14] chore(porch): 1011 plan-approval gate-requested --- codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml index 1a8d94b9a..083ab9238 100644 --- a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml +++ b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml @@ -7,6 +7,7 @@ current_plan_phase: null gates: plan-approval: status: pending + requested_at: '2026-06-08T02:31:06.250Z' dev-approval: status: pending pr: @@ -15,4 +16,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-08T02:27:23.467Z' -updated_at: '2026-06-08T02:27:23.468Z' +updated_at: '2026-06-08T02:31:06.251Z' From f591dba2f7586a7962235ce61a0067317efb7eb0 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:52:55 +1000 Subject: [PATCH 04/14] chore(porch): 1011 plan-approval gate-approved --- .../projects/1011-agent-farm-inline-protocol-md-/status.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml index 083ab9238..7ad2876a2 100644 --- a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml +++ b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml @@ -6,8 +6,9 @@ plan_phases: [] current_plan_phase: null gates: plan-approval: - status: pending + status: approved requested_at: '2026-06-08T02:31:06.250Z' + approved_at: '2026-06-08T02:52:55.657Z' dev-approval: status: pending pr: @@ -16,4 +17,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-08T02:27:23.467Z' -updated_at: '2026-06-08T02:31:06.251Z' +updated_at: '2026-06-08T02:52:55.658Z' From 9c4a44514999b6b82f8644298719cfdad45377f2 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:53:06 +1000 Subject: [PATCH 05/14] chore(porch): 1011 implement phase-transition --- .../projects/1011-agent-farm-inline-protocol-md-/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml index 7ad2876a2..f99aa40db 100644 --- a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml +++ b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml @@ -1,7 +1,7 @@ id: '1011' title: agent-farm-inline-protocol-md- protocol: pir -phase: plan +phase: implement plan_phases: [] current_plan_phase: null gates: @@ -17,4 +17,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-08T02:27:23.467Z' -updated_at: '2026-06-08T02:52:55.658Z' +updated_at: '2026-06-08T02:53:06.467Z' From d814851fbd0f233b211d72f1f9eaf2fcc603fbeb Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:56:00 +1000 Subject: [PATCH 06/14] [PIR #1011] Inline protocol.md into builder spawn prompt --- .../agent-farm/__tests__/spawn-roles.test.ts | 39 +++++++++++++++++++ .../src/agent-farm/commands/spawn-roles.ts | 30 ++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts b/packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts index e955d222f..2fbba9d70 100644 --- a/packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts +++ b/packages/codev/src/agent-farm/__tests__/spawn-roles.test.ts @@ -363,6 +363,45 @@ describe('spawn-roles', () => { expect(prompt).toContain('SPIR prompt for a v3 feature'); }); + it('inlines protocol.md content under a Protocol Reference delimiter (issue #1011)', async () => { + const fs = await import('node:fs'); + const path = await import('node:path'); + fs.writeFileSync( + path.join(skeletonRoot, 'protocols', 'spir', 'protocol.md'), + '# SPIR Protocol\n\nMETA_DOC_SENTINEL: gate semantics and when-to-use guidance.', + ); + + const ctx: TemplateContext = { + protocol_name: 'SPIR', + mode: 'strict', + mode_soft: false, + mode_strict: true, + input_description: 'a v3 feature', + }; + const prompt = buildPromptFromTemplate(makeConfig(), 'spir', ctx); + + // Still carries the rendered builder-prompt template... + expect(prompt).toContain('SPIR prompt for a v3 feature'); + // ...plus the inlined protocol meta-doc under a clear delimiter heading. + expect(prompt).toContain('## Protocol Reference (full text)'); + expect(prompt).toContain('META_DOC_SENTINEL: gate semantics and when-to-use guidance.'); + }); + + it('builds the prompt without error and omits the reference when protocol.md is absent (issue #1011)', () => { + // The skeleton-fallback beforeEach creates spir/ with no protocol.md. + const ctx: TemplateContext = { + protocol_name: 'SPIR', + mode: 'strict', + mode_soft: false, + mode_strict: true, + input_description: 'a v3 feature', + }; + const prompt = buildPromptFromTemplate(makeConfig(), 'spir', ctx); + + expect(prompt).toContain('SPIR prompt for a v3 feature'); + expect(prompt).not.toContain('## Protocol Reference (full text)'); + }); + it('local codev/protocols/ takes precedence over skeleton', async () => { const fs = await import('node:fs'); const path = await import('node:path'); diff --git a/packages/codev/src/agent-farm/commands/spawn-roles.ts b/packages/codev/src/agent-farm/commands/spawn-roles.ts index 807aff34d..f434fcad0 100644 --- a/packages/codev/src/agent-farm/commands/spawn-roles.ts +++ b/packages/codev/src/agent-farm/commands/spawn-roles.ts @@ -101,10 +101,34 @@ function loadBuilderPromptTemplate(config: Config, protocolName: string): string `protocols/${protocolName}/builder-prompt.md`, config.workspaceRoot, ); - if (templatePath) { - return readFileSync(templatePath, 'utf-8'); + if (!templatePath) { + return null; } - return null; + let template = readFileSync(templatePath, 'utf-8'); + + // Inline protocol.md so the builder has the protocol meta-doc in its initial + // context instead of fetching it. Post-Spec-618 fresh installs don't copy + // protocol files locally; the resolver finds protocol.md in the embedded + // skeleton (tier 4), but the builder-prompt's literal `cat codev/protocols/...` + // instruction can't — it would hit an empty path and waste turns hunting. + // Delivering it inline at spawn means the builder never runs a shell command + // that bypasses the resolver, and the meta-doc is in early conversation + // context for the whole session (read once, never re-shipped per phase). + const protocolDocPath = resolveCodevFile( + `protocols/${protocolName}/protocol.md`, + config.workspaceRoot, + ); + if (protocolDocPath) { + template += `\n\n---\n\n## Protocol Reference (full text)\n\n` + + readFileSync(protocolDocPath, 'utf-8'); + } else { + // Missing protocol.md is not fatal here: validateProtocol() runs earlier in + // the spawn flow and already aborts if both protocol.json and protocol.md + // are absent. Reaching this point means the json exists; spawn proceeds + // without the inline reference rather than failing. + logger.debug(`No protocol.md found for ${protocolName}; spawning without inlined reference`); + } + return template; } /** From 0a7fcbbd3c2cf54ae14f7410ca0de030dab380ba Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:56:40 +1000 Subject: [PATCH 07/14] chore(porch): 1011 dev-approval gate-requested --- codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml index f99aa40db..a413abd24 100644 --- a/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml +++ b/codev/projects/1011-agent-farm-inline-protocol-md-/status.yaml @@ -11,10 +11,11 @@ gates: approved_at: '2026-06-08T02:52:55.657Z' dev-approval: status: pending + requested_at: '2026-06-08T02:56:40.545Z' pr: status: pending iteration: 1 build_complete: false history: [] started_at: '2026-06-08T02:27:23.467Z' -updated_at: '2026-06-08T02:53:06.467Z' +updated_at: '2026-06-08T02:56:40.546Z' From b64328f2383b7d8fe9fd190419314e9322b95a37 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 12:57:05 +1000 Subject: [PATCH 08/14] [PIR #1011] Update thread log: implement phase --- codev/state/pir-1011_thread.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/codev/state/pir-1011_thread.md b/codev/state/pir-1011_thread.md index 65f76454f..cf2a2a699 100644 --- a/codev/state/pir-1011_thread.md +++ b/codev/state/pir-1011_thread.md @@ -12,3 +12,12 @@ Read issue #1011, PIR protocol, and the target code. Investigation findings: - Plan-gate decisions locked: (1) `---` + `## Protocol Reference (full text)` delimiter, (2) silently skip + debug log when protocol.md absent, (3) unconditional (no config flag). Plan written to `codev/plans/1011-agent-farm-inline-protocol-md-.md`. Awaiting plan-approval gate. + +## Implement phase (2026-06-08) + +plan-approval approved. Implemented per plan: + +- `spawn-roles.ts`: `loadBuilderPromptTemplate()` now resolves `protocol.md` via `resolveCodevFile()` and appends it under `\n\n---\n\n## Protocol Reference (full text)\n\n`. Missing protocol.md → `logger.debug` + skip (validateProtocol already fatals earlier if both json+md absent). +- `spawn-roles.test.ts`: 2 new tests in the skeleton-fallback block — (1) inlines protocol.md under the delimiter with a sentinel body, (2) builds without error and omits the heading when protocol.md is absent. + +Build ✓ (root `npm run build`), full suite ✓ (3260 passed, 13 pre-existing skips — none mine). Committed + pushed. At dev-approval gate. From 46a18c0de0c557c751608a45951af271fa05cab3 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Mon, 8 Jun 2026 13:31:38 +1000 Subject: [PATCH 09/14] [PIR #1011] Revise plan for expanded framework-file class scope (A.1/A.2/A.3) --- .../1011-agent-farm-inline-protocol-md-.md | 265 +++++++++--------- 1 file changed, 135 insertions(+), 130 deletions(-) diff --git a/codev/plans/1011-agent-farm-inline-protocol-md-.md b/codev/plans/1011-agent-farm-inline-protocol-md-.md index e1bc6c605..6ab23f4f8 100644 --- a/codev/plans/1011-agent-farm-inline-protocol-md-.md +++ b/codev/plans/1011-agent-farm-inline-protocol-md-.md @@ -1,148 +1,153 @@ -# PIR Plan: Inline `protocol.md` into the builder prompt at spawn +# PIR Plan: Deliver framework files via resolver-aware channels (fresh-install class fix) + +> **Scope note (2026-06-08):** Issue #1011 was expanded mid-plan from "inline protocol.md +> at spawn" to the whole class of framework-file literal-path references reachable from +> builder-side consumers (sub-cases A.1 / A.2 / A.3). This plan supersedes the original +> narrow version. Patch 1 (A.1) is already implemented and sitting at the `dev-approval` +> gate; this revision adds Patch 2 (A.2) and the A.3 disposition. The project-bootstrap gap +> (`codev/resources/` not created on init) is explicitly **out of scope** (separate issue). ## Understanding -Spec 618 correctly moved framework files (protocols, roles, resources) out of user -projects and into the embedded package skeleton, resolved at runtime via the four-tier -resolver (`.codev/` → `codev/` → cache → skeleton). The resolver works. +Spec 618 moved framework files into the package skeleton (resolver tier 4). The resolver +(`resolveCodevFile`, `skeleton.ts:63`) reaches them correctly. The bug is **consumer-side**: +prompts, role docs, and protocol docs reference framework files by **literal path**, which a +raw shell `cat`/`cp` cannot resolve when the file lives only in the embedded skeleton (fresh +post-618 installs). The builder hits "No such file" and wastes turns hunting. + +Three observed sub-instances: + +- **A.1 — protocol meta-doc.** All 9 `builder-prompt.md` files instruct `codev/protocols//protocol.md`; `roles/builder.md:83` has a literal `cat codev/protocols/spir/protocol.md`. +- **A.2 — template references.** 4 references to `codev/protocols//templates/`: + - `spir/prompts/plan.md:79`, `aspir/prompts/plan.md:79` — "Use the plan template … if available" + - `experiment/protocol.md:40` — `cp codev/protocols/experiment/templates/notes.md notes.md` + - `spike/protocol.md:55` — "Use the template: `…/templates/findings.md`" +- **A.3 — workflow-reference.** `spir/protocol.md:7` (`> Quick Reference: See codev/resources/workflow-reference.md …`). Once A.1 delivers protocol.md inline, this pointer rides along and itself bypasses the resolver. + +### Key investigation finding (drives decision #2) + +The 4 A.2 references are **heterogeneous**: +- `spir`/`aspir` plan prompts **already embed** a self-contained `### Plan Structure` block + (`spir/prompts/plan.md:81+`) — a *different, simpler* layout than the 184-line canonical + `templates/plan.md`. The "if available" pointer is **redundant chrome**, not load-bearing. +- `experiment` (`notes.md`, 97 lines) and `spike` (`findings.md`, 67 lines) reference + **genuine content** with no inline equivalent in their protocol.md. + +This is why **explicit-embed (B) is correct and auto-detect (A) is wrong**: an auto-inliner +would deliver the 184-line canonical plan template *on top of* the prompt's existing +`### Plan Structure`, giving the builder two conflicting plan layouts. A human embedder drops +the redundant pointer and embeds only genuine content; a regex cannot make that distinction. + +## Locked Decisions (the 5 plan-gate decisions) + +**1 — Delimiter / heading format.** +- Patch 1 (protocol.md): `\n\n---\n\n## Protocol Reference (full text)\n\n` (already implemented). +- Patch 2 embeds (experiment/spike): under a clearly fenced sub-section adjacent to the + reworded instruction, e.g.: + ``` + > The following is the embedded copy of the template, delivered inline so you do + > not need to fetch a file. Recreate the target file from this content. + + +