From 1e47f4faa87af59ec4578e9a236004a6c69992c4 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 05:24:46 +1000 Subject: [PATCH 01/28] chore(porch): 1012 init pir --- .../status.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml new file mode 100644 index 000000000..a71abeaea --- /dev/null +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -0,0 +1,18 @@ +id: '1012' +title: scaffold-codev-init-bootstraps +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-12T19:24:46.703Z' +updated_at: '2026-06-12T19:24:46.703Z' From 9a316b8182b9fdc4ffc6365a702b8ee5c2984917 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 05:27:59 +1000 Subject: [PATCH 02/28] [PIR #1012] Plan draft --- .../1012-scaffold-codev-init-bootstraps.md | 90 +++++++++++++++++++ codev/state/pir-1012_thread.md | 11 +++ 2 files changed, 101 insertions(+) create mode 100644 codev/plans/1012-scaffold-codev-init-bootstraps.md create mode 100644 codev/state/pir-1012_thread.md diff --git a/codev/plans/1012-scaffold-codev-init-bootstraps.md b/codev/plans/1012-scaffold-codev-init-bootstraps.md new file mode 100644 index 000000000..7112aec1f --- /dev/null +++ b/codev/plans/1012-scaffold-codev-init-bootstraps.md @@ -0,0 +1,90 @@ +# PIR Plan: `codev init` bootstraps `codev/resources/` with arch.md + lessons-learned.md starters + +## Understanding + +Fresh `codev init` projects have no `codev/resources/` directory. The review prompts of PIR, SPIR, ASPIR, and MAINTAIN unconditionally read `codev/resources/arch.md` (e.g. `codev-skeleton/protocols/spir/prompts/review.md:152` says "Read the current `codev/resources/arch.md`"), so the first review phase in a fresh project errors on a missing file. + +Root cause: `createUserDirs` in `packages/codev/src/lib/scaffold.ts:23-44` creates only `specs`, `plans`, and `reviews`. Nothing in the init flow (`packages/codev/src/commands/init.ts:79-140`) creates `codev/resources/`. + +These two files are project-specific (each workspace's own architecture and lessons), so the fix is bootstrap-on-init with minimal placeholder content, not resolver fallback (that framework-side concern is #1011). + +### Codebase findings worth noting + +1. **`copyResourceTemplates` (scaffold.ts:113-145) already exists but is dead code.** No command imports it; only `scaffold.test.ts` exercises it. It copies the skeleton's `templates/arch.md` and `templates/lessons-learned.md` (rich, instructional template stubs) plus `cheatsheet.md` and `lifecycle.md` (framework docs, the #1011 side of the audit). It was orphaned when init/adopt moved to the "minimal structure, framework files resolve from the package" model. +2. **`codev update` already never touches `resources/`** — `update.ts` has no reference to the resources dir. That acceptance criterion is satisfied by the status quo; I will add a guard test so it stays that way. +3. **`codev adopt` refuses to run when `codev/` exists** (`adopt.ts:75-77`), so the "don't clobber existing resources" criterion is structurally unreachable today. The new function still implements skip-if-exists semantics per file, so adopt is safe even if that precondition ever loosens. +4. `init.test.ts:74` carries a comment documenting the current behavior ("resources/ is NOT created in minimal structure") — it must be updated with the new expectation. + +## Proposed Change + +Add a new `createResourcesDir(targetDir, options)` function to `packages/codev/src/lib/scaffold.ts`, alongside `createUserDirs` / `createProjectsDir`, that: + +- Creates `codev/resources/` (mkdir recursive). +- Writes `arch.md` and `lessons-learned.md` with the minimal starter content from the issue (inline string constants in scaffold.ts — no skeleton dependency, since the content is project-owned placeholder, not a framework template). +- **Always skips a file that already exists** (per-file, unconditional). Clobbering an existing arch.md is never correct in any flow, so this is not behind the `skipExisting` option flag. +- Returns `{ created: string[], skipped: string[] }` matching the existing result-shape convention. + +Starter content (verbatim from the issue): + +`arch.md`: +```markdown +# Architecture + +This document evolves as the project grows. Update it during the review phase of any work that introduces or changes architectural patterns. + +_No architecture documented yet._ +``` + +`lessons-learned.md`: +```markdown +# Lessons Learned + +Durable engineering wisdom captured across the project's work. Update it during the review phase of any work that surfaces a generally-applicable pattern, gotcha, or constraint. + +_No lessons captured yet._ +``` + +Wire it into both bootstrap commands: + +- `init.ts`: call `createResourcesDir` after `createProjectsDir` (around line 90), print each created file with the existing `+` prefix pattern (`+ codev/resources/arch.md`, `+ codev/resources/lessons-learned.md`), increment `fileCount`. +- `adopt.ts`: same call after `createProjectsDir` (around line 126), same output pattern. Skip-if-exists semantics make this safe by construction. +- `update.ts`: **no change** (resources are project-owned; update must not touch them). + +### Why inline content instead of reviving `copyResourceTemplates` + +The dead `copyResourceTemplates` copies the skeleton's rich template stubs (~60 lines each of "how to use this template" guidance) into every project. The issue deliberately chose trivial placeholders: enough for the first review-phase `Read` to succeed, with a one-line invitation to grow the file. Seeding every workspace with the same heavyweight generic content is exactly what the issue argues against. Inline constants also remove any dependency on skeleton resolution for this path. + +**Open question for the reviewer**: `copyResourceTemplates` remains dead code after this change, and two of the files it copies (`cheatsheet.md`, `lifecycle.md`) belong to the #1011 framework-file story. I propose leaving it untouched here (out of scope) and letting the architect decide whether its removal should be a separate item. If you'd rather I delete it (and its tests) in this PR, say so at this gate. + +## Files to Change + +- `packages/codev/src/lib/scaffold.ts` — add `createResourcesDir` + the two starter-content constants (after `createProjectsDir`, ~line 223). +- `packages/codev/src/commands/init.ts:86-90` — import and call `createResourcesDir`, print created entries. +- `packages/codev/src/commands/adopt.ts:122-126` — same, in the adopt flow. +- `packages/codev/src/__tests__/scaffold.test.ts` — new `describe('createResourcesDir')`: creates dir + both files with starter content; preserves pre-existing files (write sentinel content, call, assert unchanged and reported in `skipped`). +- `packages/codev/src/__tests__/init.test.ts:68-74` — replace the "resources/ is NOT created" note with positive assertions: `codev/resources/arch.md` and `codev/resources/lessons-learned.md` exist and contain the placeholder markers. +- `packages/codev/src/__tests__/adopt.test.ts` — extend the happy-path adopt test to assert resources files are created. +- (Guard) a test asserting `update.ts` source has no resources-write path, or simpler: an update-flow test asserting pre-existing customized `resources/arch.md` content survives `codev update`. I'll use the behavioral form if the update test harness supports it cheaply; otherwise note in the review file that update's no-touch property is by construction (no code path references resources). + +Estimated net diff: ~40 LOC source + ~60 LOC tests. + +## Risks & Alternatives Considered + +- **Alternative: revive `copyResourceTemplates`** (copy skeleton templates at init). Rejected — see "Why inline content" above; also drags `cheatsheet.md`/`lifecycle.md` (framework files, #1011 territory) into project repos. +- **Alternative: resolver fallback to a skeleton default arch.md.** Rejected by the issue itself — these files are project-specific; a shared default defeats the purpose. +- **Alternative: extend `createUserDirs` instead of a sibling function.** Rejected — `createUserDirs` creates empty dirs with `.gitkeep`; resources needs file content and per-file skip semantics. A sibling keeps both functions simple and matches the issue's suggestion. +- **Risk: existing tests assert resources is absent.** `init.test.ts` only carries a comment (no negative assertion), so breakage risk is low; I'll run the full test suite to confirm. +- **Risk: adopt onto a repo with a hand-made `codev/resources/`.** Unreachable today (adopt aborts when `codev/` exists), and the per-file skip makes it safe regardless. + +## Test Plan + +- **Unit (scaffold.test.ts)**: `createResourcesDir` creates `codev/resources/arch.md` + `lessons-learned.md` with the starter markers (`_No architecture documented yet._`, `_No lessons captured yet._`); a pre-existing file with sentinel content is left byte-identical and reported as skipped. +- **Unit (init.test.ts)**: `init --yes` produces `codev/resources/` with both starter files. +- **Unit (adopt.test.ts)**: `adopt --yes` on a plain repo produces both starter files. +- **Unit (update)**: customized `resources/arch.md` survives `codev update` unchanged (or by-construction note, per Files to Change). +- **Build + full suite**: `pnpm --filter @cluesmith/codev build && pnpm --filter @cluesmith/codev test` from the worktree. +- **Manual (for the dev-approval reviewer)**: + 1. From the worktree: `pnpm build`, then run the built CLI: `node packages/codev/dist/src/cli.js init /tmp/pir1012-fresh --yes` (or the bin entry point; exact invocation confirmed at implement time). + 2. Verify the init output lists `+ codev/resources/arch.md` and `+ codev/resources/lessons-learned.md`. + 3. `cat /tmp/pir1012-fresh/codev/resources/arch.md` — starter content present; a `Read` of the path succeeds (the original failure mode). + 4. In an adopt-shaped temp dir (no `codev/`), run adopt with `--yes` and verify the same two files appear. diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md new file mode 100644 index 000000000..d15bcdb8a --- /dev/null +++ b/codev/state/pir-1012_thread.md @@ -0,0 +1,11 @@ +# Builder thread: pir-1012 + +Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.md + lessons-learned.md starters. Protocol: PIR (strict, porch-driven). + +## Plan phase + +- Investigated scaffold flow. Root cause confirmed: `createUserDirs` (scaffold.ts:23) only creates specs/plans/reviews; nothing in init/adopt creates `codev/resources/`. +- Found `copyResourceTemplates` (scaffold.ts:113) is **dead code** — exported, tested, but no command calls it. It would copy the skeleton's rich template stubs plus framework docs (cheatsheet.md, lifecycle.md → #1011 territory). Plan proposes inline minimal starters instead, leaving the dead function untouched (flagged as an open question at the plan gate). +- `codev update` already never touches resources — acceptance criterion satisfied by status quo; adding a guard test. +- `codev adopt` aborts when `codev/` exists, so the "don't clobber" criterion is structurally unreachable; new function uses per-file skip-if-exists anyway. +- Plan written to `codev/plans/1012-scaffold-codev-init-bootstraps.md`. Awaiting plan-approval gate. From 7b29ac8fcf3f4598216d60e9ca49d8ea698b9e75 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 05:28:14 +1000 Subject: [PATCH 03/28] chore(porch): 1012 plan-approval gate-requested --- codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index a71abeaea..b8ef55fe1 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -7,6 +7,7 @@ current_plan_phase: null gates: plan-approval: status: pending + requested_at: '2026-06-12T19:28:14.611Z' dev-approval: status: pending pr: @@ -15,4 +16,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-12T19:24:46.703Z' +updated_at: '2026-06-12T19:28:14.612Z' From 7a856fef670cce7c8c3aa1f337c0b14929a124ab Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 11:40:18 +1000 Subject: [PATCH 04/28] [PIR #1012] Plan revised: update backfills missing-only resources files --- codev/plans/1012-scaffold-codev-init-bootstraps.md | 13 ++++++++----- codev/state/pir-1012_thread.md | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/codev/plans/1012-scaffold-codev-init-bootstraps.md b/codev/plans/1012-scaffold-codev-init-bootstraps.md index 7112aec1f..0906cc74f 100644 --- a/codev/plans/1012-scaffold-codev-init-bootstraps.md +++ b/codev/plans/1012-scaffold-codev-init-bootstraps.md @@ -11,7 +11,7 @@ These two files are project-specific (each workspace's own architecture and less ### Codebase findings worth noting 1. **`copyResourceTemplates` (scaffold.ts:113-145) already exists but is dead code.** No command imports it; only `scaffold.test.ts` exercises it. It copies the skeleton's `templates/arch.md` and `templates/lessons-learned.md` (rich, instructional template stubs) plus `cheatsheet.md` and `lifecycle.md` (framework docs, the #1011 side of the audit). It was orphaned when init/adopt moved to the "minimal structure, framework files resolve from the package" model. -2. **`codev update` already never touches `resources/`** — `update.ts` has no reference to the resources dir. That acceptance criterion is satisfied by the status quo; I will add a guard test so it stays that way. +2. **`codev update` never touches `resources/`** today — `update.ts` has no reference to the resources dir. **Scope amendment (approved at the plan gate)**: update WILL backfill missing-only resources files. Rationale: adopt structurally cannot reach already-initialized projects (it aborts when `codev/` exists), so the population missing `resources/` — projects bootstrapped before this fix — is only ever touched by `update`. Create-if-missing honors the intent of the issue's "update does NOT touch resources" criterion (protecting customizations) while closing the backfill gap: the skip-if-exists semantics make it provably non-clobbering. 3. **`codev adopt` refuses to run when `codev/` exists** (`adopt.ts:75-77`), so the "don't clobber existing resources" criterion is structurally unreachable today. The new function still implements skip-if-exists semantics per file, so adopt is safe even if that precondition ever loosens. 4. `init.test.ts:74` carries a comment documenting the current behavior ("resources/ is NOT created in minimal structure") — it must be updated with the new expectation. @@ -48,7 +48,7 @@ Wire it into both bootstrap commands: - `init.ts`: call `createResourcesDir` after `createProjectsDir` (around line 90), print each created file with the existing `+` prefix pattern (`+ codev/resources/arch.md`, `+ codev/resources/lessons-learned.md`), increment `fileCount`. - `adopt.ts`: same call after `createProjectsDir` (around line 126), same output pattern. Skip-if-exists semantics make this safe by construction. -- `update.ts`: **no change** (resources are project-owned; update must not touch them). +- `update.ts`: call `createResourcesDir` in the every-run refresh section, alongside the `.gitignore` backfill (`update.ts:256`), which already embodies the same create-missing-only philosophy. Created files are pushed to `result.newFiles` and logged with the existing `+ (new)` pattern; existing files are silently left alone (no noise on the common path). Respects `dryRun`: no writes, log missing files as `+ (would create)`. ### Why inline content instead of reviving `copyResourceTemplates` @@ -64,9 +64,10 @@ The dead `copyResourceTemplates` copies the skeleton's rich template stubs (~60 - `packages/codev/src/__tests__/scaffold.test.ts` — new `describe('createResourcesDir')`: creates dir + both files with starter content; preserves pre-existing files (write sentinel content, call, assert unchanged and reported in `skipped`). - `packages/codev/src/__tests__/init.test.ts:68-74` — replace the "resources/ is NOT created" note with positive assertions: `codev/resources/arch.md` and `codev/resources/lessons-learned.md` exist and contain the placeholder markers. - `packages/codev/src/__tests__/adopt.test.ts` — extend the happy-path adopt test to assert resources files are created. -- (Guard) a test asserting `update.ts` source has no resources-write path, or simpler: an update-flow test asserting pre-existing customized `resources/arch.md` content survives `codev update`. I'll use the behavioral form if the update test harness supports it cheaply; otherwise note in the review file that update's no-touch property is by construction (no code path references resources). +- `packages/codev/src/commands/update.ts:~256` — import and call `createResourcesDir` in the refresh section (next to the gitignore backfill), with `dryRun` handling as described above. +- `packages/codev/src/__tests__/update.test.ts` (or the existing update test file) — two cases: (a) update on a project missing `resources/` creates both starter files and reports them in `newFiles`; (b) update on a project with a customized `resources/arch.md` leaves it byte-identical and creates only the missing `lessons-learned.md`. -Estimated net diff: ~40 LOC source + ~60 LOC tests. +Estimated net diff: ~50 LOC source + ~90 LOC tests. ## Risks & Alternatives Considered @@ -75,16 +76,18 @@ Estimated net diff: ~40 LOC source + ~60 LOC tests. - **Alternative: extend `createUserDirs` instead of a sibling function.** Rejected — `createUserDirs` creates empty dirs with `.gitkeep`; resources needs file content and per-file skip semantics. A sibling keeps both functions simple and matches the issue's suggestion. - **Risk: existing tests assert resources is absent.** `init.test.ts` only carries a comment (no negative assertion), so breakage risk is low; I'll run the full test suite to confirm. - **Risk: adopt onto a repo with a hand-made `codev/resources/`.** Unreachable today (adopt aborts when `codev/` exists), and the per-file skip makes it safe regardless. +- **Deviation from the issue as filed**: the issue's acceptance criteria say "update does NOT touch `resources/*`". Amended at the plan-approval gate to create-if-missing backfill (architect-approved), because update is the only command that reaches projects initialized before this fix. Existing content is never modified — the criterion's protective intent is preserved. This deviation will be documented in the review file. ## Test Plan - **Unit (scaffold.test.ts)**: `createResourcesDir` creates `codev/resources/arch.md` + `lessons-learned.md` with the starter markers (`_No architecture documented yet._`, `_No lessons captured yet._`); a pre-existing file with sentinel content is left byte-identical and reported as skipped. - **Unit (init.test.ts)**: `init --yes` produces `codev/resources/` with both starter files. - **Unit (adopt.test.ts)**: `adopt --yes` on a plain repo produces both starter files. -- **Unit (update)**: customized `resources/arch.md` survives `codev update` unchanged (or by-construction note, per Files to Change). +- **Unit (update)**: (a) update backfills both starter files into a project missing `resources/`; (b) customized `resources/arch.md` survives update byte-identical while the missing sibling is created; (c) `--dry-run` writes nothing. - **Build + full suite**: `pnpm --filter @cluesmith/codev build && pnpm --filter @cluesmith/codev test` from the worktree. - **Manual (for the dev-approval reviewer)**: 1. From the worktree: `pnpm build`, then run the built CLI: `node packages/codev/dist/src/cli.js init /tmp/pir1012-fresh --yes` (or the bin entry point; exact invocation confirmed at implement time). 2. Verify the init output lists `+ codev/resources/arch.md` and `+ codev/resources/lessons-learned.md`. 3. `cat /tmp/pir1012-fresh/codev/resources/arch.md` — starter content present; a `Read` of the path succeeds (the original failure mode). 4. In an adopt-shaped temp dir (no `codev/`), run adopt with `--yes` and verify the same two files appear. + 5. In a codev project missing `resources/` (simulating a pre-fix project), run `codev update` and verify both files are backfilled; re-run with a customized `arch.md` present and verify it is untouched. diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index d15bcdb8a..fb9eff589 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -9,3 +9,4 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - `codev update` already never touches resources — acceptance criterion satisfied by status quo; adding a guard test. - `codev adopt` aborts when `codev/` exists, so the "don't clobber" criterion is structurally unreachable; new function uses per-file skip-if-exists anyway. - Plan written to `codev/plans/1012-scaffold-codev-init-bootstraps.md`. Awaiting plan-approval gate. +- Gate discussion: architect asked about VSCode extension behavior on uninitialized workspaces (answer: extension never invokes init/adopt; degrades gracefully, user bootstraps manually) and whether adopt should backfill pre-fix projects. Adopt can't (aborts when `codev/` exists), so architect approved a scope amendment: `codev update` backfills missing-only resources files (create-if-missing, never modifies existing), deviating from the issue's literal "update does NOT touch resources" criterion. Plan revised accordingly. From b0447b971822510450c66b0218e1314f44f78e22 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 12:29:57 +1000 Subject: [PATCH 05/28] [PIR #1012] Plan revised post-rebase: ride Spec 987 rails, materialize cold tier --- .../1012-scaffold-codev-init-bootstraps.md | 119 +++++++++--------- codev/state/pir-1012_thread.md | 11 +- 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/codev/plans/1012-scaffold-codev-init-bootstraps.md b/codev/plans/1012-scaffold-codev-init-bootstraps.md index 0906cc74f..5e27930ea 100644 --- a/codev/plans/1012-scaffold-codev-init-bootstraps.md +++ b/codev/plans/1012-scaffold-codev-init-bootstraps.md @@ -1,93 +1,90 @@ -# PIR Plan: `codev init` bootstraps `codev/resources/` with arch.md + lessons-learned.md starters +# PIR Plan: `codev init` bootstraps `codev/resources/` cold-tier files (arch.md + lessons-learned.md) -## Understanding +> **Rebased on main 2026-06-13.** Spec 987 (two-tier governance docs) landed since this plan was first drafted and changes the picture materially. This revision re-scopes the work to ride the 987 rails. See "What Spec 987 already did" below. -Fresh `codev init` projects have no `codev/resources/` directory. The review prompts of PIR, SPIR, ASPIR, and MAINTAIN unconditionally read `codev/resources/arch.md` (e.g. `codev-skeleton/protocols/spir/prompts/review.md:152` says "Read the current `codev/resources/arch.md`"), so the first review phase in a fresh project errors on a missing file. +## Understanding -Root cause: `createUserDirs` in `packages/codev/src/lib/scaffold.ts:23-44` creates only `specs`, `plans`, and `reviews`. Nothing in the init flow (`packages/codev/src/commands/init.ts:79-140`) creates `codev/resources/`. +The issue: fresh `codev init` projects have no `codev/resources/arch.md` or `lessons-learned.md`, so review prompts that read them error out. -These two files are project-specific (each workspace's own architecture and lessons), so the fix is bootstrap-on-init with minimal placeholder content, not resolver fallback (that framework-side concern is #1011). +### What Spec 987 already did (post-rebase reality) -### Codebase findings worth noting +Spec 987 introduced a **hot/cold two-tier** governance-doc model and, as part of it, wired resource materialization into init/adopt/update — but **only for the HOT tier**: -1. **`copyResourceTemplates` (scaffold.ts:113-145) already exists but is dead code.** No command imports it; only `scaffold.test.ts` exercises it. It copies the skeleton's `templates/arch.md` and `templates/lessons-learned.md` (rich, instructional template stubs) plus `cheatsheet.md` and `lifecycle.md` (framework docs, the #1011 side of the audit). It was orphaned when init/adopt moved to the "minimal structure, framework files resolve from the package" model. -2. **`codev update` never touches `resources/`** today — `update.ts` has no reference to the resources dir. **Scope amendment (approved at the plan gate)**: update WILL backfill missing-only resources files. Rationale: adopt structurally cannot reach already-initialized projects (it aborts when `codev/` exists), so the population missing `resources/` — projects bootstrapped before this fix — is only ever touched by `update`. Create-if-missing honors the intent of the issue's "update does NOT touch resources" criterion (protecting customizations) while closing the backfill gap: the skip-if-exists semantics make it provably non-clobbering. -3. **`codev adopt` refuses to run when `codev/` exists** (`adopt.ts:75-77`), so the "don't clobber existing resources" criterion is structurally unreachable today. The new function still implements skip-if-exists semantics per file, so adopt is safe even if that precondition ever loosens. -4. `init.test.ts:74` carries a comment documenting the current behavior ("resources/ is NOT created in minimal structure") — it must be updated with the new expectation. +- `copyHotTierDefaults()` (`scaffold.ts:161`) copies skeleton `templates/arch-critical.md` + `templates/lessons-critical.md` into `codev/resources/`, with `skipExisting` for adopt/update. It is called at all three sites: + - `init.ts:117` (no skip — fresh project) + - `adopt.ts:159` (`skipExisting: true`) + - `update.ts:263` (`skipExisting: true`, with a `dryRun` branch + `result.newFiles` reporting) +- `update` already **backfills** missing hot files for existing adopters (`update.ts:258-268`). The "should update touch resources?" question we discussed at the gate is therefore already settled in the codebase: backfill-missing-only via `skipExisting` is the shipped house style. +- The COLD files `resources/arch.md` and `resources/lessons-learned.md` are **already registered as protected user data** (`templates.ts:83-84`), so update's clean step will never overwrite them — they're just never *created*. -## Proposed Change +### What's still broken (the residual #1012 gap) -Add a new `createResourcesDir(targetDir, options)` function to `packages/codev/src/lib/scaffold.ts`, alongside `createUserDirs` / `createProjectsDir`, that: +The **COLD** files are still not materialized by any command. The review prompts reference them directly: -- Creates `codev/resources/` (mkdir recursive). -- Writes `arch.md` and `lessons-learned.md` with the minimal starter content from the issue (inline string constants in scaffold.ts — no skeleton dependency, since the content is project-owned placeholder, not a framework template). -- **Always skips a file that already exists** (per-file, unconditional). Clobbering an existing arch.md is never correct in any flow, so this is not behind the `skipExisting` option flag. -- Returns `{ created: string[], skipped: string[] }` matching the existing result-shape convention. +- `spir/prompts/review.md:156` — "Read `arch-critical.md` (hot) and **skim `arch.md`** (cold)." +- `spir/prompts/review.md:163` — skim `lessons-learned.md` (cold). +- `pir/prompts/review.md:88,99-100` — routes changes into / `git add`s `arch.md` and `lessons-learned.md`. -Starter content (verbatim from the issue): +Beyond the prompts, the cold files are the **archive that the hot-tier maps point into**: each hot template carries a "Map of arch.md (consult when…)" section that directs readers into `arch.md`. Materializing the cold tier is the coherent completion of 987's model, not just an error-avoidance patch. -`arch.md`: -```markdown -# Architecture +So the fix shrinks from the original "invent `createResourcesDir` with inline placeholder content" to: **add a cold-tier sibling to `copyHotTierDefaults` and wire it in at the same three sites.** -This document evolves as the project grows. Update it during the review phase of any work that introduces or changes architectural patterns. +## Proposed Change -_No architecture documented yet._ -``` +Mirror the proven 987 hot-tier path for the cold tier. -`lessons-learned.md`: -```markdown -# Lessons Learned +1. **`scaffold.ts`** — add, directly below `HOT_TIER_FILES` / `copyHotTierDefaults`: + - `export const COLD_TIER_FILES = ['arch.md', 'lessons-learned.md'] as const;` + - `export function copyColdTierDefaults(targetDir, skeletonDir, options)` — byte-for-byte structural mirror of `copyHotTierDefaults`: ensure `codev/resources/` exists, copy each cold file from `skeletonDir/templates/`, honor `skipExisting`, return `{ copied, skipped }`. -Durable engineering wisdom captured across the project's work. Update it during the review phase of any work that surfaces a generally-applicable pattern, gotcha, or constraint. +2. **Wire into the three commands**, immediately after each existing `copyHotTierDefaults` call (so cold files are materialized alongside hot, with identical logging/`fileCount`/`result.newFiles` handling): + - `init.ts:~117` — `copyColdTierDefaults(targetDir, skeletonDir)` (no skip). + - `adopt.ts:~159` — `copyColdTierDefaults(targetDir, skeletonDir, { skipExisting: true })`. + - `update.ts:~263` — `copyColdTierDefaults(targetDir, templatesDir, { skipExisting: true })`, inside the same `dryRun` if/else, pushing to `result.newFiles` and logging `+ (new)`. Extend the dry-run message to mention `{arch,lessons}.md` too. -_No lessons captured yet._ -``` +### Content decision: copy the skeleton templates (NOT inline minimal placeholders) -Wire it into both bootstrap commands: +The original issue asked for *trivial inline* starter content and argued against seeding heavyweight generic content. **That preference predates Spec 987 and I propose overriding it**, for three reasons: -- `init.ts`: call `createResourcesDir` after `createProjectsDir` (around line 90), print each created file with the existing `+` prefix pattern (`+ codev/resources/arch.md`, `+ codev/resources/lessons-learned.md`), increment `fileCount`. -- `adopt.ts`: same call after `createProjectsDir` (around line 126), same output pattern. Skip-if-exists semantics make this safe by construction. -- `update.ts`: call `createResourcesDir` in the every-run refresh section, alongside the `.gitignore` backfill (`update.ts:256`), which already embodies the same create-missing-only philosophy. Created files are pushed to `result.newFiles` and logged with the existing `+ (new)` pattern; existing files are silently left alone (no noise on the common path). Respects `dryRun`: no writes, log missing files as `+ (would create)`. +1. **Consistency**: 987 established that resource starters come from `skeleton/templates/` via a `copy*TierDefaults` function. Inventing a second mechanism (inline string constants) for the cold tier would be an inconsistent oddity sitting right next to the hot-tier code. +2. **The skeleton already ships curated cold starters**: `templates/arch.md` (126 lines) and `templates/lessons-learned.md` (66 lines) are proper "how to use this doc" stubs with section scaffolding and "skip if N/A" hints — more useful to a new project than a one-line `_No architecture documented yet._`. +3. **The hot tier points into the cold tier**: the hot template's cold-doc map expects `arch.md` to have real top-level sections to map onto. A skeletal-but-structured cold file satisfies that; a one-liner does not. -### Why inline content instead of reviving `copyResourceTemplates` +**This is the main judgment call and I want your explicit sign-off at the gate.** If you prefer the issue's original minimal-inline content, say so and I'll seed trivial placeholders instead (still via `copyColdTierDefaults`, just sourcing inline strings rather than the skeleton files — or I trim the skeleton templates down). -The dead `copyResourceTemplates` copies the skeleton's rich template stubs (~60 lines each of "how to use this template" guidance) into every project. The issue deliberately chose trivial placeholders: enough for the first review-phase `Read` to succeed, with a one-line invitation to grow the file. Seeding every workspace with the same heavyweight generic content is exactly what the issue argues against. Inline constants also remove any dependency on skeleton resolution for this path. +### Why a sibling function, not a generalization -**Open question for the reviewer**: `copyResourceTemplates` remains dead code after this change, and two of the files it copies (`cheatsheet.md`, `lifecycle.md`) belong to the #1011 framework-file story. I propose leaving it untouched here (out of scope) and letting the architect decide whether its removal should be a separate item. If you'd rather I delete it (and its tests) in this PR, say so at this gate. +I considered refactoring `copyHotTierDefaults` into a generic `copyResourceDefaults(files, …)` called twice. Rejected: 987 landed days ago and its hot path is load-bearing (porch injection + managed-block depend on it). A parallel `copyColdTierDefaults` is lower-risk, reads symmetrically, and keeps the 987 code untouched. The minor duplication is acceptable and easy to fold later if desired. ## Files to Change -- `packages/codev/src/lib/scaffold.ts` — add `createResourcesDir` + the two starter-content constants (after `createProjectsDir`, ~line 223). -- `packages/codev/src/commands/init.ts:86-90` — import and call `createResourcesDir`, print created entries. -- `packages/codev/src/commands/adopt.ts:122-126` — same, in the adopt flow. -- `packages/codev/src/__tests__/scaffold.test.ts` — new `describe('createResourcesDir')`: creates dir + both files with starter content; preserves pre-existing files (write sentinel content, call, assert unchanged and reported in `skipped`). -- `packages/codev/src/__tests__/init.test.ts:68-74` — replace the "resources/ is NOT created" note with positive assertions: `codev/resources/arch.md` and `codev/resources/lessons-learned.md` exist and contain the placeholder markers. -- `packages/codev/src/__tests__/adopt.test.ts` — extend the happy-path adopt test to assert resources files are created. -- `packages/codev/src/commands/update.ts:~256` — import and call `createResourcesDir` in the refresh section (next to the gitignore backfill), with `dryRun` handling as described above. -- `packages/codev/src/__tests__/update.test.ts` (or the existing update test file) — two cases: (a) update on a project missing `resources/` creates both starter files and reports them in `newFiles`; (b) update on a project with a customized `resources/arch.md` leaves it byte-identical and creates only the missing `lessons-learned.md`. +- `packages/codev/src/lib/scaffold.ts` — add `COLD_TIER_FILES` + `copyColdTierDefaults` (~25 LOC, mirrors lines 147-190). +- `packages/codev/src/commands/init.ts` — import + call after `copyHotTierDefaults` (~line 117). +- `packages/codev/src/commands/adopt.ts` — import + call after `copyHotTierDefaults` (~line 159). +- `packages/codev/src/commands/update.ts` — import + call inside the hot-tier `dryRun` block (~line 263); extend dry-run log line. +- `packages/codev/src/__tests__/hot-tier-materialization.test.ts` (or a new parallel `cold-tier-materialization.test.ts`) — mirror the two unit tests (`copies both cold files` / `skip-existing preserves a curated copy`) and the update-integration test (`update creates the cold files`) for the cold tier. +- `packages/codev/src/__tests__/init.test.ts:74` — replace the stale comment ("resources/ is NOT created in minimal structure") with positive assertions that all four resource files exist after init. +- `packages/codev/src/__tests__/adopt.test.ts` — assert cold files appear after adopt. -Estimated net diff: ~50 LOC source + ~90 LOC tests. +Estimated net diff: ~30 LOC source + ~70 LOC tests. ## Risks & Alternatives Considered -- **Alternative: revive `copyResourceTemplates`** (copy skeleton templates at init). Rejected — see "Why inline content" above; also drags `cheatsheet.md`/`lifecycle.md` (framework files, #1011 territory) into project repos. -- **Alternative: resolver fallback to a skeleton default arch.md.** Rejected by the issue itself — these files are project-specific; a shared default defeats the purpose. -- **Alternative: extend `createUserDirs` instead of a sibling function.** Rejected — `createUserDirs` creates empty dirs with `.gitkeep`; resources needs file content and per-file skip semantics. A sibling keeps both functions simple and matches the issue's suggestion. -- **Risk: existing tests assert resources is absent.** `init.test.ts` only carries a comment (no negative assertion), so breakage risk is low; I'll run the full test suite to confirm. -- **Risk: adopt onto a repo with a hand-made `codev/resources/`.** Unreachable today (adopt aborts when `codev/` exists), and the per-file skip makes it safe regardless. -- **Deviation from the issue as filed**: the issue's acceptance criteria say "update does NOT touch `resources/*`". Amended at the plan-approval gate to create-if-missing backfill (architect-approved), because update is the only command that reaches projects initialized before this fix. Existing content is never modified — the criterion's protective intent is preserved. This deviation will be documented in the review file. +- **Content-source deviation from the issue** (skeleton templates vs inline minimal). Documented above; gated on your approval. Will be recorded in the review file as a deliberate, approved deviation. +- **Risk: the skeleton cold templates drift / are seen as "too heavy."** Mitigated by the fact they're already the canonical MAINTAIN-curated stubs; if they're too heavy that's a separate skeleton-content concern, not a scaffold-wiring concern. +- **Risk: stale negative assertions break.** `init.test.ts:74` is only a comment (no assertion), so no breakage; I update it to a positive assertion. Full suite run will confirm nothing else asserts absence. +- **`copyResourceTemplates` remains dead code** (987's own comment flags it as such). Out of scope here; flagged for the architect to retire separately if desired. +- **Alternative: do nothing in `update`** (init/adopt only). Rejected — update is the only command that reaches pre-987/pre-fix projects, and 987 already backfills the hot tier there; leaving the cold tier out would be asymmetric and re-open the gap for existing projects. +- **Alternative: generalize `copyHotTierDefaults`.** Rejected (see above) to protect the freshly-landed 987 code. ## Test Plan -- **Unit (scaffold.test.ts)**: `createResourcesDir` creates `codev/resources/arch.md` + `lessons-learned.md` with the starter markers (`_No architecture documented yet._`, `_No lessons captured yet._`); a pre-existing file with sentinel content is left byte-identical and reported as skipped. -- **Unit (init.test.ts)**: `init --yes` produces `codev/resources/` with both starter files. -- **Unit (adopt.test.ts)**: `adopt --yes` on a plain repo produces both starter files. -- **Unit (update)**: (a) update backfills both starter files into a project missing `resources/`; (b) customized `resources/arch.md` survives update byte-identical while the missing sibling is created; (c) `--dry-run` writes nothing. +- **Unit**: `copyColdTierDefaults` copies both cold files (creating `resources/`); `skipExisting` preserves a curated `arch.md` while creating the missing sibling. +- **Unit (init)**: `init --yes` yields all four `codev/resources/*.md` files. +- **Unit (adopt)**: `adopt --yes` on a plain repo yields the cold files. +- **Integration (update)**: update on a project missing the cold files creates both and reports them in `result.newFiles`; a customized `arch.md` survives byte-identical while `lessons-learned.md` is created; `--dry-run` writes nothing. (Mirror the existing hot-tier update integration test.) - **Build + full suite**: `pnpm --filter @cluesmith/codev build && pnpm --filter @cluesmith/codev test` from the worktree. -- **Manual (for the dev-approval reviewer)**: - 1. From the worktree: `pnpm build`, then run the built CLI: `node packages/codev/dist/src/cli.js init /tmp/pir1012-fresh --yes` (or the bin entry point; exact invocation confirmed at implement time). - 2. Verify the init output lists `+ codev/resources/arch.md` and `+ codev/resources/lessons-learned.md`. - 3. `cat /tmp/pir1012-fresh/codev/resources/arch.md` — starter content present; a `Read` of the path succeeds (the original failure mode). - 4. In an adopt-shaped temp dir (no `codev/`), run adopt with `--yes` and verify the same two files appear. - 5. In a codev project missing `resources/` (simulating a pre-fix project), run `codev update` and verify both files are backfilled; re-run with a customized `arch.md` present and verify it is untouched. +- **Manual (dev-approval reviewer)**: + 1. Build, then run the built CLI `init` into a temp dir; confirm output lists `+ codev/resources/arch.md` and `+ codev/resources/lessons-learned.md` alongside the hot files. + 2. `cat` both cold files — present and readable (the original failure mode is gone). + 3. In a codev project missing the cold files (pre-fix simulation), run `codev update`; confirm both are backfilled and a pre-existing customized `arch.md` is untouched. diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index fb9eff589..faf0a7c55 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -9,4 +9,13 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - `codev update` already never touches resources — acceptance criterion satisfied by status quo; adding a guard test. - `codev adopt` aborts when `codev/` exists, so the "don't clobber" criterion is structurally unreachable; new function uses per-file skip-if-exists anyway. - Plan written to `codev/plans/1012-scaffold-codev-init-bootstraps.md`. Awaiting plan-approval gate. -- Gate discussion: architect asked about VSCode extension behavior on uninitialized workspaces (answer: extension never invokes init/adopt; degrades gracefully, user bootstraps manually) and whether adopt should backfill pre-fix projects. Adopt can't (aborts when `codev/` exists), so architect approved a scope amendment: `codev update` backfills missing-only resources files (create-if-missing, never modifies existing), deviating from the issue's literal "update does NOT touch resources" criterion. Plan revised accordingly. +- Gate discussion: architect asked about VSCode extension behavior on uninitialized workspaces (answer: extension never invokes init/adopt; degrades gracefully, user bootstraps manually) and whether adopt should backfill pre-fix projects. Adopt can't (aborts when `codev/` exists), so architect approved a scope amendment: `codev update` backfills missing-only resources files (create-if-missing, never modifies existing). Plan revised accordingly. + +## Rebase on main (2026-06-13) + +- Rebased onto origin/main (was 63 behind). **Spec 987 had landed** — two-tier governance docs (HOT arch-critical.md/lessons-critical.md + COLD arch.md/lessons-learned.md). This re-scopes the issue significantly. +- 987 already wired `copyHotTierDefaults` into init/adopt/update (incl. update-backfill via skipExisting + dryRun handling) — but ONLY for the HOT tier. The COLD files (arch.md, lessons-learned.md), which #1012 is actually about, are STILL not materialized. +- COLD files are already in USER_DATA_PATTERNS (templates.ts:83-84) so update never overwrites them — they're just never created. +- The update-backfill "scope amendment" I discussed with the architect is now MOOT: 987 already ships update backfilling resources. Extending to cold is consistent house style, not a deviation. +- Revised plan: add `copyColdTierDefaults` (sibling of `copyHotTierDefaults`) + wire into the same 3 sites. Main open question for the gate: copy skeleton cold templates (consistent with 987) vs the issue's original "minimal inline content" preference. Plan recommends skeleton templates; awaiting architect sign-off. +- Dead `copyResourceTemplates` still dead; 987's own comment flags it. Out of scope. From 6aa28b2604285116bb94e8ca2267de81fc8d0159 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 12:35:04 +1000 Subject: [PATCH 06/28] [PIR #1012] Plan revised: single materialization mechanism, fix template source --- .../1012-scaffold-codev-init-bootstraps.md | 49 ++++++++++++------- codev/state/pir-1012_thread.md | 8 +++ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/codev/plans/1012-scaffold-codev-init-bootstraps.md b/codev/plans/1012-scaffold-codev-init-bootstraps.md index 5e27930ea..ddb263bb2 100644 --- a/codev/plans/1012-scaffold-codev-init-bootstraps.md +++ b/codev/plans/1012-scaffold-codev-init-bootstraps.md @@ -37,45 +37,56 @@ Mirror the proven 987 hot-tier path for the cold tier. - `export const COLD_TIER_FILES = ['arch.md', 'lessons-learned.md'] as const;` - `export function copyColdTierDefaults(targetDir, skeletonDir, options)` — byte-for-byte structural mirror of `copyHotTierDefaults`: ensure `codev/resources/` exists, copy each cold file from `skeletonDir/templates/`, honor `skipExisting`, return `{ copied, skipped }`. -2. **Wire into the three commands**, immediately after each existing `copyHotTierDefaults` call (so cold files are materialized alongside hot, with identical logging/`fileCount`/`result.newFiles` handling): +2. **Generalize the existing copy mechanism** rather than fork it. Extract the shared body of `copyHotTierDefaults` into a private `copyResourceDefaults(targetDir, skeletonDir, files, options)` (the exact logic already at `scaffold.ts:166-189`: ensure `resources/` exists, copy each file from `skeletonDir/templates/`, honor `skipExisting`, return `{ copied, skipped }`). Then: + - `copyHotTierDefaults = (…) => copyResourceDefaults(…, HOT_TIER_FILES, …)` — behavior byte-identical to today; the load-bearing 987 path is preserved. + - `copyColdTierDefaults = (…) => copyResourceDefaults(…, COLD_TIER_FILES, …)` where `COLD_TIER_FILES = ['arch.md', 'lessons-learned.md']`. + +3. **Wire `copyColdTierDefaults` into the three commands**, immediately after each existing `copyHotTierDefaults` call (identical logging/`fileCount`/`result.newFiles` handling): - `init.ts:~117` — `copyColdTierDefaults(targetDir, skeletonDir)` (no skip). - `adopt.ts:~159` — `copyColdTierDefaults(targetDir, skeletonDir, { skipExisting: true })`. - - `update.ts:~263` — `copyColdTierDefaults(targetDir, templatesDir, { skipExisting: true })`, inside the same `dryRun` if/else, pushing to `result.newFiles` and logging `+ (new)`. Extend the dry-run message to mention `{arch,lessons}.md` too. + - `update.ts:~263` — `copyColdTierDefaults(targetDir, templatesDir, { skipExisting: true })`, inside the same `dryRun` if/else, pushing to `result.newFiles`/logging `+ (new)`. Extend the dry-run message to mention `{arch,lessons}.md` too. + +### Content source: fix the skeleton templates, don't hand-roll content + +This is the crux the gate discussion settled. There is **no resolver or command that serves these files at read time** — review prompts read the literal path `codev/resources/arch.md`, and the file is registered as project-owned user data (`templates.ts:83-84`) with deliberately no runtime fallback (the #1011 boundary). So the file must physically exist; init has to materialize it, exactly as it already does for the hot tier. "Just call the command" still bottoms out in a copy. -### Content decision: copy the skeleton templates (NOT inline minimal placeholders) +The earlier idea of writing *inline string* content in `scaffold.ts` is rejected: it forks the materialization mechanism (inline strings vs `fs.copyFileSync` from skeleton) for no reason. Instead, **fix the source of truth** so the one existing mechanism does its job: -The original issue asked for *trivial inline* starter content and argued against seeding heavyweight generic content. **That preference predates Spec 987 and I propose overriding it**, for three reasons: +- The only thing wrong with copying the current `templates/arch.md` verbatim is its **"Note on propagation"** section, which asserts the file is *not* copied into projects and gives a manual-`cp` recipe. Once `codev init` copies it, that note is false and self-contradicting. +- **Verified blast radius is nil**: nothing in `packages/**` (non-test) reads `templates/arch.md` or `templates/lessons-learned.md` — they are pure copy sources. So editing them is safe. +- **Change**: remove the "Note on propagation" section from `templates/arch.md` (and the parallel "Generated by MAINTAIN" footer line in `templates/lessons-learned.md` that reads falsely in a brand-new project). Keep the lean section scaffolding ("How to use this template", the stub headings, "skip if N/A" hints) — that scaffolding is exactly what makes the file a useful starter and what the hot-tier "Map of arch.md" expects to point into. -1. **Consistency**: 987 established that resource starters come from `skeleton/templates/` via a `copy*TierDefaults` function. Inventing a second mechanism (inline string constants) for the cold tier would be an inconsistent oddity sitting right next to the hot-tier code. -2. **The skeleton already ships curated cold starters**: `templates/arch.md` (126 lines) and `templates/lessons-learned.md` (66 lines) are proper "how to use this doc" stubs with section scaffolding and "skip if N/A" hints — more useful to a new project than a one-line `_No architecture documented yet._`. -3. **The hot tier points into the cold tier**: the hot template's cold-doc map expects `arch.md` to have real top-level sections to map onto. A skeletal-but-structured cold file satisfies that; a one-liner does not. +Net: content lives in the skeleton (where every other template lives), one copy mechanism serves both tiers, no inline content, no second code path. -**This is the main judgment call and I want your explicit sign-off at the gate.** If you prefer the issue's original minimal-inline content, say so and I'll seed trivial placeholders instead (still via `copyColdTierDefaults`, just sourcing inline strings rather than the skeleton files — or I trim the skeleton templates down). +**Remaining judgment for the gate**: how lean to trim. I propose a light trim (drop only the propagation note + the false footer; keep the structural stubs). If you'd rather go all the way to the issue's one-line `_No architecture documented yet._` placeholder, that's a heavier edit to the skeleton templates — say which you prefer. -### Why a sibling function, not a generalization +### Skeleton mirroring -I considered refactoring `copyHotTierDefaults` into a generic `copyResourceDefaults(files, …)` called twice. Rejected: 987 landed days ago and its hot path is load-bearing (porch injection + managed-block depend on it). A parallel `copyColdTierDefaults` is lower-risk, reads symmetrically, and keeps the 987 code untouched. The minor duplication is acceptable and easy to fold later if desired. +This repo is self-hosted: `codev-skeleton/templates/` is the shipped template, and the live `codev/resources/arch.md` in *this* repo is the project's own curated copy. The edit is to **`codev-skeleton/templates/{arch,lessons-learned}.md`** only (the shipped starters). Our own `codev/resources/*` cold files already exist and are untouched. ## Files to Change -- `packages/codev/src/lib/scaffold.ts` — add `COLD_TIER_FILES` + `copyColdTierDefaults` (~25 LOC, mirrors lines 147-190). -- `packages/codev/src/commands/init.ts` — import + call after `copyHotTierDefaults` (~line 117). -- `packages/codev/src/commands/adopt.ts` — import + call after `copyHotTierDefaults` (~line 159). -- `packages/codev/src/commands/update.ts` — import + call inside the hot-tier `dryRun` block (~line 263); extend dry-run log line. -- `packages/codev/src/__tests__/hot-tier-materialization.test.ts` (or a new parallel `cold-tier-materialization.test.ts`) — mirror the two unit tests (`copies both cold files` / `skip-existing preserves a curated copy`) and the update-integration test (`update creates the cold files`) for the cold tier. +- `packages/codev/src/lib/scaffold.ts` — extract `copyResourceDefaults` helper; redefine `copyHotTierDefaults` in terms of it (no behavior change); add `COLD_TIER_FILES` + `copyColdTierDefaults`. +- `codev-skeleton/templates/arch.md` — remove the "Note on propagation" section (it becomes false once copied). +- `codev-skeleton/templates/lessons-learned.md` — remove the false "Generated by MAINTAIN" footer; keep section scaffolding. +- `packages/codev/src/commands/init.ts` — import + call `copyColdTierDefaults` after `copyHotTierDefaults` (~line 117). +- `packages/codev/src/commands/adopt.ts` — same (~line 159, `skipExisting`). +- `packages/codev/src/commands/update.ts` — same, inside the hot-tier `dryRun` block (~line 263); extend dry-run log line. +- `packages/codev/src/__tests__/hot-tier-materialization.test.ts` (or a new parallel `cold-tier-materialization.test.ts`) — mirror the two unit tests (`copies both cold files` / `skip-existing preserves a curated copy`) and the update-integration test (`update creates the cold files`) for the cold tier. Keep a regression assertion that `copyHotTierDefaults` behavior is unchanged after the refactor. - `packages/codev/src/__tests__/init.test.ts:74` — replace the stale comment ("resources/ is NOT created in minimal structure") with positive assertions that all four resource files exist after init. - `packages/codev/src/__tests__/adopt.test.ts` — assert cold files appear after adopt. -Estimated net diff: ~30 LOC source + ~70 LOC tests. +Estimated net diff: ~30 LOC source + skeleton template trims + ~70 LOC tests. ## Risks & Alternatives Considered -- **Content-source deviation from the issue** (skeleton templates vs inline minimal). Documented above; gated on your approval. Will be recorded in the review file as a deliberate, approved deviation. -- **Risk: the skeleton cold templates drift / are seen as "too heavy."** Mitigated by the fact they're already the canonical MAINTAIN-curated stubs; if they're too heavy that's a separate skeleton-content concern, not a scaffold-wiring concern. +- **Refactor risk: generalizing `copyHotTierDefaults` could regress the load-bearing 987 hot path** (porch injection + managed-block depend on it). Mitigated: `copyHotTierDefaults` keeps its exact signature and is reimplemented as a one-line delegate to `copyResourceDefaults`; a regression test asserts unchanged behavior; full suite run confirms. +- **Editing the skeleton cold templates** (`arch.md`/`lessons-learned.md`). Verified safe: grep shows no non-test code reads those template files; they are pure copy sources. The trim only removes self-referential text that becomes false once the files are copied. +- **Content-source deviation from the issue** (skeleton starter vs one-line inline placeholder). The issue's "minimal inline" wording predates Spec 987's copy-from-skeleton pattern; this plan keeps the *mechanism* the issue implied (a real starter file exists) while sourcing it the 987 way. Recorded in the review as a deliberate, gate-approved deviation; the lean-vs-ultra-minimal trim is the one open sub-decision. - **Risk: stale negative assertions break.** `init.test.ts:74` is only a comment (no assertion), so no breakage; I update it to a positive assertion. Full suite run will confirm nothing else asserts absence. - **`copyResourceTemplates` remains dead code** (987's own comment flags it as such). Out of scope here; flagged for the architect to retire separately if desired. - **Alternative: do nothing in `update`** (init/adopt only). Rejected — update is the only command that reaches pre-987/pre-fix projects, and 987 already backfills the hot tier there; leaving the cold tier out would be asymmetric and re-open the gap for existing projects. -- **Alternative: generalize `copyHotTierDefaults`.** Rejected (see above) to protect the freshly-landed 987 code. +- **Alternative: inline string content in `scaffold.ts`.** Rejected — forks the materialization mechanism for no benefit (see "Content source" above). ## Test Plan diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index faf0a7c55..9ec022b1e 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -19,3 +19,11 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - The update-backfill "scope amendment" I discussed with the architect is now MOOT: 987 already ships update backfilling resources. Extending to cold is consistent house style, not a deviation. - Revised plan: add `copyColdTierDefaults` (sibling of `copyHotTierDefaults`) + wire into the same 3 sites. Main open question for the gate: copy skeleton cold templates (consistent with 987) vs the issue's original "minimal inline content" preference. Plan recommends skeleton templates; awaiting architect sign-off. - Dead `copyResourceTemplates` still dead; 987's own comment flags it. Out of scope. + +## Gate iter: "why copy at all / use the proper code path" + +- Architect challenged the inline-content idea: don't fork the mechanism, use the existing materialization code. +- Verified: (a) NO production code reads templates/arch.md or templates/lessons-learned.md (pure copy sources — safe to edit); (b) no resolver/lazy path serves cold files — review prompts read literal `codev/resources/arch.md`, which is project-owned user-data (templates.ts:83-84) with no fallback by design. So the file MUST physically exist; materialization is unavoidable. +- Also found: the skeleton `templates/arch.md` has a "Note on propagation" section asserting it is NOT copied into projects — copying it verbatim would be self-contradicting. That's the real reason inline content was tempting. +- Final design (plan revised): generalize `copyHotTierDefaults` body into a private `copyResourceDefaults(files,...)`, redefine hot in terms of it (no behavior change), add `copyColdTierDefaults` for ['arch.md','lessons-learned.md']; wire at the same 3 sites. Fix the SOURCE: trim the propagation note from skeleton arch.md + false footer from lessons-learned.md. One mechanism, one source of truth, no inline strings. +- Open sub-decision for gate: how lean to trim the templates (light trim keeping structural stubs [recommended] vs ultra-minimal one-liner). From b8912b98d5ec3de416f4c03ed1df19db0b527d8d Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 13:46:15 +1000 Subject: [PATCH 07/28] [PIR #1012] Thread: second rebase, plan assumptions re-confirmed --- codev/state/pir-1012_thread.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index 9ec022b1e..fff389318 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -27,3 +27,7 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - Also found: the skeleton `templates/arch.md` has a "Note on propagation" section asserting it is NOT copied into projects — copying it verbatim would be self-contradicting. That's the real reason inline content was tempting. - Final design (plan revised): generalize `copyHotTierDefaults` body into a private `copyResourceDefaults(files,...)`, redefine hot in terms of it (no behavior change), add `copyColdTierDefaults` for ['arch.md','lessons-learned.md']; wire at the same 3 sites. Fix the SOURCE: trim the propagation note from skeleton arch.md + false footer from lessons-learned.md. One mechanism, one source of truth, no inline strings. - Open sub-decision for gate: how lean to trim the templates (light trim keeping structural stubs [recommended] vs ultra-minimal one-liner). + +## Second rebase on main (2026-06-13) + +- Rebased again (was 38 behind). Verified NONE of the 38 new commits touched any plan-target file (scaffold.ts, init/adopt/update.ts, templates.ts, skeleton templates, review prompts). All plan assumptions re-confirmed: hot-tier mechanism intact, cold files still user-data with no fallback, propagation note still in skeleton arch.md:116, no production reader of cold template sources, review prompts still reference cold files. Plan unchanged. Force-pushed. Still at plan-approval gate. From 576e69fb11e2d55df408dc7f0cbf1b00813db79a Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:11:12 +1000 Subject: [PATCH 08/28] [PIR #1012] Plan finalized: one-line placeholders, skeleton templates untouched --- .../1012-scaffold-codev-init-bootstraps.md | 70 ++++++++++--------- codev/state/pir-1012_thread.md | 7 ++ 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/codev/plans/1012-scaffold-codev-init-bootstraps.md b/codev/plans/1012-scaffold-codev-init-bootstraps.md index ddb263bb2..c1f639bd1 100644 --- a/codev/plans/1012-scaffold-codev-init-bootstraps.md +++ b/codev/plans/1012-scaffold-codev-init-bootstraps.md @@ -27,70 +27,74 @@ The **COLD** files are still not materialized by any command. The review prompts Beyond the prompts, the cold files are the **archive that the hot-tier maps point into**: each hot template carries a "Map of arch.md (consult when…)" section that directs readers into `arch.md`. Materializing the cold tier is the coherent completion of 987's model, not just an error-avoidance patch. -So the fix shrinks from the original "invent `createResourcesDir` with inline placeholder content" to: **add a cold-tier sibling to `copyHotTierDefaults` and wire it in at the same three sites.** +So the fix is: **materialize a minimal placeholder for each cold file, wired into the same three commands that 987 already uses to materialize the hot tier.** ## Proposed Change -Mirror the proven 987 hot-tier path for the cold tier. +### Content decision (gate-approved): one-line placeholders, skeleton templates untouched -1. **`scaffold.ts`** — add, directly below `HOT_TIER_FILES` / `copyHotTierDefaults`: - - `export const COLD_TIER_FILES = ['arch.md', 'lessons-learned.md'] as const;` - - `export function copyColdTierDefaults(targetDir, skeletonDir, options)` — byte-for-byte structural mirror of `copyHotTierDefaults`: ensure `codev/resources/` exists, copy each cold file from `skeletonDir/templates/`, honor `skipExisting`, return `{ copied, skipped }`. +The materialized cold files are **minimal placeholders**, using the issue's suggested text verbatim: -2. **Generalize the existing copy mechanism** rather than fork it. Extract the shared body of `copyHotTierDefaults` into a private `copyResourceDefaults(targetDir, skeletonDir, files, options)` (the exact logic already at `scaffold.ts:166-189`: ensure `resources/` exists, copy each file from `skeletonDir/templates/`, honor `skipExisting`, return `{ copied, skipped }`). Then: - - `copyHotTierDefaults = (…) => copyResourceDefaults(…, HOT_TIER_FILES, …)` — behavior byte-identical to today; the load-bearing 987 path is preserved. - - `copyColdTierDefaults = (…) => copyResourceDefaults(…, COLD_TIER_FILES, …)` where `COLD_TIER_FILES = ['arch.md', 'lessons-learned.md']`. +`codev/resources/arch.md`: +```markdown +# Architecture -3. **Wire `copyColdTierDefaults` into the three commands**, immediately after each existing `copyHotTierDefaults` call (identical logging/`fileCount`/`result.newFiles` handling): - - `init.ts:~117` — `copyColdTierDefaults(targetDir, skeletonDir)` (no skip). - - `adopt.ts:~159` — `copyColdTierDefaults(targetDir, skeletonDir, { skipExisting: true })`. - - `update.ts:~263` — `copyColdTierDefaults(targetDir, templatesDir, { skipExisting: true })`, inside the same `dryRun` if/else, pushing to `result.newFiles`/logging `+ (new)`. Extend the dry-run message to mention `{arch,lessons}.md` too. +This document evolves as the project grows. Update it during the review phase of any work that introduces or changes architectural patterns. -### Content source: fix the skeleton templates, don't hand-roll content +_No architecture documented yet._ +``` -This is the crux the gate discussion settled. There is **no resolver or command that serves these files at read time** — review prompts read the literal path `codev/resources/arch.md`, and the file is registered as project-owned user data (`templates.ts:83-84`) with deliberately no runtime fallback (the #1011 boundary). So the file must physically exist; init has to materialize it, exactly as it already does for the hot tier. "Just call the command" still bottoms out in a copy. +`codev/resources/lessons-learned.md`: +```markdown +# Lessons Learned -The earlier idea of writing *inline string* content in `scaffold.ts` is rejected: it forks the materialization mechanism (inline strings vs `fs.copyFileSync` from skeleton) for no reason. Instead, **fix the source of truth** so the one existing mechanism does its job: +Durable engineering wisdom captured across the project's work. Update it during the review phase of any work that surfaces a generally-applicable pattern, gotcha, or constraint. -- The only thing wrong with copying the current `templates/arch.md` verbatim is its **"Note on propagation"** section, which asserts the file is *not* copied into projects and gives a manual-`cp` recipe. Once `codev init` copies it, that note is false and self-contradicting. -- **Verified blast radius is nil**: nothing in `packages/**` (non-test) reads `templates/arch.md` or `templates/lessons-learned.md` — they are pure copy sources. So editing them is safe. -- **Change**: remove the "Note on propagation" section from `templates/arch.md` (and the parallel "Generated by MAINTAIN" footer line in `templates/lessons-learned.md` that reads falsely in a brand-new project). Keep the lean section scaffolding ("How to use this template", the stub headings, "skip if N/A" hints) — that scaffolding is exactly what makes the file a useful starter and what the hot-tier "Map of arch.md" expects to point into. +_No lessons captured yet._ +``` -Net: content lives in the skeleton (where every other template lives), one copy mechanism serves both tiers, no inline content, no second code path. +**Consequence — the skeleton templates are NOT edited.** A placeholder has no skeleton file worth copying, so we do not copy `templates/arch.md`/`lessons-learned.md` and therefore do not need to trim their self-referential "Note on propagation" / MAINTAIN footer. The rich skeleton templates stay exactly as-is, preserving the manual-`cp` escape hatch they document. This is simpler and lower-risk than the copy-and-trim approach considered earlier. -**Remaining judgment for the gate**: how lean to trim. I propose a light trim (drop only the propagation note + the false footer; keep the structural stubs). If you'd rather go all the way to the issue's one-line `_No architecture documented yet._` placeholder, that's a heavier edit to the skeleton templates — say which you prefer. +**Why a small inline write is correct here, not a "forked mechanism":** the hot tier *copies* because its skeleton files (`arch-critical.md`/`lessons-critical.md`) are genuine, curated starters meant to land verbatim. The cold tier *writes a placeholder* because the desired content is an intentionally-trivial stub with no skeleton equivalent. Each tier uses the minimal mechanism its content nature calls for; there is no second copy of the *same* logic. -### Skeleton mirroring +### Implementation -This repo is self-hosted: `codev-skeleton/templates/` is the shipped template, and the live `codev/resources/arch.md` in *this* repo is the project's own curated copy. The edit is to **`codev-skeleton/templates/{arch,lessons-learned}.md`** only (the shipped starters). Our own `codev/resources/*` cold files already exist and are untouched. +1. **`scaffold.ts`** — add a `createColdTierDefaults(targetDir, options)` function next to `copyHotTierDefaults` (Spec 987's materialization neighborhood): + - A `COLD_TIER_STARTERS` map of `{ 'arch.md': , 'lessons-learned.md': }` (two short const strings). + - Ensure `codev/resources/` exists; for each entry, **skip if the file already exists**, else `writeFileSync` the placeholder. Return `{ created, skipped }` matching the existing result-shape convention. + - No `skeletonDir` parameter (nothing is copied). + +2. **Wire `createColdTierDefaults` into the three commands**, immediately after each existing `copyHotTierDefaults` call, with identical logging / `fileCount` / `result.newFiles` handling: + - `init.ts:~117` — `createColdTierDefaults(targetDir)` (no skip; fresh project). + - `adopt.ts:~159` — `createColdTierDefaults(targetDir, { skipExisting: true })`. + - `update.ts:~263` — `createColdTierDefaults(targetDir, { skipExisting: true })`, inside the same `dryRun` if/else, pushing created files to `result.newFiles` / logging `+ (new)`. Extend the dry-run message to mention `{arch,lessons}.md`. + + `update` backfilling missing cold files is consistent with the shipped 987 behavior (it already backfills missing hot files there via `skipExisting`); the cold files are already protected user-data (`templates.ts:83-84`), so a customized cold file is never overwritten. ## Files to Change -- `packages/codev/src/lib/scaffold.ts` — extract `copyResourceDefaults` helper; redefine `copyHotTierDefaults` in terms of it (no behavior change); add `COLD_TIER_FILES` + `copyColdTierDefaults`. -- `codev-skeleton/templates/arch.md` — remove the "Note on propagation" section (it becomes false once copied). -- `codev-skeleton/templates/lessons-learned.md` — remove the false "Generated by MAINTAIN" footer; keep section scaffolding. -- `packages/codev/src/commands/init.ts` — import + call `copyColdTierDefaults` after `copyHotTierDefaults` (~line 117). +- `packages/codev/src/lib/scaffold.ts` — add `COLD_TIER_STARTERS` + `createColdTierDefaults` (~20 LOC). **Skeleton templates untouched.** +- `packages/codev/src/commands/init.ts` — import + call `createColdTierDefaults` after `copyHotTierDefaults` (~line 117). - `packages/codev/src/commands/adopt.ts` — same (~line 159, `skipExisting`). - `packages/codev/src/commands/update.ts` — same, inside the hot-tier `dryRun` block (~line 263); extend dry-run log line. -- `packages/codev/src/__tests__/hot-tier-materialization.test.ts` (or a new parallel `cold-tier-materialization.test.ts`) — mirror the two unit tests (`copies both cold files` / `skip-existing preserves a curated copy`) and the update-integration test (`update creates the cold files`) for the cold tier. Keep a regression assertion that `copyHotTierDefaults` behavior is unchanged after the refactor. +- `packages/codev/src/__tests__/hot-tier-materialization.test.ts` (or a new parallel `cold-tier-materialization.test.ts`) — unit tests: `createColdTierDefaults` creates both placeholder files (and the dir); `skipExisting` preserves a curated cold file; an update-integration test that `update` backfills the cold files into a project missing them. - `packages/codev/src/__tests__/init.test.ts:74` — replace the stale comment ("resources/ is NOT created in minimal structure") with positive assertions that all four resource files exist after init. - `packages/codev/src/__tests__/adopt.test.ts` — assert cold files appear after adopt. -Estimated net diff: ~30 LOC source + skeleton template trims + ~70 LOC tests. +Estimated net diff: ~20 LOC source + ~70 LOC tests. No skeleton or framework-template changes. ## Risks & Alternatives Considered -- **Refactor risk: generalizing `copyHotTierDefaults` could regress the load-bearing 987 hot path** (porch injection + managed-block depend on it). Mitigated: `copyHotTierDefaults` keeps its exact signature and is reimplemented as a one-line delegate to `copyResourceDefaults`; a regression test asserts unchanged behavior; full suite run confirms. -- **Editing the skeleton cold templates** (`arch.md`/`lessons-learned.md`). Verified safe: grep shows no non-test code reads those template files; they are pure copy sources. The trim only removes self-referential text that becomes false once the files are copied. -- **Content-source deviation from the issue** (skeleton starter vs one-line inline placeholder). The issue's "minimal inline" wording predates Spec 987's copy-from-skeleton pattern; this plan keeps the *mechanism* the issue implied (a real starter file exists) while sourcing it the 987 way. Recorded in the review as a deliberate, gate-approved deviation; the lean-vs-ultra-minimal trim is the one open sub-decision. +- **Content-source deviation from the issue**: none — this uses the issue's suggested placeholder text verbatim. - **Risk: stale negative assertions break.** `init.test.ts:74` is only a comment (no assertion), so no breakage; I update it to a positive assertion. Full suite run will confirm nothing else asserts absence. - **`copyResourceTemplates` remains dead code** (987's own comment flags it as such). Out of scope here; flagged for the architect to retire separately if desired. +- **Alternative: copy + trim the rich skeleton templates.** Rejected in favor of placeholders — copying drags the rich framework template (and its now-false propagation note) into every project, and the issue explicitly preferred minimal placeholder content. - **Alternative: do nothing in `update`** (init/adopt only). Rejected — update is the only command that reaches pre-987/pre-fix projects, and 987 already backfills the hot tier there; leaving the cold tier out would be asymmetric and re-open the gap for existing projects. -- **Alternative: inline string content in `scaffold.ts`.** Rejected — forks the materialization mechanism for no benefit (see "Content source" above). +- **Alternative: generalize `copyHotTierDefaults` into a shared copy helper.** Not applicable now — the cold tier writes placeholders rather than copying, so there is no shared copy body to extract. `copyHotTierDefaults` is left completely untouched (zero risk to the load-bearing 987 hot path). ## Test Plan -- **Unit**: `copyColdTierDefaults` copies both cold files (creating `resources/`); `skipExisting` preserves a curated `arch.md` while creating the missing sibling. +- **Unit**: `createColdTierDefaults` writes both placeholder files (creating `resources/`); `skipExisting` preserves a curated `arch.md` while creating the missing sibling. - **Unit (init)**: `init --yes` yields all four `codev/resources/*.md` files. - **Unit (adopt)**: `adopt --yes` on a plain repo yields the cold files. - **Integration (update)**: update on a project missing the cold files creates both and reports them in `result.newFiles`; a customized `arch.md` survives byte-identical while `lessons-learned.md` is created; `--dry-run` writes nothing. (Mirror the existing hot-tier update integration test.) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index fff389318..19ef4e246 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -31,3 +31,10 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m ## Second rebase on main (2026-06-13) - Rebased again (was 38 behind). Verified NONE of the 38 new commits touched any plan-target file (scaffold.ts, init/adopt/update.ts, templates.ts, skeleton templates, review prompts). All plan assumptions re-confirmed: hot-tier mechanism intact, cold files still user-data with no fallback, propagation note still in skeleton arch.md:116, no production reader of cold template sources, review prompts still reference cold files. Plan unchanged. Force-pushed. Still at plan-approval gate. + +## Content decision settled: one-line placeholders + +- Architect chose the one-line placeholder option (issue's suggested text verbatim) over copying/trimming the rich skeleton templates. +- This SIMPLIFIES the plan: skeleton templates are NOT edited at all (rich templates + their manual-cp note stay as-is). No `copyHotTierDefaults` refactor needed. +- Final implementation: `createColdTierDefaults(targetDir, opts)` in scaffold.ts writes two small placeholder consts (arch.md, lessons-learned.md) with skip-if-exists; wired at the same 3 sites next to copyHotTierDefaults; update backfills (consistent with 987). copyHotTierDefaults left completely untouched. +- Plan finalized. No open decisions remain. Awaiting plan-approval. From c21f03c13360cf996d5e8bd5843d582e4485166e Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:18:30 +1000 Subject: [PATCH 09/28] chore(porch): 1012 plan-approval gate-approved --- .../projects/1012-scaffold-codev-init-bootstraps/status.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index b8ef55fe1..147f87e41 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -6,8 +6,9 @@ plan_phases: [] current_plan_phase: null gates: plan-approval: - status: pending + status: approved requested_at: '2026-06-12T19:28:14.611Z' + approved_at: '2026-06-13T06:18:30.528Z' dev-approval: status: pending pr: @@ -16,4 +17,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-12T19:28:14.612Z' +updated_at: '2026-06-13T06:18:30.529Z' From 14913a5c06c91a050ed555bf9898c0aa9a78c2fd Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:18:43 +1000 Subject: [PATCH 10/28] chore(porch): 1012 implement phase-transition --- .../projects/1012-scaffold-codev-init-bootstraps/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index 147f87e41..eae11de3b 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -1,7 +1,7 @@ id: '1012' title: scaffold-codev-init-bootstraps 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-12T19:24:46.703Z' -updated_at: '2026-06-13T06:18:30.529Z' +updated_at: '2026-06-13T06:18:43.738Z' From 93ad834853e9ab2c767727e031476714f1fa9b0b Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:23:12 +1000 Subject: [PATCH 11/28] [PIR #1012] Bootstrap cold-tier resources (arch.md, lessons-learned.md) on init/adopt/update --- packages/codev/src/commands/adopt.ts | 8 ++++ packages/codev/src/commands/init.ts | 8 ++++ packages/codev/src/commands/update.ts | 13 +++++ packages/codev/src/lib/scaffold.ts | 69 +++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/packages/codev/src/commands/adopt.ts b/packages/codev/src/commands/adopt.ts index bd8f0908c..61cb283b6 100644 --- a/packages/codev/src/commands/adopt.ts +++ b/packages/codev/src/commands/adopt.ts @@ -17,6 +17,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, + createColdTierDefaults, } from '../lib/scaffold.js'; import { updateGitignore } from '../lib/gitignore.js'; @@ -161,6 +162,13 @@ export async function adopt(options: AdoptOptions = {}): Promise { fileCount++; } + // Create the cold-tier governance files (arch.md, lessons-learned.md) with placeholder + // content (issue #1012), skip-existing so a curated copy is preserved. + for (const file of createColdTierDefaults(targetDir, { skipExisting: true }).created) { + console.log(chalk.green(' +'), `codev/resources/${file}`); + fileCount++; + } + // Create .codev/config.json if it doesn't exist const codevConfigDir = path.join(targetDir, '.codev'); const codevConfigPath = path.join(codevConfigDir, 'config.json'); diff --git a/packages/codev/src/commands/init.ts b/packages/codev/src/commands/init.ts index c11900888..acfe02203 100644 --- a/packages/codev/src/commands/init.ts +++ b/packages/codev/src/commands/init.ts @@ -16,6 +16,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, + createColdTierDefaults, } from '../lib/scaffold.js'; import { syncHotContextBlock } from '../lib/managed-block.js'; import { createGitignore } from '../lib/gitignore.js'; @@ -119,6 +120,13 @@ export async function init(projectName?: string, options: InitOptions = {}): Pro fileCount++; } + // Create the cold-tier governance files (arch.md, lessons-learned.md) with placeholder + // content (issue #1012) so the first review-phase read succeeds against a real file. + for (const file of createColdTierDefaults(targetDir).created) { + console.log(chalk.green(' +'), `codev/resources/${file}`); + fileCount++; + } + // Inject the always-on hot-tier managed block into CLAUDE.md / AGENTS.md (Spec 987). for (const file of syncHotContextBlock(targetDir)) { console.log(chalk.green(' ~'), `${file} (hot-tier context)`); diff --git a/packages/codev/src/commands/update.ts b/packages/codev/src/commands/update.ts index e62025766..973395e73 100644 --- a/packages/codev/src/commands/update.ts +++ b/packages/codev/src/commands/update.ts @@ -26,6 +26,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, + createColdTierDefaults, } from '../lib/scaffold.js'; import { syncHotContextBlock } from '../lib/managed-block.js'; import { @@ -267,6 +268,18 @@ export async function update(options: UpdateOptions = {}): Promise } } + // Backfill the cold-tier governance files for existing adopters (issue #1012), + // skip-existing so a curated file is never overwritten. + if (dryRun) { + log(chalk.dim(' + (cold-tier) would create missing codev/resources/{arch,lessons-learned}.md')); + } else { + for (const file of createColdTierDefaults(targetDir, { skipExisting: true }).created) { + const rel = `codev/resources/${file}`; + result.newFiles.push(rel); + log(chalk.green(' + (new)'), rel); + } + } + // Refresh the always-on hot-tier managed block in CLAUDE.md / AGENTS.md (Spec 987). // Non-clobbering: only the marked block is replaced; user content is preserved. // Logged as a side effect (not added to result.updated, which tracks template copies). diff --git a/packages/codev/src/lib/scaffold.ts b/packages/codev/src/lib/scaffold.ts index 64245f2c1..a33b0f69c 100644 --- a/packages/codev/src/lib/scaffold.ts +++ b/packages/codev/src/lib/scaffold.ts @@ -189,6 +189,75 @@ export function copyHotTierDefaults( return { copied, skipped }; } +interface CreateColdTierDefaultsOptions { + skipExisting?: boolean; +} + +interface CreateColdTierDefaultsResult { + created: string[]; + skipped: string[]; +} + +/** + * The cold-tier governance files and their minimal placeholder content (issue #1012). + * + * These are the on-demand reference archives the hot tier's "Map of …" sections point + * into. Unlike the hot tier, they are NOT copied from the skeleton: the skeleton's + * `templates/{arch,lessons-learned}.md` are rich framework reference templates (they even + * carry a "this file is not copied into projects" note), so copying them verbatim would be + * self-contradicting. Instead each project gets a trivial placeholder it grows over time — + * enough for the review-phase read to succeed against a real, locally-owned file. + */ +export const COLD_TIER_STARTERS: Record = { + 'arch.md': + '# Architecture\n\n' + + 'This document evolves as the project grows. Update it during the review phase ' + + 'of any work that introduces or changes architectural patterns.\n\n' + + '_No architecture documented yet._\n', + 'lessons-learned.md': + '# Lessons Learned\n\n' + + "Durable engineering wisdom captured across the project's work. Update it during " + + 'the review phase of any work that surfaces a generally-applicable pattern, gotcha, ' + + 'or constraint.\n\n' + + '_No lessons captured yet._\n', +}; + +/** + * Create the cold-tier governance files (arch.md, lessons-learned.md) in codev/resources/ + * with minimal placeholder content (issue #1012). + * + * Companion to `copyHotTierDefaults`: Spec 987 materializes the hot tier on + * init/adopt/update but left the cold tier — which the review prompts read and the hot-tier + * maps point into — uncreated. `skipExisting` so a curated file is never overwritten (the + * cold files are already registered as protected user data in templates.ts). + */ +export function createColdTierDefaults( + targetDir: string, + options: CreateColdTierDefaultsOptions = {} +): CreateColdTierDefaultsResult { + const { skipExisting = false } = options; + const resourcesDir = path.join(targetDir, 'codev', 'resources'); + const created: string[] = []; + const skipped: string[] = []; + + if (!fs.existsSync(resourcesDir)) { + fs.mkdirSync(resourcesDir, { recursive: true }); + } + + for (const [file, content] of Object.entries(COLD_TIER_STARTERS)) { + const destPath = path.join(resourcesDir, file); + + if (skipExisting && fs.existsSync(destPath)) { + skipped.push(file); + continue; + } + fs.writeFileSync(destPath, content); + created.push(file); + } + + return { created, skipped }; +} + interface CopyRootFilesOptions { handleConflicts?: boolean; } From 322b6120463d76ead49ff41aea4308e5b74320f0 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:23:23 +1000 Subject: [PATCH 12/28] [PIR #1012] Tests: cold-tier materialization + init/adopt assertions --- packages/codev/src/__tests__/adopt.test.ts | 4 + .../cold-tier-materialization.test.ts | 118 ++++++++++++++++++ packages/codev/src/__tests__/init.test.ts | 7 +- 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 packages/codev/src/__tests__/cold-tier-materialization.test.ts diff --git a/packages/codev/src/__tests__/adopt.test.ts b/packages/codev/src/__tests__/adopt.test.ts index 8bf894b1f..d5423aeca 100644 --- a/packages/codev/src/__tests__/adopt.test.ts +++ b/packages/codev/src/__tests__/adopt.test.ts @@ -83,6 +83,10 @@ describe('adopt command', () => { // Verify user data directories expect(fs.existsSync(path.join(projectDir, 'codev', 'specs'))).toBe(true); expect(fs.existsSync(path.join(projectDir, 'codev', 'plans'))).toBe(true); + + // Issue #1012: cold-tier governance files are bootstrapped. + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'arch.md'))).toBe(true); + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'lessons-learned.md'))).toBe(true); }); it('should throw error if codev directory already exists', async () => { diff --git a/packages/codev/src/__tests__/cold-tier-materialization.test.ts b/packages/codev/src/__tests__/cold-tier-materialization.test.ts new file mode 100644 index 000000000..51d2a1a32 --- /dev/null +++ b/packages/codev/src/__tests__/cold-tier-materialization.test.ts @@ -0,0 +1,118 @@ +/** + * Issue #1012 — cold-tier governance files (arch.md, lessons-learned.md) materialized + * into projects on init/adopt/update with minimal placeholder content. Companion to the + * Spec 987 hot-tier materialization; mirrors its test shape. + */ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { createColdTierDefaults, COLD_TIER_STARTERS } from '../lib/scaffold.js'; +import { isUserDataPath } from '../lib/templates.js'; + +const COLD_TIER_FILES = Object.keys(COLD_TIER_STARTERS); + +describe('issue #1012 — createColdTierDefaults', () => { + let tmp: string; + + beforeEach(() => { + tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cold-mat-')); + }); + afterEach(() => fs.rmSync(tmp, { recursive: true, force: true })); + + it('creates both cold files into codev/resources/, creating the dir', () => { + const target = path.join(tmp, 'proj'); + const result = createColdTierDefaults(target); + expect(result.created.sort()).toEqual([...COLD_TIER_FILES].sort()); + for (const f of COLD_TIER_FILES) { + expect(fs.existsSync(path.join(target, 'codev', 'resources', f))).toBe(true); + } + }); + + it('writes the placeholder marker so the file is clearly a stub to replace', () => { + const target = path.join(tmp, 'proj'); + createColdTierDefaults(target); + const res = path.join(target, 'codev', 'resources'); + expect(fs.readFileSync(path.join(res, 'arch.md'), 'utf-8')).toContain('_No architecture documented yet._'); + expect(fs.readFileSync(path.join(res, 'lessons-learned.md'), 'utf-8')).toContain('_No lessons captured yet._'); + }); + + it('skip-existing preserves a curated copy', () => { + const target = path.join(tmp, 'proj'); + const res = path.join(target, 'codev', 'resources'); + fs.mkdirSync(res, { recursive: true }); + fs.writeFileSync(path.join(res, 'arch.md'), 'MY CURATED CONTENT'); + + const result = createColdTierDefaults(target, { skipExisting: true }); + expect(result.skipped).toContain('arch.md'); + expect(result.created).toContain('lessons-learned.md'); + // Curated content untouched. + expect(fs.readFileSync(path.join(res, 'arch.md'), 'utf-8')).toBe('MY CURATED CONTENT'); + }); +}); + +describe('issue #1012 — cold files are protected user data', () => { + it('treats the cold files as user data (never overwritten by update)', () => { + expect(isUserDataPath('resources/arch.md')).toBe(true); + expect(isUserDataPath('resources/lessons-learned.md')).toBe(true); + }); +}); + +// Integration: `codev update` on a project lacking the cold files must backfill them, +// while never clobbering a customized one. Mirrors the hot-tier update integration test. +describe('issue #1012 — codev update backfills cold files', () => { + let originalCwd: string; + let projectDir: string; + + beforeEach(() => { + originalCwd = process.cwd(); + projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cold-update-')); + fs.mkdirSync(path.join(projectDir, 'codev'), { recursive: true }); + fs.writeFileSync(path.join(projectDir, 'CLAUDE.md'), '# Codev\n\nKEEP_USER_CONTENT'); + fs.writeFileSync(path.join(projectDir, 'AGENTS.md'), '# Codev\n\nKEEP_USER_CONTENT'); + vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(() => { + process.chdir(originalCwd); + vi.restoreAllMocks(); + fs.rmSync(projectDir, { recursive: true, force: true }); + }); + + it('creates missing cold files and reports them in newFiles', async () => { + process.chdir(projectDir); + const { update } = await import('../commands/update.js'); + const result = await update(); + + for (const f of COLD_TIER_FILES) { + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', f)), `${f} created`).toBe(true); + expect(result.newFiles).toContain(`codev/resources/${f}`); + } + }); + + it('leaves a customized cold file byte-identical while backfilling the missing sibling', async () => { + const res = path.join(projectDir, 'codev', 'resources'); + fs.mkdirSync(res, { recursive: true }); + fs.writeFileSync(path.join(res, 'arch.md'), 'MY CURATED ARCH'); + + process.chdir(projectDir); + const { update } = await import('../commands/update.js'); + const result = await update(); + + expect(fs.readFileSync(path.join(res, 'arch.md'), 'utf-8')).toBe('MY CURATED ARCH'); + expect(result.newFiles).not.toContain('codev/resources/arch.md'); + expect(fs.existsSync(path.join(res, 'lessons-learned.md'))).toBe(true); + expect(result.newFiles).toContain('codev/resources/lessons-learned.md'); + }); + + it('--dry-run writes nothing', async () => { + process.chdir(projectDir); + const { update } = await import('../commands/update.js'); + await update({ dryRun: true }); + + for (const f of COLD_TIER_FILES) { + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', f)), `${f} not created in dry-run`).toBe(false); + } + }); +}); diff --git a/packages/codev/src/__tests__/init.test.ts b/packages/codev/src/__tests__/init.test.ts index 269982683..f571564a3 100644 --- a/packages/codev/src/__tests__/init.test.ts +++ b/packages/codev/src/__tests__/init.test.ts @@ -71,7 +71,12 @@ describe('init command', () => { expect(fs.existsSync(path.join(projectDir, 'codev', 'reviews'))).toBe(true); // Spec 0126: projectlist.md is no longer created expect(fs.existsSync(path.join(projectDir, 'codev', 'projectlist.md'))).toBe(false); - // Note: resources/ is NOT created in minimal structure (created by user if needed) + // Spec 987 (hot tier) + issue #1012 (cold tier): codev/resources/ is bootstrapped + // with all four governance files. + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'arch-critical.md'))).toBe(true); + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'lessons-critical.md'))).toBe(true); + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'arch.md'))).toBe(true); + expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', 'lessons-learned.md'))).toBe(true); } finally { process.chdir(originalCwd); } From 8469cf4fa1879d5db1475fe8a3cf8b443a98f325 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:24:26 +1000 Subject: [PATCH 13/28] [PIR #1012] Thread: implement phase complete --- codev/state/pir-1012_thread.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index 19ef4e246..6a8de8b6a 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -38,3 +38,11 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - This SIMPLIFIES the plan: skeleton templates are NOT edited at all (rich templates + their manual-cp note stay as-is). No `copyHotTierDefaults` refactor needed. - Final implementation: `createColdTierDefaults(targetDir, opts)` in scaffold.ts writes two small placeholder consts (arch.md, lessons-learned.md) with skip-if-exists; wired at the same 3 sites next to copyHotTierDefaults; update backfills (consistent with 987). copyHotTierDefaults left completely untouched. - Plan finalized. No open decisions remain. Awaiting plan-approval. + +## Implement phase + +- plan-approval approved; advanced to implement. +- Added `createColdTierDefaults` + `COLD_TIER_STARTERS` to scaffold.ts; wired into init/adopt/update next to copyHotTierDefaults. copyHotTierDefaults + skeleton templates untouched. +- Tests: new cold-tier-materialization.test.ts (7 tests: create/placeholder-marker/skip-existing/user-data-protection/update-backfill/customized-survives/dry-run); init.test.ts comment flipped to positive assertions for all 4 resource files; adopt.test.ts asserts cold files. +- Build: needed full `pnpm build` from worktree root first (codev-core wasn't built — pre-existing infra, not my change). Then green. +- Full suite: 163 files / 3310 tests pass, 48 pre-existing skips. Awaiting dev-approval gate. From 092f45dfa346c507c3ae23fedd94c6955c8b8a81 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sat, 13 Jun 2026 16:25:00 +1000 Subject: [PATCH 14/28] chore(porch): 1012 dev-approval gate-requested --- codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index eae11de3b..83a3cae06 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -11,10 +11,11 @@ gates: approved_at: '2026-06-13T06:18:30.528Z' dev-approval: status: pending + requested_at: '2026-06-13T06:25:00.899Z' pr: status: pending iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-13T06:18:43.738Z' +updated_at: '2026-06-13T06:25:00.899Z' From 8ef441cc25dc87a952d615cd63b975eee481c59c Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:06:20 +1000 Subject: [PATCH 15/28] [PIR #1012] Source cold-tier content from skeleton *.starter.md instead of inline constants --- codev-skeleton/templates/arch.starter.md | 5 ++ .../templates/lessons-learned.starter.md | 5 ++ packages/codev/src/commands/adopt.ts | 8 +-- packages/codev/src/commands/init.ts | 9 +-- packages/codev/src/commands/update.ts | 8 +-- packages/codev/src/lib/scaffold.ts | 71 ++++++++----------- 6 files changed, 52 insertions(+), 54 deletions(-) create mode 100644 codev-skeleton/templates/arch.starter.md create mode 100644 codev-skeleton/templates/lessons-learned.starter.md diff --git a/codev-skeleton/templates/arch.starter.md b/codev-skeleton/templates/arch.starter.md new file mode 100644 index 000000000..9142944c9 --- /dev/null +++ b/codev-skeleton/templates/arch.starter.md @@ -0,0 +1,5 @@ +# Architecture + +This document evolves as the project grows. Update it during the review phase of any work that introduces or changes architectural patterns. + +_No architecture documented yet._ diff --git a/codev-skeleton/templates/lessons-learned.starter.md b/codev-skeleton/templates/lessons-learned.starter.md new file mode 100644 index 000000000..3effefc47 --- /dev/null +++ b/codev-skeleton/templates/lessons-learned.starter.md @@ -0,0 +1,5 @@ +# Lessons Learned + +Durable engineering wisdom captured across the project's work. Update it during the review phase of any work that surfaces a generally-applicable pattern, gotcha, or constraint. + +_No lessons captured yet._ diff --git a/packages/codev/src/commands/adopt.ts b/packages/codev/src/commands/adopt.ts index 61cb283b6..59738c499 100644 --- a/packages/codev/src/commands/adopt.ts +++ b/packages/codev/src/commands/adopt.ts @@ -17,7 +17,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, - createColdTierDefaults, + copyColdTierDefaults, } from '../lib/scaffold.js'; import { updateGitignore } from '../lib/gitignore.js'; @@ -162,9 +162,9 @@ export async function adopt(options: AdoptOptions = {}): Promise { fileCount++; } - // Create the cold-tier governance files (arch.md, lessons-learned.md) with placeholder - // content (issue #1012), skip-existing so a curated copy is preserved. - for (const file of createColdTierDefaults(targetDir, { skipExisting: true }).created) { + // Materialize the cold-tier governance files (arch.md, lessons-learned.md) from the + // skeleton's *.starter.md placeholders (issue #1012), skip-existing so a curated copy is preserved. + for (const file of copyColdTierDefaults(targetDir, skeletonDir, { skipExisting: true }).copied) { console.log(chalk.green(' +'), `codev/resources/${file}`); fileCount++; } diff --git a/packages/codev/src/commands/init.ts b/packages/codev/src/commands/init.ts index acfe02203..c475175b2 100644 --- a/packages/codev/src/commands/init.ts +++ b/packages/codev/src/commands/init.ts @@ -16,7 +16,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, - createColdTierDefaults, + copyColdTierDefaults, } from '../lib/scaffold.js'; import { syncHotContextBlock } from '../lib/managed-block.js'; import { createGitignore } from '../lib/gitignore.js'; @@ -120,9 +120,10 @@ export async function init(projectName?: string, options: InitOptions = {}): Pro fileCount++; } - // Create the cold-tier governance files (arch.md, lessons-learned.md) with placeholder - // content (issue #1012) so the first review-phase read succeeds against a real file. - for (const file of createColdTierDefaults(targetDir).created) { + // Materialize the cold-tier governance files (arch.md, lessons-learned.md) from the + // skeleton's *.starter.md placeholders (issue #1012) so the first review-phase read + // succeeds against a real file. + for (const file of copyColdTierDefaults(targetDir, skeletonDir).copied) { console.log(chalk.green(' +'), `codev/resources/${file}`); fileCount++; } diff --git a/packages/codev/src/commands/update.ts b/packages/codev/src/commands/update.ts index 973395e73..eae3ffe52 100644 --- a/packages/codev/src/commands/update.ts +++ b/packages/codev/src/commands/update.ts @@ -26,7 +26,7 @@ import { copySkills, copyRootFiles, copyHotTierDefaults, - createColdTierDefaults, + copyColdTierDefaults, } from '../lib/scaffold.js'; import { syncHotContextBlock } from '../lib/managed-block.js'; import { @@ -268,12 +268,12 @@ export async function update(options: UpdateOptions = {}): Promise } } - // Backfill the cold-tier governance files for existing adopters (issue #1012), - // skip-existing so a curated file is never overwritten. + // Backfill the cold-tier governance files for existing adopters from the skeleton's + // *.starter.md placeholders (issue #1012), skip-existing so a curated file is never overwritten. if (dryRun) { log(chalk.dim(' + (cold-tier) would create missing codev/resources/{arch,lessons-learned}.md')); } else { - for (const file of createColdTierDefaults(targetDir, { skipExisting: true }).created) { + for (const file of copyColdTierDefaults(targetDir, templatesDir, { skipExisting: true }).copied) { const rel = `codev/resources/${file}`; result.newFiles.push(rel); log(chalk.green(' + (new)'), rel); diff --git a/packages/codev/src/lib/scaffold.ts b/packages/codev/src/lib/scaffold.ts index a33b0f69c..9584ab299 100644 --- a/packages/codev/src/lib/scaffold.ts +++ b/packages/codev/src/lib/scaffold.ts @@ -189,73 +189,60 @@ export function copyHotTierDefaults( return { copied, skipped }; } -interface CreateColdTierDefaultsOptions { - skipExisting?: boolean; -} - -interface CreateColdTierDefaultsResult { - created: string[]; - skipped: string[]; -} - /** - * The cold-tier governance files and their minimal placeholder content (issue #1012). + * Cold-tier governance files: the skeleton starter to copy from → the project-local + * filename it materializes as (issue #1012). * - * These are the on-demand reference archives the hot tier's "Map of …" sections point - * into. Unlike the hot tier, they are NOT copied from the skeleton: the skeleton's - * `templates/{arch,lessons-learned}.md` are rich framework reference templates (they even - * carry a "this file is not copied into projects" note), so copying them verbatim would be - * self-contradicting. Instead each project gets a trivial placeholder it grows over time — - * enough for the review-phase read to succeed against a real, locally-owned file. + * The starter sources are minimal placeholders (`*.starter.md`) kept separate from the rich + * `templates/{arch,lessons-learned}.md` reference templates — those carry a "this file is not + * copied into projects" note and are the manual-`cp` opt-in, so they must not be the copied + * starter. Each project instead gets a trivial placeholder it grows over time, enough for the + * review-phase read to succeed against a real, locally-owned file. */ -export const COLD_TIER_STARTERS: Record = { - 'arch.md': - '# Architecture\n\n' + - 'This document evolves as the project grows. Update it during the review phase ' + - 'of any work that introduces or changes architectural patterns.\n\n' + - '_No architecture documented yet._\n', - 'lessons-learned.md': - '# Lessons Learned\n\n' + - "Durable engineering wisdom captured across the project's work. Update it during " + - 'the review phase of any work that surfaces a generally-applicable pattern, gotcha, ' + - 'or constraint.\n\n' + - '_No lessons captured yet._\n', -}; +export const COLD_TIER_FILES = [ + { src: 'arch.starter.md', dest: 'arch.md' }, + { src: 'lessons-learned.starter.md', dest: 'lessons-learned.md' }, +] as const; /** - * Create the cold-tier governance files (arch.md, lessons-learned.md) in codev/resources/ - * with minimal placeholder content (issue #1012). + * Copy the cold-tier governance files (arch.md, lessons-learned.md) from the skeleton's + * `*.starter.md` placeholders into the project's codev/resources/. * * Companion to `copyHotTierDefaults`: Spec 987 materializes the hot tier on * init/adopt/update but left the cold tier — which the review prompts read and the hot-tier * maps point into — uncreated. `skipExisting` so a curated file is never overwritten (the - * cold files are already registered as protected user data in templates.ts). + * cold files are already registered as protected user data in templates.ts). Results are + * keyed by the destination filename. */ -export function createColdTierDefaults( +export function copyColdTierDefaults( targetDir: string, - options: CreateColdTierDefaultsOptions = {} -): CreateColdTierDefaultsResult { + skeletonDir: string, + options: CopyResourceTemplatesOptions = {} +): CopyResourceTemplatesResult { const { skipExisting = false } = options; const resourcesDir = path.join(targetDir, 'codev', 'resources'); - const created: string[] = []; + const copied: string[] = []; const skipped: string[] = []; if (!fs.existsSync(resourcesDir)) { fs.mkdirSync(resourcesDir, { recursive: true }); } - for (const [file, content] of Object.entries(COLD_TIER_STARTERS)) { - const destPath = path.join(resourcesDir, file); + for (const { src, dest } of COLD_TIER_FILES) { + const destPath = path.join(resourcesDir, dest); + const srcPath = path.join(skeletonDir, 'templates', src); if (skipExisting && fs.existsSync(destPath)) { - skipped.push(file); + skipped.push(dest); continue; } - fs.writeFileSync(destPath, content); - created.push(file); + if (fs.existsSync(srcPath)) { + fs.copyFileSync(srcPath, destPath); + copied.push(dest); + } } - return { created, skipped }; + return { copied, skipped }; } interface CopyRootFilesOptions { From 1c290516d85a42cb081debf255009263d9ee8ade Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:06:27 +1000 Subject: [PATCH 16/28] [PIR #1012] Tests: cold-tier copies from skeleton starters --- .../cold-tier-materialization.test.ts | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/codev/src/__tests__/cold-tier-materialization.test.ts b/packages/codev/src/__tests__/cold-tier-materialization.test.ts index 51d2a1a32..b12faf5f7 100644 --- a/packages/codev/src/__tests__/cold-tier-materialization.test.ts +++ b/packages/codev/src/__tests__/cold-tier-materialization.test.ts @@ -7,31 +7,42 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; -import { createColdTierDefaults, COLD_TIER_STARTERS } from '../lib/scaffold.js'; +import { copyColdTierDefaults, COLD_TIER_FILES } from '../lib/scaffold.js'; import { isUserDataPath } from '../lib/templates.js'; -const COLD_TIER_FILES = Object.keys(COLD_TIER_STARTERS); +const COLD_DEST_FILES = COLD_TIER_FILES.map(f => f.dest); -describe('issue #1012 — createColdTierDefaults', () => { +describe('issue #1012 — copyColdTierDefaults', () => { let tmp: string; + let skeleton: string; beforeEach(() => { tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cold-mat-')); + skeleton = path.join(tmp, 'skeleton'); + fs.mkdirSync(path.join(skeleton, 'templates'), { recursive: true }); + fs.writeFileSync( + path.join(skeleton, 'templates', 'arch.starter.md'), + '# Architecture\n\n_No architecture documented yet._\n' + ); + fs.writeFileSync( + path.join(skeleton, 'templates', 'lessons-learned.starter.md'), + '# Lessons Learned\n\n_No lessons captured yet._\n' + ); }); afterEach(() => fs.rmSync(tmp, { recursive: true, force: true })); - it('creates both cold files into codev/resources/, creating the dir', () => { + it('copies both cold files into codev/resources/ under their dest names, creating the dir', () => { const target = path.join(tmp, 'proj'); - const result = createColdTierDefaults(target); - expect(result.created.sort()).toEqual([...COLD_TIER_FILES].sort()); - for (const f of COLD_TIER_FILES) { + const result = copyColdTierDefaults(target, skeleton); + expect(result.copied.sort()).toEqual([...COLD_DEST_FILES].sort()); + for (const f of COLD_DEST_FILES) { expect(fs.existsSync(path.join(target, 'codev', 'resources', f))).toBe(true); } }); it('writes the placeholder marker so the file is clearly a stub to replace', () => { const target = path.join(tmp, 'proj'); - createColdTierDefaults(target); + copyColdTierDefaults(target, skeleton); const res = path.join(target, 'codev', 'resources'); expect(fs.readFileSync(path.join(res, 'arch.md'), 'utf-8')).toContain('_No architecture documented yet._'); expect(fs.readFileSync(path.join(res, 'lessons-learned.md'), 'utf-8')).toContain('_No lessons captured yet._'); @@ -43,9 +54,9 @@ describe('issue #1012 — createColdTierDefaults', () => { fs.mkdirSync(res, { recursive: true }); fs.writeFileSync(path.join(res, 'arch.md'), 'MY CURATED CONTENT'); - const result = createColdTierDefaults(target, { skipExisting: true }); + const result = copyColdTierDefaults(target, skeleton, { skipExisting: true }); expect(result.skipped).toContain('arch.md'); - expect(result.created).toContain('lessons-learned.md'); + expect(result.copied).toContain('lessons-learned.md'); // Curated content untouched. expect(fs.readFileSync(path.join(res, 'arch.md'), 'utf-8')).toBe('MY CURATED CONTENT'); }); @@ -85,7 +96,7 @@ describe('issue #1012 — codev update backfills cold files', () => { const { update } = await import('../commands/update.js'); const result = await update(); - for (const f of COLD_TIER_FILES) { + for (const f of COLD_DEST_FILES) { expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', f)), `${f} created`).toBe(true); expect(result.newFiles).toContain(`codev/resources/${f}`); } @@ -111,7 +122,7 @@ describe('issue #1012 — codev update backfills cold files', () => { const { update } = await import('../commands/update.js'); await update({ dryRun: true }); - for (const f of COLD_TIER_FILES) { + for (const f of COLD_DEST_FILES) { expect(fs.existsSync(path.join(projectDir, 'codev', 'resources', f)), `${f} not created in dry-run`).toBe(false); } }); From 046f229c2668da4f61fb255de7115af9696aa0c4 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:06:43 +1000 Subject: [PATCH 17/28] [PIR #1012] Thread: convention fix to skeleton-sourced starters --- codev/state/pir-1012_thread.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index 6a8de8b6a..afad79cc7 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -46,3 +46,10 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - Tests: new cold-tier-materialization.test.ts (7 tests: create/placeholder-marker/skip-existing/user-data-protection/update-backfill/customized-survives/dry-run); init.test.ts comment flipped to positive assertions for all 4 resource files; adopt.test.ts asserts cold files. - Build: needed full `pnpm build` from worktree root first (codev-core wasn't built — pre-existing infra, not my change). Then green. - Full suite: 163 files / 3310 tests pass, 48 pre-existing skips. Awaiting dev-approval gate. + +## dev-approval iter: convention fix (skeleton-sourced content) + +- Architect flagged: inline `COLD_TIER_STARTERS` constants in scaffold.ts break the copy-from-skeleton convention. Chose (via question) "minimal starter files in skeleton". +- Refactored: added `codev-skeleton/templates/arch.starter.md` + `lessons-learned.starter.md` (the 4-line placeholders); replaced inline `createColdTierDefaults` with copy-based `copyColdTierDefaults(targetDir, skeletonDir, opts)` that maps starter→dest (arch.starter.md→arch.md). Rich `templates/{arch,lessons-learned}.md` left untouched (manual-cp reference). copyHotTierDefaults untouched. +- COLD_TIER_FILES is now `[{src,dest}]`. Callers pass skeletonDir/templatesDir and use `.copied`. +- Tests updated to mock a skeleton dir; full suite green (163/3310). Smoke: init copies starters → arch.md/lessons-learned.md, no .starter.md leaks into project. From 9fa9c2316dd737007ddf67efc429dea1ddc23b51 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:12:54 +1000 Subject: [PATCH 18/28] [PIR #1012] Mark cold-tier starters with explicit STARTER replace-me comment --- codev-skeleton/templates/arch.starter.md | 4 ++++ codev-skeleton/templates/lessons-learned.starter.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/codev-skeleton/templates/arch.starter.md b/codev-skeleton/templates/arch.starter.md index 9142944c9..680910cbe 100644 --- a/codev-skeleton/templates/arch.starter.md +++ b/codev-skeleton/templates/arch.starter.md @@ -1,5 +1,9 @@ # Architecture + + This document evolves as the project grows. Update it during the review phase of any work that introduces or changes architectural patterns. _No architecture documented yet._ diff --git a/codev-skeleton/templates/lessons-learned.starter.md b/codev-skeleton/templates/lessons-learned.starter.md index 3effefc47..2e09adc8d 100644 --- a/codev-skeleton/templates/lessons-learned.starter.md +++ b/codev-skeleton/templates/lessons-learned.starter.md @@ -1,5 +1,9 @@ # Lessons Learned + + Durable engineering wisdom captured across the project's work. Update it during the review phase of any work that surfaces a generally-applicable pattern, gotcha, or constraint. _No lessons captured yet._ From 43d3404467624df925f9cd29e8b4ce817c128b9a Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:13:10 +1000 Subject: [PATCH 19/28] [PIR #1012] Thread: explicit replace-me marker on cold starters --- codev/state/pir-1012_thread.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index afad79cc7..ac2154558 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -53,3 +53,8 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - Refactored: added `codev-skeleton/templates/arch.starter.md` + `lessons-learned.starter.md` (the 4-line placeholders); replaced inline `createColdTierDefaults` with copy-based `copyColdTierDefaults(targetDir, skeletonDir, opts)` that maps starter→dest (arch.starter.md→arch.md). Rich `templates/{arch,lessons-learned}.md` left untouched (manual-cp reference). copyHotTierDefaults untouched. - COLD_TIER_FILES is now `[{src,dest}]`. Callers pass skeletonDir/templatesDir and use `.copied`. - Tests updated to mock a skeleton dir; full suite green (163/3310). Smoke: init copies starters → arch.md/lessons-learned.md, no .starter.md leaks into project. + +## dev-approval iter: explicit replace-me marker + +- Architect asked whether a builder/architect would know to remove the placeholder. Found the gap: review prompts + update-arch-docs skill never mention the placeholder, so the agent path could append below it. Hot-tier starters use an explicit `STARTER: replace…` comment; cold lacked one. +- Added a `` HTML comment to both `*.starter.md` files, mirroring the hot-tier convention. Satisfies the issue AC "clearly marked as a placeholder ready to be replaced." Full suite green; smoke confirms the comment lands in the materialized arch.md. From a7736ef81b06bb0653b83e307fe3a82b7f509454 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:18:04 +1000 Subject: [PATCH 20/28] chore(porch): 1012 dev-approval gate-approved --- .../projects/1012-scaffold-codev-init-bootstraps/status.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index 83a3cae06..719879750 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -10,12 +10,13 @@ gates: requested_at: '2026-06-12T19:28:14.611Z' approved_at: '2026-06-13T06:18:30.528Z' dev-approval: - status: pending + status: approved requested_at: '2026-06-13T06:25:00.899Z' + approved_at: '2026-06-14T00:18:04.391Z' pr: status: pending iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-13T06:25:00.899Z' +updated_at: '2026-06-14T00:18:04.395Z' From 4af04f455beca3a4336d5791796ec3c8d54e91d3 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:18:12 +1000 Subject: [PATCH 21/28] chore(porch): 1012 review phase-transition --- .../projects/1012-scaffold-codev-init-bootstraps/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index 719879750..f62771ebe 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -1,7 +1,7 @@ id: '1012' title: scaffold-codev-init-bootstraps protocol: pir -phase: implement +phase: review plan_phases: [] current_plan_phase: null gates: @@ -19,4 +19,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-14T00:18:04.395Z' +updated_at: '2026-06-14T00:18:12.136Z' From 4d3c0fe447831aaaad9311ed6f48f4667c8d0fef Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:21:00 +1000 Subject: [PATCH 22/28] [PIR #1012] Review + retrospective; route arch/lessons cold-tier updates --- codev/resources/arch.md | 2 +- codev/resources/lessons-learned.md | 2 + .../1012-scaffold-codev-init-bootstraps.md | 59 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 codev/reviews/1012-scaffold-codev-init-bootstraps.md diff --git a/codev/resources/arch.md b/codev/resources/arch.md index b6e545104..e84f7b292 100644 --- a/codev/resources/arch.md +++ b/codev/resources/arch.md @@ -1925,7 +1925,7 @@ Spec 987 split the two governance docs into a **hot/cold** two-tier model so dur - **porch builders** — `buildHotTierContext()` in `packages/codev/src/commands/porch/prompts.ts` resolves the hot files via the runtime four-tier resolver (`resolveCodevFile`) and prepends them to *every* phase prompt. - **interactive sessions** — a generated managed block (`packages/codev/src/lib/managed-block.ts`, delimited by ``) is written into `CLAUDE.md`/`AGENTS.md` at `codev init`/`update` time (non-clobbering; preserves user content). -Hot files are materialized into projects by `copyHotTierDefaults` (wired into init/adopt/update) and resolve from the skeleton at tier-4 until a project curates its own. Producers **route** new facts/lessons by tier at review time (see the review prompts); MAINTAIN + the `update-arch-docs` skill police the hot caps, displacement (demote to cold when full), and cold-doc map accuracy. The cap is load-bearing: it is what keeps the hot tier cheap enough to inject everywhere. +Hot files are materialized into projects by `copyHotTierDefaults` (wired into init/adopt/update) and resolve from the skeleton at tier-4 until a project curates its own. The cold files are likewise bootstrapped on init/adopt/update by `copyColdTierDefaults`, which copies minimal placeholder starters from the skeleton's `templates/{arch,lessons-learned}.starter.md` into `codev/resources/{arch,lessons-learned}.md` (issue #1012) — distinct from the rich `templates/{arch,lessons-learned}.md` reference templates, which are a manual-`cp` opt-in and are never auto-copied. Both materializers are skip-existing, so a project's curated copy is never overwritten; the cold files are registered as protected user data in `templates.ts`. Producers **route** new facts/lessons by tier at review time (see the review prompts); MAINTAIN + the `update-arch-docs` skill police the hot caps, displacement (demote to cold when full), and cold-doc map accuracy. The cap is load-bearing: it is what keeps the hot tier cheap enough to inject everywhere. ## Troubleshooting diff --git a/codev/resources/lessons-learned.md b/codev/resources/lessons-learned.md index 82075f2f1..42d76ebd5 100644 --- a/codev/resources/lessons-learned.md +++ b/codev/resources/lessons-learned.md @@ -337,6 +337,8 @@ Generalizable wisdom extracted from review documents, ordered by impact. Updated - [From 0376] The research agent pattern (spawning a subagent to read all review files in parallel and return structured data) should be documented as a standard approach for future analyses. - [From #909] Cross-file content references in framework files are brittle. Deduplicating shared content across `CLAUDE.md` and a role file via "see X for the table" pointers is a novel pattern with no precedent in this repo (every existing CLAUDE.md mention in framework files is for diffing or scaffolding, not content lookup). The pointer is redundant when the referenced file is auto-loaded and misleading if it isn't. Keep each file self-contained for its audience: `CLAUDE.md` for everyone-loaded content (vocabulary + policy), role files for role-specific content (recipes, workflows). - [From #909] Codev's skeleton has a two-layer design that's intentional: the **internal automation layer** (`packages/codev/scripts/forge//` concept commands, dispatched via `packages/codev/src/lib/forge.ts`) is forge-agnostic; the **user-facing layer** (skeleton docs, AI prompts, protocol prompts) hardcodes `gh` directly throughout. The forge concept set is read-mostly (`issue-view`, `pr-list`, etc.) — no concepts for label management, jq-piping, or interactive ops. When adding new skeleton content, match the established `gh`-direct pattern. Localized forge-CLI awareness in one section creates inconsistency vs. the rest of the skeleton. +- [From #1012] Materialized project starter files belong in `codev-skeleton/templates/` and are copied by the scaffold, never hardcoded as TS string constants — keep the copy-from-skeleton convention uniform across every materialized file. When the desired starter differs from an existing rich template (minimal placeholder vs. the full `arch.md` reference template), add a dedicated `*.starter.md` source rather than inlining content or gutting the reference template. +- [From #1012] A placeholder filled by the agent-driven review path needs an explicit "replace me" marker (mirror the hot-tier `` convention), not just an italic `_None yet._` line — otherwise the builder may append below it, leaving a self-contradictory file. The review prompts and `update-arch-docs` skill never mention the placeholder, so the file itself must signal replacement. - [From 778] In a self-hosted Codev repo the four-tier resolver means `codev/` instance copies *shadow* `codev-skeleton/`, so the two trees (and the `codev/` copies themselves) drift independently. A terminology/backend change (Gemini-CLI → `agy`) cost 3 review iterations because each round surfaced another stale copy (skeleton → `DEPENDENCIES.md` → `resources/commands/consult.md` → `codev.md` + `arch.md`). When changing any shared doc, grep BOTH trees in one pass and run `diff codev/ codev-skeleton/` for every shared file — empty diff is the consistency proof. Distinguish in-scope current docs from historical artifacts (`specs/`, `plans/`, dated analyses) which must keep their original wording. ## 3-Way Reviews diff --git a/codev/reviews/1012-scaffold-codev-init-bootstraps.md b/codev/reviews/1012-scaffold-codev-init-bootstraps.md new file mode 100644 index 000000000..e74673b67 --- /dev/null +++ b/codev/reviews/1012-scaffold-codev-init-bootstraps.md @@ -0,0 +1,59 @@ +# PIR Review: Bootstrap cold-tier `codev/resources/` files on init/adopt/update + +Fixes #1012 + +## Summary + +Fresh `codev init` projects had no `codev/resources/arch.md` or `lessons-learned.md`, so the first PIR/SPIR/ASPIR/MAINTAIN review phase failed when it read those files. Spec 987 had already wired up materialization of the *hot*-tier governance files (`arch-critical.md`, `lessons-critical.md`) but left the *cold* tier uncreated. This change adds `copyColdTierDefaults`, which materializes `arch.md` and `lessons-learned.md` from minimal skeleton placeholder starters (`templates/{arch,lessons-learned}.starter.md`) on init/adopt/update — mirroring the hot-tier pattern, skip-existing so curated copies are never overwritten, and `update` backfills the cold files for projects created before this fix. + +## Files Changed + +- `codev-skeleton/templates/arch.starter.md` (+9 / -0) — new minimal cold starter with an explicit `STARTER:` replace-me marker +- `codev-skeleton/templates/lessons-learned.starter.md` (+9 / -0) — same, for lessons +- `packages/codev/src/lib/scaffold.ts` (+56 / -0) — `COLD_TIER_FILES` mapping + `copyColdTierDefaults` +- `packages/codev/src/commands/init.ts` (+9 / -0) — wire cold materialization beside hot +- `packages/codev/src/commands/adopt.ts` (+8 / -0) — same (skip-existing) +- `packages/codev/src/commands/update.ts` (+13 / -0) — same, with dry-run + `newFiles` reporting (backfill for existing adopters) +- `packages/codev/src/__tests__/cold-tier-materialization.test.ts` (+129 / -0) — new test file (7 tests) +- `packages/codev/src/__tests__/init.test.ts` (+7 / -1) — positive assertions for all four resource files +- `packages/codev/src/__tests__/adopt.test.ts` (+4 / -0) — assert cold files created on adopt +- `codev/resources/arch.md`, `codev/resources/lessons-learned.md` — governance-doc updates (see below) + +## Commits + +- `f0e75182` [PIR #1012] Bootstrap cold-tier resources (arch.md, lessons-learned.md) on init/adopt/update +- `04e4deb1` [PIR #1012] Tests: cold-tier materialization + init/adopt assertions +- `2d133f13` [PIR #1012] Source cold-tier content from skeleton *.starter.md instead of inline constants +- `04478a98` [PIR #1012] Tests: cold-tier copies from skeleton starters +- `f5f4b118` [PIR #1012] Mark cold-tier starters with explicit STARTER replace-me comment +- (plus `[PIR #1012] Thread:` / `Plan:` housekeeping commits) + +## Test Results + +- `pnpm build`: ✓ pass (build core first from worktree root — `codev-core` isn't pre-built in a fresh worktree) +- `pnpm test`: ✓ pass — full suite 163 files / 3310 tests, 48 pre-existing skips; 7 new cold-tier tests +- Manual verification (and at the dev-approval gate): built CLI `init` into a temp dir creates `codev/resources/{arch,lessons-learned}.md` with the placeholder + `STARTER:` marker; `.starter.md` source files do not leak into the project; `update` backfills missing cold files without clobbering a customized one; `--dry-run` writes nothing. + +## Architecture Updates + +**COLD** (`codev/resources/arch.md`) — updated. The "Governance Docs (Hot/Cold Tiers)" section previously stated only the hot files were materialized; extended it to record that the cold files are now bootstrapped by `copyColdTierDefaults` from `*.starter.md` placeholders (distinct from the rich manual-`cp` reference templates), both materializers skip-existing. No **HOT** (`arch-critical.md`) change: the existing hot fact about two-tier routing is unaffected — this adds materialization detail, which is reference-tier, not a capped always-on fact. + +## Lessons Learned Updates + +**COLD** (`codev/resources/lessons-learned.md`) — added two entries under Documentation: (1) materialized starter files belong in `codev-skeleton/templates/` and are copied, never hardcoded as TS constants — add a dedicated `*.starter.md` when the desired starter differs from an existing rich template; (2) a placeholder filled by the agent-driven review path needs an explicit "replace me" marker (mirroring the hot-tier `STARTER:` convention), since the review prompts and `update-arch-docs` skill never mention the placeholder. No **HOT** (`lessons-critical.md`) change: these are scaffold-convention reference tips, not behavior-changing rules warranting a capped slot. + +## Things to Look At During PR Review + +- **`COLD_TIER_FILES` is a `{ src, dest }` mapping** (`arch.starter.md` → `arch.md`), unlike the hot tier's same-name copy. This is deliberate: the plain `templates/arch.md` is the rich reference template (with a "this file is not copied into projects" note) and must NOT be the copied starter, so the minimal starter lives in a separate `*.starter.md` source. +- **`copyHotTierDefaults` is untouched** — the cold function is a sibling, so the load-bearing Spec 987 hot path carries zero risk from this change. +- **`update` backfill**: cold files are already in `USER_DATA_PATTERNS` (`templates.ts`), so update's clean step never overwrote them; this change makes update *create* the missing ones (consistent with how 987 backfills the hot files). + +## How to Test Locally + +For reviewers pulling the branch: + +- **View diff**: VSCode sidebar → right-click builder pir-1012 → **Review Diff** +- **What to verify**: + 1. `pnpm build` (from worktree root), then `node packages/codev/dist/cli.js init /tmp/p --yes` → inspect `/tmp/p/codev/resources/` (all four `.md` present; `arch.md`/`lessons-learned.md` carry the `STARTER:` marker; no `.starter.md` files). + 2. In a codev project missing the cold files, `codev update` backfills both; a customized `arch.md` survives byte-identical. + 3. `codev update --dry-run` writes nothing. From 9401d94656deb07f49b4e914270d89ced2c45a18 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:21:22 +1000 Subject: [PATCH 23/28] chore(porch): 1012 record PR #1046 --- .../1012-scaffold-codev-init-bootstraps/status.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index f62771ebe..a82aa259a 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -19,4 +19,9 @@ iteration: 1 build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-14T00:18:12.136Z' +updated_at: '2026-06-14T00:21:22.369Z' +pr_history: + - phase: review + pr_number: 1046 + branch: builder/pir-1012 + created_at: '2026-06-14T00:21:22.368Z' From 373d422d868734b7850cf79bad57b6c237ccdada Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:21:30 +1000 Subject: [PATCH 24/28] chore(porch): 1012 review build-complete --- .../projects/1012-scaffold-codev-init-bootstraps/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index a82aa259a..589bdd1b9 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -16,10 +16,10 @@ gates: pr: status: pending iteration: 1 -build_complete: false +build_complete: true history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-14T00:21:22.369Z' +updated_at: '2026-06-14T00:21:30.690Z' pr_history: - phase: review pr_number: 1046 From 44b4aab7985869f0221ea505f9ba62a6e67753af Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:22:19 +1000 Subject: [PATCH 25/28] [PIR #1012] Thread: review phase, PR #1046, consult running --- codev/state/pir-1012_thread.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index ac2154558..34ca04178 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -58,3 +58,9 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - Architect asked whether a builder/architect would know to remove the placeholder. Found the gap: review prompts + update-arch-docs skill never mention the placeholder, so the agent path could append below it. Hot-tier starters use an explicit `STARTER: replace…` comment; cold lacked one. - Added a `` HTML comment to both `*.starter.md` files, mirroring the hot-tier convention. Satisfies the issue AC "clearly marked as a placeholder ready to be replaced." Full suite green; smoke confirms the comment lands in the materialized arch.md. + +## Review phase + +- dev-approval approved; advanced to review. +- Wrote codev/reviews/1012-scaffold-codev-init-bootstraps.md. Routed cold-tier governance updates: arch.md "Governance Docs" section extended to cover copyColdTierDefaults; lessons-learned.md +2 Documentation entries (skeleton-sourced starters; explicit replace-me marker). No hot-tier changes (reference-tier detail, not capped facts). +- PR #1046 opened (Fixes #1012), recorded with porch. porch done → structural checks pass. 3-way consult (gemini/codex/claude, type=impl) running in background — single advisory pass (max_iterations:1). Awaiting verdicts → pr gate. From 170cd14a59554e3f9a36e7e698b2e2e41cb9260f Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:29:52 +1000 Subject: [PATCH 26/28] [PIR #1012] Review: document consult false-positive, resolved by rebase --- codev/reviews/1012-scaffold-codev-init-bootstraps.md | 1 + 1 file changed, 1 insertion(+) diff --git a/codev/reviews/1012-scaffold-codev-init-bootstraps.md b/codev/reviews/1012-scaffold-codev-init-bootstraps.md index e74673b67..8300f1660 100644 --- a/codev/reviews/1012-scaffold-codev-init-bootstraps.md +++ b/codev/reviews/1012-scaffold-codev-init-bootstraps.md @@ -47,6 +47,7 @@ Fresh `codev init` projects had no `codev/resources/arch.md` or `lessons-learned - **`COLD_TIER_FILES` is a `{ src, dest }` mapping** (`arch.starter.md` → `arch.md`), unlike the hot tier's same-name copy. This is deliberate: the plain `templates/arch.md` is the rich reference template (with a "this file is not copied into projects" note) and must NOT be the copied starter, so the minimal starter lives in a separate `*.starter.md` source. - **`copyHotTierDefaults` is untouched** — the cold function is a sibling, so the load-bearing Spec 987 hot path carries zero risk from this change. - **`update` backfill**: cold files are already in `USER_DATA_PATTERNS` (`templates.ts`), so update's clean step never overwrote them; this change makes update *create* the missing ones (consistent with how 987 backfills the hot files). +- **3-way consult outcome**: Gemini APPROVE (HIGH), Codex APPROVE (HIGH), Claude COMMENT (HIGH). Claude's COMMENT flagged apparent removals of unrelated governance entries (#859 from arch.md, #913 from lessons-learned.md). **Verified false positive**: `git diff ...HEAD` showed the governance edits are purely additive (no removals); the branch was simply behind main, and those entries were added to main after the branch point. Resolved by rebasing onto current main — the branch is now 0 commits behind, `#859` is present, and the net governance diff remains additive (arch.md +1 sentence, lessons-learned.md +2 entries). ## How to Test Locally From 90c41dc5d4e439acbe94ef94f662681ca29f7d8b Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:30:03 +1000 Subject: [PATCH 27/28] chore(porch): 1012 pr gate-requested --- .../1012-scaffold-codev-init-bootstraps/status.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml index 589bdd1b9..1d6137dc4 100644 --- a/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml +++ b/codev/projects/1012-scaffold-codev-init-bootstraps/status.yaml @@ -15,13 +15,15 @@ gates: approved_at: '2026-06-14T00:18:04.391Z' pr: status: pending + requested_at: '2026-06-14T00:30:03.080Z' iteration: 1 -build_complete: true +build_complete: false history: [] started_at: '2026-06-12T19:24:46.703Z' -updated_at: '2026-06-14T00:21:30.690Z' +updated_at: '2026-06-14T00:30:03.080Z' pr_history: - phase: review pr_number: 1046 branch: builder/pir-1012 created_at: '2026-06-14T00:21:22.368Z' +pr_ready_for_human: true From dcc1fa7d2fb7efe1053cebbad8900a0978f3c8f3 Mon Sep 17 00:00:00 2001 From: Amr Elsayed Date: Sun, 14 Jun 2026 10:30:37 +1000 Subject: [PATCH 28/28] [PIR #1012] Thread: pr gate pending, consult verdicts recorded --- codev/state/pir-1012_thread.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codev/state/pir-1012_thread.md b/codev/state/pir-1012_thread.md index 34ca04178..9874c05f8 100644 --- a/codev/state/pir-1012_thread.md +++ b/codev/state/pir-1012_thread.md @@ -64,3 +64,6 @@ Issue #1012 — scaffold: `codev init` bootstraps `codev/resources/` with arch.m - dev-approval approved; advanced to review. - Wrote codev/reviews/1012-scaffold-codev-init-bootstraps.md. Routed cold-tier governance updates: arch.md "Governance Docs" section extended to cover copyColdTierDefaults; lessons-learned.md +2 Documentation entries (skeleton-sourced starters; explicit replace-me marker). No hot-tier changes (reference-tier detail, not capped facts). - PR #1046 opened (Fixes #1012), recorded with porch. porch done → structural checks pass. 3-way consult (gemini/codex/claude, type=impl) running in background — single advisory pass (max_iterations:1). Awaiting verdicts → pr gate. +- Verdicts: gemini APPROVE/HIGH, codex APPROVE/HIGH, claude COMMENT/HIGH. Claude flagged "removed unrelated governance entries (#859 arch, #913 lessons)". VERIFIED FALSE POSITIVE via `git diff merge-base...HEAD` (purely additive) — branch was 71 commits behind main; #859 was added to main after my branch point (#913 never existed in either tree). Root cause: stale diff base. +- Resolved by rebasing onto current main (was 71 behind → 0; clean, no conflicts; code targets untouched by the 71 commits — only governance docs overlapped, combined cleanly). #859 now present; my edits intact; net governance diff still additive. Rebuilt + full suite green (164/3313). Documented in review "Things to Look At"; refreshed PR body; force-pushed. +- porch next → **pr gate pending** (gemini/codex APPROVE, claude COMMENT). Notified architect leading with the false-positive disposition. Waiting at pr gate for human merge approval.