Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .codecarto/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,17 @@ Landed as `codecarto_amend` over `scratch/amendments/<slug>.yaml` (see `template

---

## B16. Project-level BACKLOG.md template + GUIDE.md project-vs-framework-level clarification
## B16. Project-level BACKLOG.md template + GUIDE.md project-vs-framework-level clarification — SHIPPED

Landed with the fix for the larger bug it was a symptom of: init copied this repository's
`.codecarto/` wholesale, so every new workspace inherited *this* backlog, thread log, and a
closeout from a CodeCartographer self-analysis session. `templates/backlog-project.md` now
ships with the entry shape B16 specified (rationale, raised-by, preconditions, smallest
viable form), init seeds `BACKLOG.md` and `THREAD_LOG.md` from templates alongside
CONVENTIONS/DECISIONS, and GUIDE.md carries the project-vs-framework distinction under
Trust Boundaries. Original entry below.



**Raised by:** `Spec-delta-3 application - 2026-05-03.txt` (1 agent — first-run-of-SKILL friction)

Expand All @@ -164,7 +174,15 @@ Landed as `codecarto_amend` over `scratch/amendments/<slug>.yaml` (see `template

---

## B17. SKILL clarification — DECISIONS.md vs BACKLOG.md semantics for DEFER
## B17. SKILL clarification — DECISIONS.md vs BACKLOG.md semantics for DEFER — SHIPPED

Landed as specified: the spec-delta-application SKILL's triage section now states that DEFER
goes to BACKLOG.md with no D number, that a refinement made while applying is a decision
recorded in the audit file's Decisions Beyond Triage section (lifted to DECISIONS.md only if
cross-cutting), and that an existing D entry's disposition is updated in place rather than
superseded. Original entry below.



**Raised by:** `Spec-delta-3 application - 2026-05-03.txt` (1 agent — first-run-of-SKILL semantic ambiguity)

Expand Down
8 changes: 7 additions & 1 deletion .codecarto/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,17 @@ Some files in this workspace are **read-only instructions** and must not be modi
| Closeouts (framework-owned) | `closeouts/<date>-<phase-or-module>.md`, `THREAD_LOG.md` | Completion writes or updates one canonical closeout and one idempotent index entry. |
| Conventions (orchestrator-maintained) | `CONVENTIONS.md` | Cross-cutting patterns promoted to project-wide invariants. Phase executors propose; the orchestrator promotes at the phase boundary — in inline runs, the same chat changing hats. |
| Decisions (orchestrator-maintained, append-only) | `DECISIONS.md` | Numbered log of decisions that diverge from spec, prompt, or obvious-default. Completion appends each handoff's `decisions` under `## Completion log`; the orchestrator may re-file entries into categories. |
| Backlog (read-write) | `BACKLOG.md` | Deferred items with rationale. |
| Backlog (orchestrator-maintained) | `BACKLOG.md` | Work this project decided to **defer**, with the reasoning, the preconditions for revisiting, and the smallest viable form. Seeded from `templates/backlog-project.md` at init. |
| Scratch (read-write) | `scratch/*` | Working notes; `scratch/checkpoints/<phase>.md` is the durable in-phase continuation checkpoint until the phase validates; `scratch/spikes/<spike-id>/<scenario>.md` holds spike reports (`templates/spike-report.md`); `scratch/amendments/<slug>.yaml` holds post-pipeline amendments (`templates/amendment.yaml`). |

If you are uncertain whether a file should be modified, treat it as read-only.

### Two things named "backlog", and neither is the other

- **`BACKLOG.md` in this workspace** is *this project's* deferrals: work the project chose not to do yet. `DECISIONS.md` records what the project decided to **do**; `BACKLOG.md` records what it decided to **defer**. Deferrals get no `D` number. When a deferred item is later picked up, remove its entry here and record the decision in `DECISIONS.md`.
- **`status.yaml`'s `post_pipeline` list** is framework-owned lifecycle state, not this file. Items there are retired by `codecarto_amend`, never by hand.
- **CodeCartographer's own backlog** — deferred improvements to the *framework* — lives in the CodeCartographer repository, not in your workspace. If a phase prompt misled you or a validation criterion did not fit, that is feedback to the framework; it does not belong in this file.

## Pipeline Selection

Seven pipeline variants are available. Check the `pipeline` field in `workflow/status.yaml` to see which is active.
Expand Down
4 changes: 3 additions & 1 deletion .codecarto/skills/spec-delta-application/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ Triage every delta into one of four buckets:
|---|---|---|
| **APPLY** | Delta is a real correction or required addition; the spec is wrong without it. | Edit the spec body. Add a `[revised per <source> §<delta-id>]` marker at the changed section. Record in DELTAS-APPLIED.md. |
| **CLARIFY** | Delta proposes wording change; the spec's *meaning* is correct but the language is ambiguous. | Edit the spec wording (not the rule). Record in DELTAS-APPLIED.md as a clarification. |
| **DEFER** | Delta is a real improvement but not load-bearing for the next implementation step. | Add to BACKLOG.md with rationale and a back-reference. Do NOT edit the spec. |
| **DEFER** | Delta is a real improvement but not load-bearing for the next implementation step. | Add to `BACKLOG.md` with rationale and a back-reference (`templates/backlog-project.md` gives the entry shape). Do NOT edit the spec. |
| **REJECT** | Delta is wrong on close reading (premise was incorrect, scope was misread, the rule it proposes already exists, etc.). | Document in DELTAS-APPLIED.md with a one-line rationale. Do NOT edit the spec. |

The previous wisdom: any delta you can't decisively bucket should default to DEFER. The cost of a missed correction is one re-application pass; the cost of a bad correction is shipped.

**DEFER goes to `BACKLOG.md`, not `DECISIONS.md`, and gets no `D` number.** `DECISIONS.md` is for what the project decided to *do*; `BACKLOG.md` is for what it decided to *defer*. A refinement you make while applying a delta — the applied text going beyond the literal proposal — is a decision: record it in the audit file's Decisions Beyond Triage section, and lift it into `DECISIONS.md` only if it is cross-cutting. An existing `D` entry for a proposed delta has its disposition updated in place (`APPLIED 2026-05-03 round-3`); it is never superseded by a new entry when applied.

## Citation convention

Every applied or clarified delta leaves a citation marker in the spec body so a future reader can trace the change back to its source.
Expand Down
51 changes: 51 additions & 0 deletions .codecarto/templates/backlog-project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Backlog

Project-level deferrals: work this project decided **not** to do yet, with the reasoning
that made deferring the right call. One entry per deferral.

This is the project's backlog, not CodeCartographer's. Items about the framework itself —
a phase prompt that misled you, a validation criterion that does not fit — belong in
feedback to the framework, not here.

**BACKLOG vs DECISIONS.** `DECISIONS.md` records what the project decided to **do**;
this file records what it decided to **defer**. Deferrals get no `D` number. If a deferred
item is later picked up, remove its entry here and record the decision in `DECISIONS.md`.

## Format

```
## <ID>. <Short title>

**Raised by:** <closeout file, phase, or DECISIONS.md entry that produced this deferral>

**Why deferred:** <the reasoning — what made this not worth doing now, not just "later">

**Preconditions:** <what has to land before this can be revisited: a module, an artifact,
a decision, an answer to an open question. "None" is a valid answer, but say so.>

**Smallest viable form:** <the least you could build that would settle the item, so whoever
picks it up does not have to redesign it from scratch>
```

## Entries

<!--
Append entries below this marker. Number them however the project prefers (B1, B2, … is
the convention the framework's own backlog uses).

Example:

## B1. Retry policy for the upload path

**Raised by:** closeouts/2026-03-14-contracts.md

**Why deferred:** The contracts phase found no documented retry behavior, but nothing
downstream depends on knowing it — the porting phase can treat uploads as at-most-once
and flag the gap.

**Preconditions:** A protocols-phase answer on whether the server deduplicates by
request id. Without that, any retry policy written here is a guess.

**Smallest viable form:** One paragraph in the contracts report stating the observed
behavior and the assumption downstream phases should hold.
-->
36 changes: 36 additions & 0 deletions .codecarto/templates/thread-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Thread Log — Index

This file is an **index** of per-session closeouts. Each session writes a full closeout to
`closeouts/<YYYY-MM-DD>-<phase-or-module>.md` using `templates/closeout-template.md`, and
appends one line here pointing to it.

The body of each session lives in the closeout file, not in this index. This pattern scales
forever: per-session files are individually small and read-budget-cheap, and avoid the
heredoc-vs-edit sync risks that bite append-to-large-file workflows once the file grows past
~50 KB.

## Format

```
- YYYY-MM-DD — <phase-or-module> — <one-line-summary> — [closeout](closeouts/YYYY-MM-DD-phase-or-module.md)
```

## De-dup discipline

Before appending, scan the bottom 5 entries. If you see a line with the same date AND same
phase-or-module AND same summary, do not append — the prior session already wrote it. The
framework has no programmatic dedup gate; this is human-discipline.

A one-liner to surface duplicates from the shell:

```bash
grep -E '^- [0-9]{4}-[0-9]{2}-[0-9]{2}' .codecarto/THREAD_LOG.md | sort | uniq -d
```

## Entries

<!--
Append one line per session below this marker.
Example:
- 2026-03-14 — architecture — mapped 14 packages across 3 layers; wire formats deferred to protocols — [closeout](closeouts/2026-03-14-architecture.md)
-->
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to this project are documented here. The format is based on

### Fixed

- **A new workspace no longer inherits CodeCartographer's own project state.** This repository's `.codecarto/` is two things at once: the template copied into a user's repo, and CodeCartographer's live workspace. Init copied it wholesale, so every new workspace started with ~40 KB of another project's history presented as its own — a 15 KB backlog of framework deferrals, a thread log with the framework's entries, and a closeout from a session where CodeCartographer analyzed itself. The damage was not only clutter: GUIDE.md keys First-Time Project Setup on `closeouts/` being empty, so the shipped closeout told every new session it was *not* the first to touch the project, suppressing the orchestrator role that #97/#98 made the default. Init now copies through a filter that skips the four orchestrator files and the contents of `closeouts/` (the directory is still created, empty), then seeds `BACKLOG.md` and `THREAD_LOG.md` from new templates alongside `CONVENTIONS.md` and `DECISIONS.md`. The one-off `CHANGELOG-2026-05-02-feedback-pass.md` moved to `docs/`, where framework history belongs. Existing workspaces are untouched — refresh already treated all four files as user-owned. Template backlog items B16 and B17 ship with it: `templates/backlog-project.md` carries the entry shape (rationale, raised-by, preconditions, smallest viable form), GUIDE.md distinguishes the project backlog from the framework's, and the spec-delta SKILL states that DEFER goes to BACKLOG.md with no `D` number while a refinement made during APPLY is a decision.
- **Windows: `writeLibraryConfig` no longer ENOENTs on parentless paths** (#128). The hand-rolled `includes("/")` separator check treated every Windows path as a bare filename, leaving `mkdir` a no-op before the write failed. Now derives the parent directory with `dirname()`.
- **`isWithinPath` accepts subpaths of filesystem roots** (#130). Appending a separator to an already-terminated root (`/` → `//`, `C:\` → `C:\\`) produced a prefix no real path starts with, rejecting every legitimate subpath. Trailing separators are now respected before prefixing.
- **`acquireLock` closes the lock descriptor when `writeFile` throws** (#131). A non-EEXIST write failure previously leaked the file handle until GC.
Expand Down
53 changes: 52 additions & 1 deletion core/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// atomic status-update primitive used by /codecarto-complete.

import { existsSync, readFileSync } from "node:fs";
import { appendFile, copyFile, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
import { appendFile, copyFile, cp, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
import { basename, dirname, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { acquireLock, applyHandoff, createEmptyStatus, normalizeStatus, parseHandoff } from "./status.ts";
Expand Down Expand Up @@ -112,8 +112,59 @@ export async function getWorkspaceState(cwd: string): Promise<WorkspaceState | n
export const ORCHESTRATOR_FILES = [
{ file: "CONVENTIONS.md", template: "conventions-template.md" },
{ file: "DECISIONS.md", template: "decisions-template.md" },
{ file: "BACKLOG.md", template: "backlog-project.md" },
{ file: "THREAD_LOG.md", template: "thread-log.md" },
] as const;

/**
* Project state that must never travel from the packaged template into a new
* workspace.
*
* This repository's `.codecarto/` is two things at once: the template that gets
* copied into a user's repo, and CodeCartographer's own live workspace. The
* second role writes real project state into it — a backlog of framework
* deferrals, a thread log, closeouts of sessions where CodeCartographer
* analyzed itself. Copying the tree wholesale handed every new workspace ~40 KB
* of another project's history as its own, and the damage was not only clutter:
* GUIDE.md keys first-time-project setup on `closeouts/` being empty, so a
* shipped closeout told every new session it was not the first to touch the
* project, suppressing the orchestrator role that issues #97/#98 made the
* default.
*
* The four top-level files are seeded fresh from templates instead
* ({@link ORCHESTRATOR_FILES}); `closeouts/` is created empty.
*/
const INIT_EXCLUDED_TOP_LEVEL = new Set(["BACKLOG.md", "THREAD_LOG.md", "CONVENTIONS.md", "DECISIONS.md"]);
const INIT_EXCLUDED_DIR_CONTENTS = new Set(["closeouts"]);

/**
* Copy the packaged template into a target workspace, skipping this
* repository's own project state. Directories are still created, so a fresh
* workspace has an empty `closeouts/` rather than no `closeouts/`.
*
* @param targetWorkspaceDir - Absolute path to the `.codecarto/` to create or merge into.
*/
export async function copyPackagedWorkspace(targetWorkspaceDir: string): Promise<void> {
await cp(packagedWorkspaceDir, targetWorkspaceDir, {
recursive: true,
filter: (source) => {
const relativePath = relative(packagedWorkspaceDir, source);
if (!relativePath) return true; // the workspace root itself
const segments = relativePath.split(/[\\/]/);
if (segments.length === 1) return !INIT_EXCLUDED_TOP_LEVEL.has(segments[0]);
// Keep the directory, drop what this repository wrote inside it.
return !INIT_EXCLUDED_DIR_CONTENTS.has(segments[0]);
},
});
// The published tarball carries no empty directories, so an excluded-contents
// directory may not exist to be copied at all. Create them either way: a
// workspace whose closeouts/ is missing rather than empty reads differently
// to anything that lists it.
for (const name of INIT_EXCLUDED_DIR_CONTENTS) {
await mkdir(join(targetWorkspaceDir, name), { recursive: true });
}
}

/**
* Seed the orchestrator-maintained files from the workspace's templates
* (issue #98): orchestration is on by default, so a fresh workspace starts
Expand Down
3 changes: 2 additions & 1 deletion extensions/codecarto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
buildSkillPrompt,
buildValidationSummary,
canonicalPath,
copyPackagedWorkspace,
computePerPhaseTotals,
computeTotals,
createEmptyStatus,
Expand Down Expand Up @@ -333,7 +334,7 @@ export default function codeCartographerExtension(pi: ExtensionAPI) {

if (!(await pathExists(targetWorkspaceDir))) {
await mkdir(ctx.cwd, { recursive: true });
await cp(sourceWorkspaceDir, targetWorkspaceDir, { recursive: true });
await copyPackagedWorkspace(targetWorkspaceDir);
}

const rawStatusPath = join(targetWorkspaceDir, "workflow", "status.yaml");
Expand Down
5 changes: 3 additions & 2 deletions mcp-server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
BROADSIDE_LENS_IDS,
type BroadsideLensId,
canonicalPath,
copyPackagedWorkspace,
collectResultText,
completeValidatedPhase,
computePerPhaseTotals,
Expand Down Expand Up @@ -197,11 +198,11 @@ export async function handleInit(args: { cwd: string; pipeline?: string; force?:

if (!(await pathExists(targetWorkspaceDir))) {
await mkdir(cwd, { recursive: true });
await cp(packagedWorkspaceDir, targetWorkspaceDir, { recursive: true });
await copyPackagedWorkspace(targetWorkspaceDir);
} else if (broadsideOnly) {
// Merge the template into the scout-only .codecarto/, preserving the
// broadside state and results already on disk.
await cp(packagedWorkspaceDir, targetWorkspaceDir, { recursive: true });
await copyPackagedWorkspace(targetWorkspaceDir);
}

const statusPath = join(targetWorkspaceDir, "workflow", "status.yaml");
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
},
"files": [
".codecarto/**/*",
"!.codecarto/BACKLOG.md",
"!.codecarto/THREAD_LOG.md",
"!.codecarto/CONVENTIONS.md",
"!.codecarto/DECISIONS.md",
"!.codecarto/closeouts/**",
"agent-skill/**/*",
"dist/**/*",
"assets/logo.svg",
Expand Down
Loading