codecartographer-pi 0.12.11 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/.codecarto/CONTRIBUTING.md +1 -1
  2. package/.codecarto/GUIDE.md +4 -3
  3. package/.codecarto/NEW_THREAD_BLURB.md +9 -10
  4. package/.codecarto/findings/defect-scan-mechanical/SKILL.md +1 -1
  5. package/.codecarto/skills/spec-delta-application/SKILL.md +1 -1
  6. package/.codecarto/templates/architecture-map.md +2 -1
  7. package/.codecarto/templates/behavioral-contracts.md +2 -1
  8. package/.codecarto/templates/closeout-template.md +5 -2
  9. package/.codecarto/templates/mechanical-defects.md +4 -2
  10. package/.codecarto/templates/protocols-and-state.md +2 -1
  11. package/.codecarto/templates/reverse-engineering-bundle.md +2 -1
  12. package/.codecarto/templates/semantic-defects.md +3 -1
  13. package/.codecarto/templates/thread-log-entry-template.md +11 -13
  14. package/.codecarto/workflow/VALIDATE.md +17 -14
  15. package/.codecarto/workflow/pipeline-architecture-only.yaml +2 -2
  16. package/.codecarto/workflow/pipeline-defect-scan.yaml +4 -4
  17. package/.codecarto/workflow/pipeline-full-with-audit.yaml +12 -12
  18. package/.codecarto/workflow/pipeline-full-with-deep-audit.yaml +15 -15
  19. package/.codecarto/workflow/pipeline-lite.yaml +6 -6
  20. package/.codecarto/workflow/pipeline.yaml +10 -10
  21. package/.codecarto/workflow/scaffold-version.yaml +6 -0
  22. package/README.md +23 -1
  23. package/dist/core/completion.js +13 -0
  24. package/dist/core/prompts.js +20 -1
  25. package/dist/core/types.d.ts +2 -0
  26. package/dist/core/workspace.d.ts +9 -0
  27. package/dist/core/workspace.js +54 -0
  28. package/dist/extensions/codecarto/index.js +4 -1
  29. package/dist/mcp-server/server.d.ts +5 -0
  30. package/dist/mcp-server/server.js +22 -10
  31. package/package.json +11 -3
@@ -25,7 +25,7 @@ Key things to verify:
25
25
  - Pipeline YAML files parse correctly and phase dependencies resolve.
26
26
  - SKILL.md instructions produce output that matches the corresponding template.
27
27
  - Validation protocol (VALIDATE.md) catches missing or incomplete sections.
28
- - `status.yaml` updates correctly after each phase, with the schema-split `open_questions` / `carry_forward` lists used for what each is for (see GUIDE.md "Open Questions vs Carry-Forward").
28
+ - `status.yaml` updates correctly after each phase from the session's phase handoff, with the schema-split `open_questions` / `carry_forward` lists used for what each is for (see GUIDE.md "Open Questions vs Carry-Forward").
29
29
  - All file paths referenced in pipeline YAML, GUIDE.md, and SKILL.md files exist.
30
30
 
31
31
  #### THREAD_LOG.md de-dup check
@@ -39,8 +39,8 @@ If this is the first LLM to touch this project — no closeouts in `closeouts/`,
39
39
  If the user accepts the orchestrator role:
40
40
 
41
41
  1. Initialize `CONVENTIONS.md` from `templates/conventions-template.md` and `DECISIONS.md` from `templates/decisions-template.md` (skeletons only — populate as patterns and decisions accumulate).
42
- 2. Set `project_name` in `workflow/status.yaml`.
43
- 3. **Do not run the architecture phase yourself.** The orchestrator never executes pipeline phases — that is the implementing-session role (see §Roles). Instead, **draft an implementing-session prompt for the architecture phase** that a fresh thread will execute. Hand the prompt to the user; they spin up a new thread with it; the implementing thread runs the phase, validates, writes a closeout, and reports back; you (the orchestrator) read the returned closeout, update `CONVENTIONS.md` / `DECISIONS.md` / `workflow/status.yaml` per the closeout ritual, and draft the next phase's prompt. **Phase work always happens in fresh implementing-session threads, never in the orchestration thread.** A useful self-check: if you find yourself reading a `findings/<phase>/SKILL.md` or writing a `findings/<phase>/<primary-output>.md`, you have slipped into the implementing-session role — stop, draft the prompt, hand off.
42
+ 2. Confirm the project name. `project_name` defaults to the repository directory name and is persisted by the framework on the first completion; it only needs attention if the project should be called something else.
43
+ 3. **Do not run the architecture phase yourself.** The orchestrator never executes pipeline phases — that is the implementing-session role (see §Roles). Instead, **draft an implementing-session prompt for the architecture phase** that a fresh thread will execute. Hand the prompt to the user; they spin up a new thread with it; the implementing thread runs the phase, validates, writes a closeout, and reports back; you (the orchestrator) read the returned closeout and update `CONVENTIONS.md` / `DECISIONS.md` from it, then draft the next phase's prompt. The implementing session's own completion already applied its handoff to `workflow/status.yaml` the orchestrator never edits that file. **Phase work always happens in fresh implementing-session threads, never in the orchestration thread.** A useful self-check: if you find yourself reading a `findings/<phase>/SKILL.md` or writing a `findings/<phase>/<primary-output>.md`, you have slipped into the implementing-session role — stop, draft the prompt, hand off.
44
44
 
45
45
  If the user declines:
46
46
 
@@ -63,7 +63,7 @@ Read these files in order before doing any analysis:
63
63
 
64
64
  All paths in this guide are relative to `.codecarto/` unless stated otherwise.
65
65
 
66
- If `project_name` in `workflow/status.yaml` is blank, set it to the name of this repository before starting analysis.
66
+ A blank `project_name` in `workflow/status.yaml` needs no action: the framework resolves it to this repository's directory name and persists it on the first completion.
67
67
 
68
68
  Treat this workspace as durable memory across sessions. Do not invent a new structure. Use the one that exists.
69
69
 
@@ -80,6 +80,7 @@ Some files in this workspace are **read-only instructions** and must not be modi
80
80
  | Source code (read-only) | `../` (everything outside `.codecarto/`) | Read only. Analyze but never modify. |
81
81
  | Synthesis input (user-maintained) | `inputs/vision.md` | The user fills this before the synthesis pipeline starts. Implementing phases read it but do not rewrite the raw brief. |
82
82
  | Workflow state (framework-owned) | `workflow/status.yaml` | Read to understand progress. Implementing sessions never edit it directly; completion applies a validated phase handoff under a lock. |
83
+ | Scaffold version stamp (framework-owned) | `workflow/scaffold-version.yaml` | Written at release, copied by init. The framework compares it to its own version to warn about stale scaffolds. Never edit. |
83
84
  | Findings (read-write) | `findings/<phase>/<primary-output>.md`, secondary output files | Create and update during phases. |
84
85
  | Phase handoff (read-write) | `scratch/handoffs/<phase>.yaml` | Implementing sessions propose state changes and closeout content here. The framework validates and applies them. |
85
86
  | Closeouts (framework-owned) | `closeouts/<date>-<phase-or-module>.md`, `THREAD_LOG.md` | Completion writes or updates one canonical closeout and one idempotent index entry. |
@@ -9,7 +9,7 @@ All CodeCartographer files are inside `.codecarto/`. Paths below are relative to
9
9
  Read these in order before doing work:
10
10
 
11
11
  1. `GUIDE.md` - the LLM entry point and session guide
12
- 2. `workflow/status.yaml` - the single source of truth for project progress
12
+ 2. `workflow/status.yaml` - current progress and routed items (framework-owned: read it, never edit it)
13
13
  3. `CONVENTIONS.md` if present - cross-cutting patterns this project follows
14
14
  4. The current phase's existing output file, if present
15
15
  5. The current phase's `SKILL.md`
@@ -20,18 +20,17 @@ Where to store results:
20
20
 
21
21
  - Durable findings: `findings/<phase>/`
22
22
  - Rough notes: `scratch/`
23
- - Workflow status: `workflow/status.yaml` (only status file to update)
24
- - Per-session closeouts: `closeouts/<YYYY-MM-DD>-<phase-or-module>.md` (use `templates/closeout-template.md`)
25
- - Cross-session log: append a one-line index entry to `THREAD_LOG.md` pointing at your closeout file
23
+ - State changes (owner notes, open questions, carry-forward routings): `scratch/handoffs/<phase>.yaml` — completion applies it to `workflow/status.yaml`; never edit `status.yaml` directly
24
+ - Closeouts and `THREAD_LOG.md`: framework-owned; supply `closeout_summary` and optional `closeout_content` in the handoff
26
25
 
27
26
  After completing work:
28
27
 
29
28
  1. Run validation per `workflow/VALIDATE.md`. Append the validation block to the output.
30
- 2. Update `workflow/status.yaml`: mark the phase `complete`, advance `current_phase` to the next pending phase (or `complete` if all phases are done), and update both `open_questions` (genuinely unknown) and `carry_forward` (deferred to a specific later phase) for the phase. See GUIDE.md "Open Questions vs Carry-Forward" for the entry shape.
31
- 3. Record 2-3 key observations in `owner_notes` for the completed phase.
32
- 4. Write a per-session closeout file at `closeouts/<YYYY-MM-DD>-<phase-or-module>.md`. Append a one-line entry to `THREAD_LOG.md` pointing at it (de-dup discipline: scan the bottom 5 entries first to avoid double-appending).
33
- 5. Store the durable output in the declared `findings/` path.
34
- 6. If the session made cross-cutting decisions or proposed conventions, fill in the closeout's "Decisions Beyond Prompt" and "Proposed Conventions" sections. The orchestrator promotes them to `DECISIONS.md` and `CONVENTIONS.md` respectively.
29
+ 2. Write `scratch/handoffs/<phase>.yaml` (see `templates/phase-handoff.yaml`): `schema_version: 1`, the exact `phase_id`, arrays for `owner_notes`, `open_questions`, `carry_forward`, `carry_forward_closures`, `open_question_closures`, `post_pipeline`, and `decisions`, plus `closeout_summary` and optional multiline `closeout_content`. See GUIDE.md "Open Questions vs Carry-Forward" for the entry shape.
30
+ 3. Record 2-3 key observations in the handoff's `owner_notes`.
31
+ 4. Store the durable output in the declared `findings/` path.
32
+ 5. Run completion. The framework verifies the primary output and validation, applies the handoff to `workflow/status.yaml`, and writes the closeout and `THREAD_LOG.md` entry.
33
+ 6. If the session made cross-cutting decisions or proposed conventions, record them in the handoff's `decisions` array and the closeout content's "Decisions Beyond Prompt" and "Proposed Conventions" sections. The orchestrator promotes them to `DECISIONS.md` and `CONVENTIONS.md` respectively.
35
34
 
36
35
  ## File-System Sync Warning
37
36
 
@@ -44,4 +43,4 @@ To stay safe:
44
43
  - **Never chain Edit and bash heredocs against the same file in a single session** — pick one writer and stick with it.
45
44
  - Keep test code in a separate file from production code so a sync gap on one doesn't poison the other.
46
45
 
47
- This is not a CodeCartographer issue per se, but the framework's reliance on append-to-narrative-files (`THREAD_LOG.md`, `status.yaml`, large primary outputs) magnifies the cost — every truncated write is a potential silent data loss in a load-bearing place.
46
+ This is not a CodeCartographer issue per se, but the framework's reliance on large narrative files (findings reports, primary outputs) magnifies the cost — every truncated write is a potential silent data loss in a load-bearing place.
@@ -57,4 +57,4 @@ If the semantic phase later finds related defects, those go in its own report
57
57
  - Auth, input validation, secrets, and trust boundary defects → pass 4 in `defect-scan-semantic`.
58
58
  - API/spec/state-machine drift → pass 5 in `defect-scan-semantic`.
59
59
 
60
- If you spot something semantic during this phase, record it as a `carry_forward` entry in `workflow/status.yaml` with `target_phase: defect-scan-semantic` so the later phase picks it up.
60
+ If you spot something semantic during this phase, record it as a `carry_forward` entry in your phase handoff at `scratch/handoffs/defect-scan-mechanical.yaml` with `target_phase: defect-scan-semantic`. Completion applies it to `workflow/status.yaml`, and the semantic phase receives it as a routed item in its prompt. Listing it only in your report's routing table does not route it.
@@ -86,7 +86,7 @@ Per the standard closeout ritual:
86
86
 
87
87
  - Append a one-line entry to `THREAD_LOG.md` pointing at the closeout file.
88
88
  - Write `closeouts/<YYYY-MM-DD>-spec-deltas.md` using `templates/closeout-template.md`.
89
- - Update `status.yaml` only if a delta closed an `open_questions` or `carry_forward` entry do NOT change `current_phase` (stays `complete`).
89
+ - If a delta closed an `open_questions` or `carry_forward` entry, record that in the closeout. Do not edit `status.yaml` phase state is framework-owned, and post-pipeline work has no completion step that applies handoffs, so a hand-edit here is unreviewed and can be overwritten by a later phase completion.
90
90
  - Append numbered entries to `DECISIONS.md` for any decisions made during triage that weren't already in the deltas (e.g., "rejected Δ7 because the spec already covered the case at §X").
91
91
 
92
92
  ## What to avoid
@@ -126,7 +126,8 @@
126
126
 
127
127
  <!-- Items deferred to a specific later phase whose rubric is the right place to close them.
128
128
  Each entry: { id, kind: defer-to-phase, target_phase, description, deferred_reason }.
129
- Mirror these into workflow/status.yaml under this phase's carry_forward list. -->
129
+ Mirror these into your phase handoff at scratch/handoffs/<phase>.yaml under carry_forward;
130
+ completion applies them to workflow/status.yaml. Never write that file yourself. -->
130
131
 
131
132
  | ID | Target Phase | Description | Deferred Reason |
132
133
  |---|---|---|---|
@@ -114,7 +114,8 @@
114
114
  <!-- Items deferred to a specific later phase whose rubric is the right place to close them.
115
115
  Common targets from contracts: protocols (wire-format details), porting (synthesis-level resolution).
116
116
  Each entry: { id, kind: defer-to-phase, target_phase, description, deferred_reason }.
117
- Mirror these into workflow/status.yaml under this phase's carry_forward list. -->
117
+ Mirror these into your phase handoff at scratch/handoffs/<phase>.yaml under carry_forward;
118
+ completion applies them to workflow/status.yaml. Never write that file yourself. -->
118
119
 
119
120
  | ID | Target Phase | Description | Deferred Reason |
120
121
  |---|---|---|---|
@@ -58,7 +58,8 @@
58
58
  ## Open Questions Left Behind
59
59
 
60
60
  <!-- Items that are still genuinely unknown after this session.
61
- Mirror these into workflow/status.yaml under the relevant phase's open_questions. -->
61
+ Mirror these into your phase handoff under open_questions; completion applies them
62
+ to workflow/status.yaml. This table documents them — it does not record them. -->
62
63
 
63
64
  | ID | Kind | Description | Deferred Reason |
64
65
  |---|---|---|---|
@@ -67,7 +68,9 @@
67
68
  ## Carry-Forward Routed
68
69
 
69
70
  <!-- Items deferred to a specific later phase.
70
- Mirror these into workflow/status.yaml under the relevant phase's carry_forward. -->
71
+ Mirror these into your phase handoff under carry_forward, each with a target_phase;
72
+ completion applies them to workflow/status.yaml. This table documents them — it does
73
+ not route them. -->
71
74
 
72
75
  | ID | Target Phase | Description | Deferred Reason |
73
76
  |---|---|---|---|
@@ -79,8 +79,10 @@
79
79
  ### Routed To Semantic Phase
80
80
 
81
81
  <!-- Items spotted during this phase that are actually semantic in nature
82
- (concurrency, security, contract drift). Mirror these into status.yaml as
83
- carry_forward entries with target_phase: defect-scan-semantic. -->
82
+ (concurrency, security, contract drift). Mirror these into your phase handoff at
83
+ scratch/handoffs/defect-scan-mechanical.yaml as carry_forward entries with
84
+ target_phase: defect-scan-semantic; completion applies them to workflow/status.yaml.
85
+ The table below documents the routing — it does not perform it. -->
84
86
 
85
87
  | ID | Description | Why Routed |
86
88
  |----|-------------|-----------|
@@ -109,7 +109,8 @@
109
109
  <!-- Items deferred to a specific later phase whose rubric is the right place to close them.
110
110
  Common target from protocols: porting (synthesis-level resolution).
111
111
  Each entry: { id, kind: defer-to-phase, target_phase, description, deferred_reason }.
112
- Mirror these into workflow/status.yaml under this phase's carry_forward list. -->
112
+ Mirror these into your phase handoff at scratch/handoffs/<phase>.yaml under carry_forward;
113
+ completion applies them to workflow/status.yaml. Never write that file yourself. -->
113
114
 
114
115
  | ID | Target Phase | Description | Deferred Reason |
115
116
  |---|---|---|---|
@@ -140,7 +140,8 @@
140
140
  <!-- Items deferred to a specific later phase whose rubric is the right place to close them.
141
141
  Common target from porting: reimplementation-spec (rule-pinning, opinionated decisions).
142
142
  Each entry: { id, kind: defer-to-phase, target_phase, description, deferred_reason }.
143
- Mirror these into workflow/status.yaml under this phase's carry_forward list. -->
143
+ Mirror these into your phase handoff at scratch/handoffs/<phase>.yaml under carry_forward;
144
+ completion applies them to workflow/status.yaml. Never write that file yourself. -->
144
145
 
145
146
  | ID | Target Phase | Description | Deferred Reason |
146
147
  |---|---|---|---|
@@ -85,7 +85,9 @@
85
85
 
86
86
  ### Carry-Forward Closure
87
87
 
88
- <!-- carry_forward entries in status.yaml that targeted defect-scan-semantic and were closed here. -->
88
+ <!-- carry_forward entries routed to defect-scan-semantic (delivered in this phase's prompt)
89
+ that were closed here. Address each in the findings above, then list its id under
90
+ carry_forward_closures in your phase handoff so completion removes the entry. -->
89
91
 
90
92
  | ID | Source Phase | Closed Because |
91
93
  |----|--------------|---------------|
@@ -1,14 +1,13 @@
1
- # THREAD_LOG.md Entry Template (legacy / one-line index form)
1
+ # THREAD_LOG.md Entry Format (framework-owned)
2
2
 
3
- This template is the **one-line index entry** appended to `THREAD_LOG.md` per session.
4
- The full body of each session lives in a separate closeout file under `closeouts/`.
3
+ `THREAD_LOG.md` is written by completion, not by sessions. Completion appends one index entry per completed phase, built from the `closeout_summary` in your phase handoff and the canonical closeout filename, and skips the append when an entry already links that closeout — so retries never duplicate.
5
4
 
6
- For the closeout file itself, use `templates/closeout-template.md`.
5
+ Your input to this file is the handoff's `closeout_summary`. The full body of each session lives in a separate closeout file under `closeouts/`; for that file, use `templates/closeout-template.md`.
7
6
 
8
- ## Entry format
7
+ ## Entry format produced
9
8
 
10
9
  ```
11
- - YYYY-MM-DD — <phase-or-module> — <one-line-summary> — [closeout](closeouts/YYYY-MM-DD-phase-or-module.md)
10
+ - YYYY-MM-DD — <phase-or-module> — <closeout_summary> — [closeout](closeouts/YYYY-MM-DD-phase-or-module.md)
12
11
  ```
13
12
 
14
13
  ## Example
@@ -17,11 +16,10 @@ For the closeout file itself, use `templates/closeout-template.md`.
17
16
  - 2026-05-02 — architecture — mapped 14 packages across 3 layers; 2 carry-forward items routed to defect-scan — [closeout](closeouts/2026-05-02-architecture.md)
18
17
  ```
19
18
 
20
- ## Rules
19
+ ## Writing a good closeout_summary
21
20
 
22
- - One line per session, no exceptions.
23
- - Date in ISO format (YYYY-MM-DD).
24
- - `<phase-or-module>` matches the closeout filename's slug.
25
- - Summary is one clause, no more than ~20 words.
26
- - Always link to the closeout file. If there is no closeout file, the entry doesn't belong here.
27
- - Before appending, scan the bottom 5 entries to avoid duplicating a prior session's entry.
21
+ - One clause, no more than ~20 words — it has to read well inside the one-line entry above.
22
+ - Name what the phase established and what it routed onward, as in the example.
23
+ - Omitting it is allowed; completion falls back to `Validation: <overall>`, which carries no information about the work.
24
+
25
+ Everything else in the entry — the date, the phase slug, the closeout link, and de-duplication across retries — is supplied by the framework. Do not append to `THREAD_LOG.md` yourself; a hand-written entry will sit alongside the canonical one rather than replacing it.
@@ -13,8 +13,8 @@ Run this check after completing a phase's primary output, before marking the pha
13
13
  4. Append a validation block to the end of the primary output file (see format below).
14
14
  5. If any criterion is FAIL, do not mark the phase as complete. Fix the output first.
15
15
  6. If any criterion is PARTIAL, route the gap appropriately:
16
- - If the gap is **still genuinely unknown** (needs a runtime test, maintainer decision, or spec ruling that no later phase will close), record it under `open_questions` for the phase in `workflow/status.yaml`.
17
- - If the gap is **deferred to a specific later phase** in this pipeline (the next phase's rubric is the right place to close it), record it under `carry_forward` with an explicit `target_phase` field. See GUIDE.md "Open Questions vs Carry-Forward" for the entry shape.
16
+ - If the gap is **still genuinely unknown** (needs a runtime test, maintainer decision, or spec ruling that no later phase will close), record it under `open_questions` in your phase handoff at `scratch/handoffs/<phase>.yaml`.
17
+ - If the gap is **deferred to a specific later phase** in this pipeline (the next phase's rubric is the right place to close it), record it under `carry_forward` in the same handoff with an explicit `target_phase` field. See GUIDE.md "Open Questions vs Carry-Forward" for the entry shape.
18
18
  - You may still mark the phase complete if the gaps are documented under one of the two lists and are non-blocking for downstream phases.
19
19
 
20
20
  ## Validation Block Format
@@ -56,20 +56,23 @@ Below is what a real PASS WITH GAPS block looks like — useful when a phase fin
56
56
  **Overall:** PASS WITH GAPS
57
57
  ```
58
58
 
59
- The `arch-CF2` entry then appears in `workflow/status.yaml` under the architecture phase's `carry_forward`:
59
+ You record the `arch-CF2` entry in your phase handoff at `scratch/handoffs/architecture.yaml`:
60
60
 
61
61
  ```yaml
62
- phases:
63
- architecture:
64
- carry_forward:
65
- - id: arch-CF2
66
- kind: defer-to-phase
67
- target_phase: protocols
68
- description: MCP server endpoints and websocket subscription channel listed by name only; schemas not extracted.
69
- deferred_reason: Wire-format extraction is the protocols phase's rubric; deferring avoids guessing at shapes here.
62
+ schema_version: 1
63
+ phase_id: architecture
64
+ carry_forward:
65
+ - id: arch-CF2
66
+ kind: defer-to-phase
67
+ target_phase: protocols
68
+ description: MCP server endpoints and websocket subscription channel listed by name only; schemas not extracted.
69
+ deferred_reason: Wire-format extraction is the protocols phase's rubric; deferring avoids guessing at shapes here.
70
+ closeout_summary: Architecture mapped; wire formats deferred to protocols.
70
71
  ```
71
72
 
72
- The protocols phase, on session start, scans `carry_forward` entries with `target_phase: protocols`, picks `arch-CF2` up, and either resolves it (deletes the entry once the schemas are pinned) or re-defers to a later phase.
73
+ Completion validates the handoff and applies the entry to `workflow/status.yaml` under the architecture phase's `carry_forward`. Never write that file yourself see GUIDE.md "Trust Boundaries."
74
+
75
+ The protocols phase then receives `arch-CF2` in its phase prompt as a routed item. It either resolves it (addressing it in the phase output and listing the id under `carry_forward_closures` in its own handoff, so completion removes the entry atomically) or re-defers it by writing a fresh `carry_forward` entry naming a later `target_phase`.
73
76
 
74
77
  ## Rules
75
78
 
@@ -77,5 +80,5 @@ The protocols phase, on session start, scans `carry_forward` entries with `targe
77
80
  - Do not inflate results. A criterion you are uncertain about is PARTIAL, not PASS.
78
81
  - If the output file already has a validation block from a prior session, replace it with a fresh one.
79
82
  - Validation checks the output against the pipeline's criteria only. It does not re-evaluate the source code.
80
- - For automated agents: a phase with any FAIL result must not have its status set to `complete` in status.yaml.
81
- - A PARTIAL row's `Evidence` cell must name what is missing and (if applicable) which `open_questions` or `carry_forward` entry tracks the gap. "Incomplete" alone is not honest enough.
83
+ - For automated agents: a phase with any FAIL result must not be completed. Completion refuses FAIL and MISSING validations outright.
84
+ - A PARTIAL row's `Evidence` cell must name what is missing and (if applicable) which `open_questions` or `carry_forward` entry tracks the gap. "Incomplete" alone is not honest enough. Record that entry in the phase handoff — an evidence cell that only *describes* the routing does not perform it.
@@ -35,5 +35,5 @@ phases:
35
35
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
36
36
  handoff_requirements:
37
37
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
38
- - Update workflow/status.yaml.
39
- - Append a summary entry to THREAD_LOG.md.
38
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
39
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -36,8 +36,8 @@ phases:
36
36
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
37
37
  handoff_requirements:
38
38
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
39
- - Update workflow/status.yaml.
40
- - Append a summary entry to THREAD_LOG.md.
39
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
40
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
41
41
  - id: defect-scan
42
42
  purpose: Run a multi-pass defect scan to identify bugs, security issues, and code quality problems for maintenance prioritization.
43
43
  skill_path: findings/defect-scan/SKILL.md
@@ -59,5 +59,5 @@ phases:
59
59
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
60
60
  handoff_requirements:
61
61
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
62
- - Update workflow/status.yaml.
63
- - Append a summary entry to THREAD_LOG.md.
62
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
63
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -40,8 +40,8 @@ phases:
40
40
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
41
41
  handoff_requirements:
42
42
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
43
- - Update workflow/status.yaml.
44
- - Append a summary entry to THREAD_LOG.md.
43
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
44
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
45
45
  - id: defect-scan
46
46
  purpose: Run a multi-pass defect scan to identify legacy bugs, security issues, and code quality problems before porting.
47
47
  skill_path: findings/defect-scan/SKILL.md
@@ -63,8 +63,8 @@ phases:
63
63
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
64
64
  handoff_requirements:
65
65
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
66
- - Update workflow/status.yaml.
67
- - Append a summary entry to THREAD_LOG.md.
66
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
67
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
68
68
  - id: contracts
69
69
  purpose: Recover user-visible behavior, defaults, side effects, error behavior, and black-box acceptance checks.
70
70
  skill_path: findings/contracts/SKILL.md
@@ -95,8 +95,8 @@ phases:
95
95
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
96
96
  handoff_requirements:
97
97
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
98
- - Update workflow/status.yaml.
99
- - Append a summary entry to THREAD_LOG.md.
98
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
99
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
100
100
  - id: protocols
101
101
  purpose: Capture event catalogs, state machines, persistence formats, and compatibility hazards.
102
102
  skill_path: findings/protocols/SKILL.md
@@ -126,8 +126,8 @@ phases:
126
126
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
127
127
  handoff_requirements:
128
128
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
129
- - Update workflow/status.yaml.
130
- - Append a summary entry to THREAD_LOG.md.
129
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
130
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
131
131
  - id: porting
132
132
  purpose: Synthesize architecture, contracts, protocols, and defect findings into a reverse-engineering bundle oriented toward porting.
133
133
  skill_path: findings/porting/SKILL.md
@@ -165,8 +165,8 @@ phases:
165
165
  - The Source Index makes the bundle a self-contained compression boundary and identifies targeted deep-read triggers.
166
166
  handoff_requirements:
167
167
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
168
- - Update workflow/status.yaml.
169
- - Append a summary entry to THREAD_LOG.md.
168
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
169
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
170
170
  - id: reimplementation-spec
171
171
  purpose: Produce the final language-agnostic reimplementation plan and acceptance spec.
172
172
  skill_path: findings/reimplementation-spec/SKILL.md
@@ -189,5 +189,5 @@ phases:
189
189
  - Lower-level findings are deep-read only when the porting bundle identifies a gap, conflict, missing acceptance detail, or defect rationale.
190
190
  handoff_requirements:
191
191
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
192
- - Update workflow/status.yaml.
193
- - Append a summary entry to THREAD_LOG.md.
192
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
193
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -41,8 +41,8 @@ phases:
41
41
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
42
42
  handoff_requirements:
43
43
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
44
- - Update workflow/status.yaml.
45
- - Append a summary entry to THREAD_LOG.md.
44
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
45
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
46
46
  - id: defect-scan-mechanical
47
47
  purpose: Early, context-light defect pass covering logic, error handling, and configuration hazards. Runs before contracts so mechanical bugs are surfaced for the contracts and porting phases to reference.
48
48
  skill_path: findings/defect-scan-mechanical/SKILL.md
@@ -60,13 +60,13 @@ phases:
60
60
  - Each finding has location, severity, evidence level, and recommended action.
61
61
  - Findings are organized by pass and sorted by severity.
62
62
  - Summary tables are complete and counts match the detailed findings.
63
- - Items spotted that are actually semantic in nature are routed to defect-scan-semantic via carry_forward in workflow/status.yaml.
63
+ - Items spotted that are actually semantic in nature are routed onward via a carry_forward entry in the phase handoff targeting defect-scan-semantic.
64
64
  - Findings are marked with evidence levels.
65
65
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
66
66
  handoff_requirements:
67
67
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
68
- - Update workflow/status.yaml.
69
- - Append a summary entry to THREAD_LOG.md.
68
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
69
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
70
70
  - id: contracts
71
71
  purpose: Recover user-visible behavior, defaults, side effects, error behavior, and black-box acceptance checks.
72
72
  skill_path: findings/contracts/SKILL.md
@@ -98,8 +98,8 @@ phases:
98
98
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
99
99
  handoff_requirements:
100
100
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
101
- - Update workflow/status.yaml.
102
- - Append a summary entry to THREAD_LOG.md.
101
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
102
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
103
103
  - id: protocols
104
104
  purpose: Capture event catalogs, state machines, persistence formats, and compatibility hazards.
105
105
  skill_path: findings/protocols/SKILL.md
@@ -130,8 +130,8 @@ phases:
130
130
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
131
131
  handoff_requirements:
132
132
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
133
- - Update workflow/status.yaml.
134
- - Append a summary entry to THREAD_LOG.md.
133
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
134
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
135
135
  - id: defect-scan-semantic
136
136
  purpose: Deep, context-rich defect pass covering concurrency, security, and API contract violations. Runs after protocols so contracts/protocols context is available, and before porting so the porting phase can synthesize all defects with full understanding of the system.
137
137
  skill_path: findings/defect-scan-semantic/SKILL.md
@@ -160,8 +160,8 @@ phases:
160
160
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
161
161
  handoff_requirements:
162
162
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
163
- - Update workflow/status.yaml.
164
- - Append a summary entry to THREAD_LOG.md.
163
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
164
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
165
165
  - id: porting
166
166
  purpose: Synthesize architecture, contracts, protocols, and both defect passes into a reverse-engineering bundle oriented toward porting. Fill the Defect Synthesis section by consolidating mechanical-defects.md and semantic-defects.md into a single porting-oriented view.
167
167
  skill_path: findings/porting/SKILL.md
@@ -202,8 +202,8 @@ phases:
202
202
  - The Source Index makes the bundle a self-contained compression boundary and identifies targeted deep-read triggers.
203
203
  handoff_requirements:
204
204
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
205
- - Update workflow/status.yaml.
206
- - Append a summary entry to THREAD_LOG.md.
205
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
206
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
207
207
  - id: reimplementation-spec
208
208
  purpose: Produce the final language-agnostic reimplementation plan and acceptance spec. The Strategic Alignment Hook in GUIDE.md decides between the default and opinionated template variants.
209
209
  skill_path: findings/reimplementation-spec/SKILL.md
@@ -227,5 +227,5 @@ phases:
227
227
  - Lower-level findings are deep-read only when the porting bundle identifies a gap, conflict, missing acceptance detail, or defect rationale.
228
228
  handoff_requirements:
229
229
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
230
- - Update workflow/status.yaml.
231
- - Append a summary entry to THREAD_LOG.md.
230
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
231
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -37,8 +37,8 @@ phases:
37
37
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
38
38
  handoff_requirements:
39
39
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
40
- - Update workflow/status.yaml.
41
- - Append a summary entry to THREAD_LOG.md.
40
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
41
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
42
42
  - id: contracts
43
43
  purpose: Recover user-visible behavior, defaults, side effects, error behavior, and black-box acceptance checks.
44
44
  skill_path: findings/contracts/SKILL.md
@@ -68,8 +68,8 @@ phases:
68
68
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
69
69
  handoff_requirements:
70
70
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
71
- - Update workflow/status.yaml.
72
- - Append a summary entry to THREAD_LOG.md.
71
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
72
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
73
73
  - id: protocols
74
74
  purpose: Capture event catalogs, state machines, persistence formats, and compatibility hazards.
75
75
  skill_path: findings/protocols/SKILL.md
@@ -99,5 +99,5 @@ phases:
99
99
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
100
100
  handoff_requirements:
101
101
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
102
- - Update workflow/status.yaml.
103
- - Append a summary entry to THREAD_LOG.md.
102
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
103
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -39,8 +39,8 @@ phases:
39
39
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
40
40
  handoff_requirements:
41
41
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
42
- - Update workflow/status.yaml.
43
- - Append a summary entry to THREAD_LOG.md.
42
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
43
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
44
44
  - id: contracts
45
45
  purpose: Recover user-visible behavior, defaults, side effects, error behavior, and black-box acceptance checks.
46
46
  skill_path: findings/contracts/SKILL.md
@@ -71,8 +71,8 @@ phases:
71
71
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
72
72
  handoff_requirements:
73
73
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
74
- - Update workflow/status.yaml.
75
- - Append a summary entry to THREAD_LOG.md.
74
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
75
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
76
76
  - id: protocols
77
77
  purpose: Capture event catalogs, state machines, persistence formats, and compatibility hazards.
78
78
  skill_path: findings/protocols/SKILL.md
@@ -102,8 +102,8 @@ phases:
102
102
  - Coverage and limits name inspected scope, skipped scope, evidence basis, and blind spots.
103
103
  handoff_requirements:
104
104
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
105
- - Update workflow/status.yaml.
106
- - Append a summary entry to THREAD_LOG.md.
105
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
106
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
107
107
  - id: porting
108
108
  purpose: Synthesize architecture, contracts, and protocols into a reverse-engineering bundle oriented toward porting.
109
109
  skill_path: findings/porting/SKILL.md
@@ -139,8 +139,8 @@ phases:
139
139
  - The Source Index makes the bundle a self-contained compression boundary and identifies targeted deep-read triggers.
140
140
  handoff_requirements:
141
141
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
142
- - Update workflow/status.yaml.
143
- - Append a summary entry to THREAD_LOG.md.
142
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
143
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
144
144
  - id: reimplementation-spec
145
145
  purpose: Produce the final language-agnostic reimplementation plan and acceptance spec.
146
146
  skill_path: findings/reimplementation-spec/SKILL.md
@@ -163,5 +163,5 @@ phases:
163
163
  - Lower-level findings are deep-read only when the porting bundle identifies a gap, conflict, missing acceptance detail, or defect rationale.
164
164
  handoff_requirements:
165
165
  - Run validation per workflow/VALIDATE.md. Append validation block to primary output.
166
- - Update workflow/status.yaml.
167
- - Append a summary entry to THREAD_LOG.md.
166
+ - Write the phase handoff to scratch/handoffs/<phase>.yaml with owner notes, open questions, and carry-forward routings; completion applies it to workflow/status.yaml.
167
+ - Provide closeout_summary and optional closeout_content in the handoff; completion writes the closeout and THREAD_LOG.md entry.
@@ -0,0 +1,6 @@
1
+ # Version of the CodeCartographer release that shipped this .codecarto/
2
+ # scaffold. The framework compares it against its own version to warn when a
3
+ # workspace's framework-owned files (GUIDE.md, templates/, workflow/ pipelines
4
+ # and VALIDATE.md) predate the running release. Written at release time and
5
+ # copied verbatim by init — never edit by hand.
6
+ scaffold_version: 0.13.0
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![npm version](https://img.shields.io/npm/v/codecartographer-pi.svg)](https://www.npmjs.com/package/codecartographer-pi)
10
10
  [![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](package.json)
11
11
 
12
- > **Evidence-backed software cartography for coding agents.** Turn an unfamiliar repository into validated architecture, contracts, defects, and a reimplementation specification—then combine explicitly confirmed specifications with a product vision to produce a provenance-backed implementation plan.
12
+ > **Understand an unfamiliar codebase with an AI agent — and get a validated spec you can rebuild from.** CodeCartographer turns a repository into layered architecture, behavioral contracts, defect findings, and a language-agnostic reimplementation spec, with each phase validated before the next one runs. Works with Pi, Claude Code, Cursor, Codex, or any MCP-capable agent.
13
13
 
14
14
  ```text
15
15
  ● CodeCartographer
@@ -19,6 +19,24 @@
19
19
  ⎿ extracting behavioral contracts from server/index.ts…
20
20
  ```
21
21
 
22
+ <p align="center">
23
+ <img src="docs/demo-dashboard-hero.png" alt="CodeCartographer dashboard — a mid-run pipeline with architecture and defect-scan phases complete, contracts in progress, per-phase token and tool-use telemetry.">
24
+ </p>
25
+
26
+ ---
27
+
28
+ ## Why CodeCartographer
29
+
30
+ Asking an LLM to "analyze this repo" loses context halfway through, hallucinates findings, and leaves no artifact the next session can pick up. CodeCartographer fixes three things:
31
+
32
+ 1. **The filesystem is the memory, not the conversation.** Each phase writes a smaller, templated, evidence-tagged artifact to `.codecarto/findings/`. Later phases re-read the specific upstream files they need. A new session — or a context compaction — picks up from `status.yaml` without losing progress.
33
+
34
+ 2. **Every phase is validated before the pipeline advances.** Completion criteria are real: a `FAIL` output stops the run. You can't accidentally build a reimplementation spec on top of hallucinated architecture.
35
+
36
+ 3. **The output is a spec, not a chat log.** The final `reimplementation-spec.md` is language-agnostic, module-inventoried, and carries acceptance scenarios plus known unknowns. Hand it to another agent to rebuild from.
37
+
38
+ Every finding is tagged with an evidence level: `observed fact`, `strong inference`, `portability hazard`, or `open question`.
39
+
22
40
  ---
23
41
 
24
42
  ## At a glance
@@ -36,6 +54,8 @@
36
54
 
37
55
  Publish completed reimplementation specs from Pi or MCP, then run the `synthesis` pipeline to turn a product vision and explicitly confirmed library entries into a conflict-aware `project-plan.md` with a decision-level provenance ledger.
38
56
 
57
+ > **If CodeCartographer saves you a day of codebase archaeology, star the repo** — it helps the next person find it.
58
+
39
59
  OpenAI Build Week reviewers: see the [new-vs-existing scope and one-command demo](docs/build-week-2026.md).
40
60
 
41
61
  ---
@@ -70,6 +90,8 @@ pi -e /absolute/path/to/CodeCartographer/extensions/codecarto/index.ts
70
90
 
71
91
  Use this when your coding agent isn't Pi — Claude Code, Codex, opencode, Cursor, Claude Desktop, or anything else that speaks MCP. The host drives the conversation and runs the LLM; CodeCartographer provides phase prompts, validation, and experimental library publish/list/reindex operations.
72
92
 
93
+ > **30-second setup for Claude Code, Cursor, Codex, and Claude Desktop: see the [MCP quickstart](docs/mcp-quickstart.md).**
94
+
73
95
  ```bash
74
96
  npm install --global codecartographer-pi
75
97
  ```
@@ -54,6 +54,19 @@ export async function completeValidatedPhase(cwd, validation, sourceLabel) {
54
54
  if (handoff && handoff.phase_id !== validation.phaseId) {
55
55
  throw new Error(`Invalid handoff: phase_id ${handoff.phase_id} does not match ${validation.phaseId}`);
56
56
  }
57
+ // A phase that declares handoff_requirements must not complete without its
58
+ // handoff: silently proceeding writes empty carry_forward/open_questions and
59
+ // severs the cross-phase routing channel (issue #84). Phases without the
60
+ // declaration keep the lenient path for custom pipelines.
61
+ if (!handoff) {
62
+ const declaringPhase = resolvePhase(initialState, validation.phaseId);
63
+ if (declaringPhase?.handoff_requirements?.length) {
64
+ throw new Error(`Phase ${validation.phaseId} declares handoff_requirements, but no phase handoff exists at .codecarto/scratch/handoffs/${validation.phaseId}.yaml. `
65
+ + `Write the handoff first (see GUIDE.md and templates/phase-handoff.yaml): schema_version: 1, the exact phase_id, `
66
+ + `arrays for owner_notes, open_questions, carry_forward, carry_forward_closures, open_question_closures, post_pipeline, and decisions (omitted arrays default to empty), `
67
+ + `plus closeout_summary and optional closeout_content. Then re-run completion.`);
68
+ }
69
+ }
57
70
  if (handoff) {
58
71
  const activePhases = new Set(initialState.pipeline.phase_order);
59
72
  const sourceIndex = initialState.pipeline.phase_order.indexOf(validation.phaseId);
@@ -4,6 +4,7 @@
4
4
  import { readdir } from "node:fs/promises";
5
5
  import { join } from "node:path";
6
6
  import { pathExists } from "./utils.js";
7
+ import { describeScaffoldStaleness } from "./workspace.js";
7
8
  import { runPhasePreflight } from "./synthesis.js";
8
9
  export function describeEntry(entry) {
9
10
  const parts = [];
@@ -30,6 +31,7 @@ export function collectRoutedCarryForward(state, targetPhaseId) {
30
31
  export async function buildPhasePrompt(state, phase, forced, options = {}) {
31
32
  const preflight = options.preflight ?? await runPhasePreflight(state, phase);
32
33
  const synthesisWorkflow = state.pipeline.workflow_name === "evidence-backed-project-synthesis";
34
+ const handoffTemplateExists = await pathExists(join(state.workspaceDir, "templates", "phase-handoff.yaml"));
33
35
  const lines = [
34
36
  `Read .codecarto/GUIDE.md and continue the CodeCartographer workflow for the phase \`${phase.id}\`.`,
35
37
  synthesisWorkflow
@@ -39,8 +41,10 @@ export async function buildPhasePrompt(state, phase, forced, options = {}) {
39
41
  "Required reads before analysis:",
40
42
  "- .codecarto/GUIDE.md",
41
43
  "- .codecarto/workflow/status.yaml",
42
- "- .codecarto/templates/phase-handoff.yaml",
43
44
  ];
45
+ if (handoffTemplateExists) {
46
+ lines.push("- .codecarto/templates/phase-handoff.yaml");
47
+ }
44
48
  const primaryOutput = phase.primary_output ? `.codecarto/${phase.primary_output}` : undefined;
45
49
  if (primaryOutput) {
46
50
  lines.push(`- ${primaryOutput} if it already exists (continue instead of duplicating work)`);
@@ -69,6 +73,21 @@ export async function buildPhasePrompt(state, phase, forced, options = {}) {
69
73
  if (await pathExists(decisionsPath)) {
70
74
  lines.push("- .codecarto/DECISIONS.md (numbered project decisions; new entries are appended in your closeout)");
71
75
  }
76
+ // Stale-scaffold warnings (issue #85): a workspace copied from an old
77
+ // template can contradict the runtime contract. Both surfaces render this
78
+ // prompt, so warn here rather than per-host.
79
+ const scaffoldWarnings = [];
80
+ if (!handoffTemplateExists) {
81
+ scaffoldWarnings.push(`.codecarto/templates/phase-handoff.yaml is missing — this scaffold predates the v0.12.0 handoff contract. Completion still requires a phase handoff at .codecarto/scratch/handoffs/${phase.id}.yaml; refresh the framework-owned files (GUIDE.md, templates/, workflow/VALIDATE.md, workflow/pipeline*.yaml) from the current CodeCartographer template, and trust this prompt over the workspace GUIDE.md where they disagree.`);
82
+ }
83
+ const staleness = describeScaffoldStaleness(state);
84
+ if (staleness)
85
+ scaffoldWarnings.push(staleness);
86
+ if (scaffoldWarnings.length > 0) {
87
+ lines.push("");
88
+ for (const warning of scaffoldWarnings)
89
+ lines.push(`WARNING: ${warning}`);
90
+ }
72
91
  const routed = collectRoutedCarryForward(state, phase.id);
73
92
  if (routed.length > 0) {
74
93
  lines.push("", `Items routed to \`${phase.id}\` for closure (carry_forward from earlier phases):`);
@@ -67,6 +67,8 @@ export type WorkspaceState = {
67
67
  pipelinePath: string;
68
68
  status: NormalizedStatus;
69
69
  pipeline: PipelineFile;
70
+ /** From workflow/scaffold-version.yaml; undefined for scaffolds that predate the marker. */
71
+ scaffoldVersion?: string;
70
72
  };
71
73
  export type ValidationOverall = "PASS" | "PASS WITH GAPS" | "FAIL" | "MISSING";
72
74
  export type ValidationResult = {
@@ -2,6 +2,15 @@ import type { PhaseHandoff, WorkspaceState } from "./types.ts";
2
2
  export declare const packagedWorkspaceDir: string;
3
3
  export declare const PACKAGE_VERSION: string;
4
4
  export declare function getWorkspaceState(cwd: string): Promise<WorkspaceState | null>;
5
+ /**
6
+ * Human-readable staleness notice for the workspace's .codecarto/ scaffold,
7
+ * or null when the scaffold matches the running framework. A missing marker
8
+ * means the scaffold was copied from a release that predates it — those
9
+ * scaffolds may also predate the v0.12.0 handoff contract, whose GUIDE and
10
+ * pipelines instruct the exact opposite completion protocol. Warn, never
11
+ * fail: unversioned workspaces must keep working.
12
+ */
13
+ export declare function describeScaffoldStaleness(state: WorkspaceState): string | null;
5
14
  export declare function updateStatusAtomically(cwd: string, updater: (state: WorkspaceState) => Promise<{
6
15
  state: WorkspaceState;
7
16
  handoff?: PhaseHandoff;
@@ -76,6 +76,17 @@ export async function getWorkspaceState(cwd) {
76
76
  }
77
77
  const pipeline = await loadYamlFile(pipelinePath);
78
78
  const status = normalizeStatus(rawStatus, pipeline, pipelineRelativePath, cwd);
79
+ const scaffoldVersionPath = join(workspaceDir, "workflow", "scaffold-version.yaml");
80
+ let scaffoldVersion;
81
+ if (await pathExists(scaffoldVersionPath)) {
82
+ const marker = await loadYamlFile(scaffoldVersionPath);
83
+ if (typeof marker.scaffold_version === "string" && marker.scaffold_version.trim()) {
84
+ scaffoldVersion = marker.scaffold_version.trim();
85
+ }
86
+ else if (typeof marker.scaffold_version === "number") {
87
+ scaffoldVersion = String(marker.scaffold_version);
88
+ }
89
+ }
79
90
  return {
80
91
  cwd,
81
92
  workspaceDir,
@@ -83,8 +94,51 @@ export async function getWorkspaceState(cwd) {
83
94
  pipelinePath,
84
95
  pipeline,
85
96
  status,
97
+ ...(scaffoldVersion !== undefined && { scaffoldVersion }),
86
98
  };
87
99
  }
100
+ // Numeric x.y.z comparison; null when either side is not a plain dotted triple.
101
+ function compareDottedVersions(a, b) {
102
+ const parse = (version) => {
103
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
104
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
105
+ };
106
+ const left = parse(a);
107
+ const right = parse(b);
108
+ if (!left || !right)
109
+ return null;
110
+ for (let i = 0; i < 3; i++) {
111
+ if (left[i] !== right[i])
112
+ return left[i] < right[i] ? -1 : 1;
113
+ }
114
+ return 0;
115
+ }
116
+ /**
117
+ * Human-readable staleness notice for the workspace's .codecarto/ scaffold,
118
+ * or null when the scaffold matches the running framework. A missing marker
119
+ * means the scaffold was copied from a release that predates it — those
120
+ * scaffolds may also predate the v0.12.0 handoff contract, whose GUIDE and
121
+ * pipelines instruct the exact opposite completion protocol. Warn, never
122
+ * fail: unversioned workspaces must keep working.
123
+ */
124
+ export function describeScaffoldStaleness(state) {
125
+ const scaffold = state.scaffoldVersion;
126
+ if (!scaffold) {
127
+ return "This workspace's .codecarto/ scaffold has no workflow/scaffold-version.yaml marker (introduced after v0.12.11), so its framework-owned files (GUIDE.md, templates/, workflow/ pipelines and VALIDATE.md) may predate the v0.12.0 handoff contract. Refresh them from the packaged CodeCartographer template.";
128
+ }
129
+ const comparison = compareDottedVersions(scaffold, PACKAGE_VERSION);
130
+ if (comparison === 0)
131
+ return null;
132
+ if (comparison === null) {
133
+ return scaffold === PACKAGE_VERSION
134
+ ? null
135
+ : `This workspace's scaffold version (${scaffold}) does not match the running framework (${PACKAGE_VERSION}). Refresh the framework-owned files (GUIDE.md, templates/, workflow/) from the packaged template.`;
136
+ }
137
+ if (comparison < 0) {
138
+ return `This workspace's scaffold (v${scaffold}) is older than the running framework (v${PACKAGE_VERSION}). Refresh the framework-owned files (GUIDE.md, templates/, workflow/) from the packaged template to pick up pipeline and template fixes.`;
139
+ }
140
+ return `This workspace's scaffold (v${scaffold}) is newer than the running framework (v${PACKAGE_VERSION}). Upgrade CodeCartographer to at least v${scaffold}.`;
141
+ }
88
142
  export async function updateStatusAtomically(cwd, updater) {
89
143
  const workspaceDir = join(cwd, ".codecarto");
90
144
  const statusPath = join(workspaceDir, "workflow", "status.yaml");
@@ -8,7 +8,7 @@ import { narrateDashboard } from "./dashboard-narrator.js";
8
8
  import { writeDashboard } from "./dashboard-writer.js";
9
9
  import { parseNextFlags } from "./next-flags.js";
10
10
  import { phaseCompactionExtension } from "./phase-compaction.js";
11
- import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPathResolved, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, PhasePreflightError, PIPELINE_ALIASES, publishEntry, resolvePhase, resolvePipelineChoice, runPhasePreflight, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../../core/index.js";
11
+ import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPathResolved, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, PhasePreflightError, PIPELINE_ALIASES, publishEntry, resolvePhase, resolvePipelineChoice, runPhasePreflight, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../../core/index.js";
12
12
  import { initLibrary } from "../../core/library.js";
13
13
  import { resolveUserConfigPath } from "../../core/orchestrator-config.js";
14
14
  const STATUS_WIDGET_ID = "codecarto-widget";
@@ -71,6 +71,9 @@ function buildStatusLines(state, extraLines = []) {
71
71
  `Post-pipeline work: ${postPipelinePending} pending`,
72
72
  `Next: ${nextAction}`,
73
73
  ];
74
+ const scaffoldNotice = describeScaffoldStaleness(state);
75
+ if (scaffoldNotice)
76
+ lines.push(`Scaffold: ${scaffoldNotice}`);
74
77
  if (extraLines.length > 0) {
75
78
  lines.push("", ...extraLines);
76
79
  }
@@ -78,6 +78,11 @@ export declare function handleSkill(args: {
78
78
  }>;
79
79
  structuredContent?: Record<string, unknown>;
80
80
  }>;
81
+ export declare function readSpecArg(args: {
82
+ spec?: unknown;
83
+ spec_path?: unknown;
84
+ cwd?: unknown;
85
+ }, allowedRoots: string[]): Promise<string>;
81
86
  export declare function handlePublish(args: Record<string, unknown>): Promise<{
82
87
  content: Array<{
83
88
  type: "text";
@@ -15,7 +15,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
15
15
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
16
16
  import { cp, mkdir, readFile, rename, writeFile } from "node:fs/promises";
17
17
  import { basename, isAbsolute, join } from "node:path";
18
- import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listEntries, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, publishEntry, reindex as libraryReindex, resolvePhase, resolvePipelineChoice, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../core/index.js";
18
+ import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listEntries, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, publishEntry, reindex as libraryReindex, resolvePhase, resolvePipelineChoice, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../core/index.js";
19
19
  import { initLibrary } from "../core/library.js";
20
20
  import { loadUserConfig, resolveUserConfigPath } from "../core/orchestrator-config.js";
21
21
  import { writeDashboard } from "../extensions/codecarto/dashboard-writer.js";
@@ -118,7 +118,8 @@ export async function handleStatus(args) {
118
118
  const currentOpenQuestions = currentPhase === "complete" ? 0 : state.status.phases[currentPhase]?.open_questions.length ?? 0;
119
119
  const terminalOpenQuestions = Object.values(state.status.phases).reduce((sum, phase) => sum + (phase.open_questions?.length ?? 0), 0);
120
120
  const postPipelinePending = state.status.post_pipeline.filter((entry) => entry.status !== "resolved").length;
121
- const summary = [
121
+ const scaffoldNotice = describeScaffoldStaleness(state);
122
+ const summaryLines = [
122
123
  `Phase: ${currentPhase}`,
123
124
  `Pipeline state: ${currentPhase === "complete" ? "complete" : "in progress"}`,
124
125
  `Pipeline: ${getPipelineLabel(state.status.pipeline)} (${state.status.pipeline})`,
@@ -127,8 +128,12 @@ export async function handleStatus(args) {
127
128
  `Carry-forward (pipeline phases): ${totalCarryForward}`,
128
129
  `Post-pipeline work: ${postPipelinePending} pending`,
129
130
  `Next: ${state.status.next_actions[0] ?? (nextPhase ? `Begin ${nextPhase.id}` : "All phases complete.")}`,
130
- ].join("\n");
131
+ ];
132
+ if (scaffoldNotice)
133
+ summaryLines.push(`Scaffold: ${scaffoldNotice}`);
134
+ const summary = summaryLines.join("\n");
131
135
  return textResult(summary, {
136
+ ...(scaffoldNotice ? { scaffoldNotice } : {}),
132
137
  currentPhase,
133
138
  pipeline: state.status.pipeline,
134
139
  pipelineLabel: getPipelineLabel(state.status.pipeline),
@@ -325,25 +330,32 @@ function buildGenerationFromArg(model_metadata) {
325
330
  out.notes = m.notes;
326
331
  return out;
327
332
  }
328
- async function readSpecArg(args, allowedRoots = []) {
333
+ // `allowedRoots` is deliberately required and must be non-empty whenever
334
+ // spec_path is used. It previously defaulted to `[]`, which made containment
335
+ // opt-in: a caller that omitted it would read any absolute path the client
336
+ // asked for, silently reopening the arbitrary-file-read class of bug fixed in
337
+ // v0.12.11. Containment is now the default posture and an empty root set is a
338
+ // programming error rather than a bypass.
339
+ export async function readSpecArg(args, allowedRoots) {
329
340
  if (typeof args.spec === "string" && args.spec.length > 0)
330
341
  return args.spec;
331
342
  if (typeof args.spec_path === "string" && args.spec_path.length > 0) {
332
343
  if (!isAbsolute(args.spec_path)) {
333
344
  throw new McpError(ErrorCode.InvalidParams, `spec_path must be absolute, got: ${args.spec_path}`);
334
345
  }
346
+ if (!Array.isArray(allowedRoots) || allowedRoots.length === 0) {
347
+ throw new McpError(ErrorCode.InternalError, "refusing to read spec_path without a containment root — this is a caller bug, not a client error");
348
+ }
335
349
  if (!(await pathExists(args.spec_path))) {
336
350
  throw new McpError(ErrorCode.InvalidParams, `spec_path does not exist: ${args.spec_path}`);
337
351
  }
338
352
  // Enforce path containment: spec_path must be within an allowed root
339
353
  // (cwd's .codecarto/ or the configured library path) to prevent
340
354
  // arbitrary file reads.
341
- if (allowedRoots.length > 0) {
342
- const resolvedSpecPath = await canonicalPath(args.spec_path);
343
- const withinAllowed = await Promise.all(allowedRoots.map((root) => isWithinPathResolved(resolvedSpecPath, root)));
344
- if (!withinAllowed.some((result) => result)) {
345
- throw new McpError(ErrorCode.InvalidParams, `spec_path must be within the workspace (.codecarto/) or the configured library path. Got: ${args.spec_path}`);
346
- }
355
+ const resolvedSpecPath = await canonicalPath(args.spec_path);
356
+ const withinAllowed = await Promise.all(allowedRoots.map((root) => isWithinPathResolved(resolvedSpecPath, root)));
357
+ if (!withinAllowed.some((result) => result)) {
358
+ throw new McpError(ErrorCode.InvalidParams, `spec_path must be within the workspace (.codecarto/) or the configured library path. Got: ${args.spec_path}`);
347
359
  }
348
360
  return readFile(args.spec_path, "utf8");
349
361
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "codecartographer-pi",
3
- "version": "0.12.11",
3
+ "version": "0.13.0",
4
4
  "mcpName": "io.github.HuginnIndustries/codecartographer",
5
- "description": "Evidence-backed reverse engineering and human-gated software planning for Pi and MCP coding agents.",
5
+ "description": "Turn an unfamiliar codebase into a validated reimplementation spec, then synthesize confirmed specs and a product vision into a traceable plan.",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "pi-package",
@@ -13,7 +13,12 @@
13
13
  "mcp",
14
14
  "software-planning",
15
15
  "code-analysis",
16
- "synthesis"
16
+ "synthesis",
17
+ "codebase",
18
+ "ai-agent",
19
+ "context-engineering",
20
+ "code-understanding",
21
+ "spec-driven"
17
22
  ],
18
23
  "license": "MIT",
19
24
  "author": "James Sesler",
@@ -59,5 +64,8 @@
59
64
  },
60
65
  "devDependencies": {
61
66
  "typescript": "^5.9.3"
67
+ },
68
+ "overrides": {
69
+ "undici": "^8.10.0"
62
70
  }
63
71
  }