Skip to content
Merged
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
66 changes: 21 additions & 45 deletions .dev-backlog/sprints/_context.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,30 @@

## Architecture Decisions

- GitHub Issues are the source of truth; `.dev-backlog/` is the execution layer
- Script interfaces should stay stable unless an issue explicitly changes the CLI contract
- GitHub-only since v0.12.0 (#445): the `files` adapter, the frozen adapter ports, and `.dev-backlog/.tracker` are parked at tag `v0.11.0`. No tracker abstraction, no adapter layer, no selection step; a leftover `.tracker` is ignored, never deleted. Re-admitting any of it needs a measured consumer, not an installed CLI.
- Plan refs are `#N` and nothing else. The one parser is `parseIssueRef` / `parsePlanCheckbox` in `scripts/lib.js`; `lib.sh` mirrors it as `RE_ISSUE_REF` for the shell rails. The JSON identity is `{ tracker, id, ref, issue_number }` with `tracker` from `.dev-backlog/.tracker`.
- Milestones, PR relationships, comments, and closing semantics are plain `gh` calls the session (or one small bash guard in `sprint-close.sh`) makes. (Mirrors and progress issues were removed by #340/#347.)
- Setup re-runs preserve user-authored configuration and task bytes; `config.yml` is neither read nor written by this skill.
- Active sprints partition by track scope (2026-07, epic #289): `component:` equality or explicit `scope:` globs decide overlap through the ONE `scopesOverlap()` in `scripts/lib.js` — never re-implement it. Disjoint tracks coexist as a portfolio; overlap fails loud; single-track behavior is the G4 text-byte-identity compatibility surface (anchored in the smoke test; never snapshot `--json`, which is schema-versioned instead).
- One declared task authority: `.dev-backlog/.tracker` is one line the session reads (absent = `github`; `backlog` for the Backlog.md CLI, `files` as its legacy spelling; `gitlab`), parsed by `readTaskAuthority` in `scripts/lib.js` and by a mirrored guard in `scripts/sprint-close.sh`. The rails are the SKILL.md Task Authority table; there is no adapter in code (charter rev 20 Non-Goal), so adding an authority is one table row plus one allow-list entry and needs a measured consumer (GitLab is the stated exception). Scripts never call `gh`/`backlog`/`glab` for task state; milestones, comments, PR links, and closing are calls the session makes, with exactly two scripted exceptions kept by `references/authority-contract.md`: `sprint-close.sh --close-milestone` (GitHub-only, closes the milestone before any local mutation) and `triage-apply --apply` (accepted triage actions behind a human gate).
- The Work rail is the authority's Read verb (`gh issue view N --json body,comments` by default): on GitHub the newest `## Agent Brief` comment overrides the body, a `spec_ref:` line in the task body overrides the body in every authority (and the Agent Brief on GitHub), and a failed read is fail-closed — never fall back to sprint text or a local copy.
- Plan refs are `#N` and nothing else. The one parser is `parseIssueRef` / `parsePlanCheckbox` in `scripts/lib.js`; `scripts/lib.sh` mirrors it as `RE_ISSUE_REF` for the shell rails.
- Active tracks partition by scope through the ONE `scopesOverlap()` in `scripts/lib.js` — never re-implement it. `component:` equality or explicit `scope:` globs decide; disjoint tracks coexist as a portfolio and overlap fails loud. Single-track text output is the compatibility surface (the G4 anchor in the smoke test); never snapshot `--json`, which is schema-versioned instead.
- Scripts own exactly four surfaces: the sprint-file contract, fail-loud shared-state guards (track overlap, unmoored `[~]`, `status: completed`), the `sprint-state` JSON recovery rail, and the `triage-apply` human gate. The keep test for any surface is (1) guards shared or irreversible state, (2) a deterministic check the model cannot cheaply redo, (3) a wire contract another tool consumes; failing all three means delete, not rewrite, and a reviewer defending a deleted doc must show the consumer reading it. A surface that stays keeps its CLI contract; interfaces change only when an issue explicitly changes them. Deletion stops at history: v1.0.0 is reserved and is not a cleanup cut, so completed sprint files stay.

## Conventions

- Prefer minimal-diff refactors over repo-wide rewrites
- Keep `node --test --test-concurrency=1 tests/*.test.js tests/*/*.test.js` green at each step of script cleanup work
- Treat sprint/task markdown shape as a compatibility surface for bash scripts and agent tooling
- Commit the active sprint file to main when opening it; an untracked sprint is invisible to delegate worktrees, which may then seed a duplicate active sprint (2026-07)
- JSON read surfaces (`status.sh --json`, `next.sh --json` via `sprint-state.js`) and `backlog-doctor.js` are the machine path for orientation and health; extend them instead of adding new markdown parsers
- No `skills/` file may carry an unconditional required-read of a cross-repo `../spec-charter/references/` path — it dangles for adopters without craftkit. Consumption-side spec degradation lives in the in-bundle `references/spec-fallback.md`; craftkit's `spec-charter` is the when-installed authoring home. The smoke test GATE_A2A3 enforces this (2026-07 adoption-hardening, #254/#255)
- Sprint spec fields are optional: `sprint-init.js` omits `objectives:`/`component:` when the backing spec file is absent; `backlog-doctor` soft-warns only when the ACTIVE sprint drops a field while its spec exists. Existing `objectives: []`/`component: ""` stay valid (no migration) (#258)

- SKILL.md is goal / rail / Done-when per mode (2026-09 skill-lightening, #396–#399): state a rule once, name the script that enforces it, keep hard constraints only for shared or irreversible state. Re-add prose only when a conformance run shows a repeated mistake (`docs/conformance/`), never for an anticipated risk.
- Eval prompts live in `tests/evals/`, never in SKILL.md — the 2026-09-12 run showed an in-file Eval Prompts section is a self-contaminating answer key.
- Cross-family review at batch boundaries (read-only `codex exec -m gpt-6-astra` on the cumulative diff) found three contradictions repetition had hidden; cheaper than per-PR review and worth keeping for prose waves.
- Keep test for any surface (2026-09 subtraction wave, epic #420): (1) guards shared/irreversible state, (2) deterministic check the model cannot cheaply redo, (3) wire contract another tool consumes. Fails all three → delete, do not rewrite. A reviewer defending a deleted doc as a "consumed contract" must show the consumer reading the doc. Since relay was deprecated (2026-09-19) the `sprint-state` JSON has no external consumer; it stays as the fresh-session recovery rail.
- Tracker generality is a charter Non-Goal (rev 19): do not re-add an adapter, a port contract, or a selection file. `gitlab` was parked at `a8ddb7d`; `files` and the ports at `v0.11.0`.
- Conformance harness recipe: `docs/conformance/2026-09-17-tracker-wave.md` (12 scenarios, prompt builder strips Expected). `codex exec` outside a git repo needs `--skip-git-repo-check`; prompt on stdin with `-`, final answer via `-o`; Astra token counts are on stderr. Fresh-session Fable runs are general-purpose subagents with one Read of the prompt file. A repeated PARTIAL earns either one clause in SKILL.md (#431) or an eval-side Expected relaxation, never both.
- The four surfaces scripts still own (charter rev 19 Non-Goal "Wrapping gh", epic #440): the sprint-file contract, fail-loud shared-state guards (overlap, unmoored `[~]`, `status: completed`), the `sprint-state` JSON recovery rail, and the `triage-apply` human gate. Anything else that wraps the authority CLI is deleted, not rewritten.
- The Work rail is `gh issue view N --json body,comments` (#443): newest `## Agent Brief` comment over body, `spec_ref:` line in the body over both, fail-closed on a failed read. The former task-spec resolver script is parked at `v0.11.0`.
- The backlog-triage report is written by the session (44-line SKILL.md, #433). The scripts-less variant was proven on a 15-issue fixture A/B before deletion (git history (the #433 fixture A/B, removed from the tree 2026-09-19)); repeat that shape — fixture, A render, B session report, checker on close set / protection / exclusions / anchors — before deleting any other judgment script.
- `setup-dev-backlog.js` re-runs only add missing directories: user-authored sprint files and any legacy `config.yml` are preserved byte-for-byte, and a symlinked or irregular root, config, or `sprints/` is refused before any mkdir.
- Commit the active sprint file to main when opening it; a sprint that is untracked or only on a working branch is invisible to delegate worktrees branched from main, which may then seed a duplicate active sprint.
- JSON read surfaces (`next.sh --json` / `status.sh --json` via `sprint-state.js`) and `backlog-doctor.js` are the machine path — extend them instead of adding a second markdown parser. With relay deprecated the JSON has no external consumer; it stays as the fresh-session recovery rail.
- SKILL.md is goal / rail / Done-when per mode: state a rule once, name the script that enforces it, keep hard constraints only for shared or irreversible state. Re-add prose only after a conformance run shows a repeated mistake, never for an anticipated risk. Eval prompts live in `tests/evals/`, never in SKILL.md — an in-file prompt section is a self-contaminating answer key.
- No `skills/` file may carry an unconditional required-read of a cross-repo `../spec-charter/references/` path; it dangles for adopters without craftkit and the smoke test enforces it. `references/spec-fallback.md` is consumption-side only (~1 page cap): it says how these skills BEHAVE when the spec axis is thin or absent and never authors spec semantics, which stays craftkit's job.
- Sprint spec fields are optional and unchecked: `sprint-init.js` never emits `objectives:` (hand-written when a charter exists) and writes `component:` only from `--component`, a free track-scope string with no `spec/` lookup; nothing lints `spec/`. O4 drift detection is the `backlog-triage` Alignment section.
- Every change lands via a PR reviewed by a model family other than the one that wrote it, iterating until LGTM; the cumulative diff gets one cross-family read at batch boundaries, which is cheaper than per-PR review and catches the contradictions repetition hides. Fresh-session conformance runs before each release tag and whenever a SKILL.md rail changes (before/after pair); recipe and retention live in `docs/conformance/README.md`; `codex exec` outside a git repo needs `--skip-git-repo-check`, takes the prompt on stdin with `-`, writes the final answer via `-o`, and reports token counts on stderr. A repeated PARTIAL earns either one clause in SKILL.md or an eval-side Expected relaxation, never both.
- The `backlog-triage` report is written by the session, not by scripts, and is GitHub-only; it reads issues via `gh` and fetches comment history or merged closing PRs only when a judgment needs them, never as a snapshot-enrichment pass. Before deleting any other judgment script, repeat the shape that retired the last ones: fixture, A render, B session report, checker over close set / protection / exclusions / anchors.

## Known Gotchas

- Live GitHub work re-reads the Issue with `gh issue view --json body,comments` when it changes. There is no local fallback copy of Issue state.
- Backlog triage reads issues via `gh`; comment history and merged closing PRs are fetched when a judgment needs them, not as a snapshot-enrichment pass.
- Relationship edges in the triage report are advisory context. A `merged-pr-link` mention is evidence, not a close by itself — close still needs an Obsolete Candidate plus an accepted apply checkbox.
- Backlog triage reports must protect issues referenced in the active sprint Plan or Running Context from close / close-duplicate proposals.
- `gh issue create` does not support `--json`; a `create --json ... || create -b "fallback"` chain fails the first call at flag parsing and posts the fallback placeholder as the real issue body (BACK-243 incident, 2026-07-05). Capture the URL from stdout instead.
- `references/spec-fallback.md` is consumption-side only, ~1 page hard cap: it says how dev-backlog/backlog-triage BEHAVE when the spec axis is thin/absent, never authors spec semantics (that lives in craftkit). Guard against it drifting into a second spec-axis authority — that was the 2026-06/07 silent-fork failure mode (#253)
- The smoke test is fully offline since #445: `status.sh` no longer lists Issues and `sprint-init.js` no longer reads milestones, so a smoke failure is a real regression, not a network flake.
- v1.0.0 is reserved and is not a cleanup cut: do not delete completed sprint files.
- Never chain `gh pr merge` after a grep-filtered test run — capture `$?` from `node --test` and the smoke test first. A backtick inside a JS template literal in `backlog-doctor.js` reached main for one commit this way (2026-09-17, #435).
- In-flight Plan pointer grammar (parsed by `sprint-state.js`): `→ PR #N (state)` at end of line, `[branch:name]`. Any other shape reads as unmoored and fails the live smoke assertion.
- `sprint-init.js --component` is a free track-scope string since #426 (no `spec/capabilities.md` lookup); `objectives:` is never emitted and never checked. Nothing lints `spec/` any more; O4 drift detection is the `backlog-triage` Alignment section.
- #366 (GitHub resilience) closed 2026-08-22 without a matrix. #367 (fresh-session conformance cadence) runs per release tag and on rail changes; only the latest run stays under `docs/conformance/`, the rest is git history.
- Wave 3 (v0.13.0, epic #456) re-ran the keep test with a consumer grep per surface; the shipped `skills/dev-backlog/scripts/` inventory is 2,255 lines / 11 files, and the repo-only helpers (the doc-drift lint and the bash locator) live in `tests/tools/`, not in the bundle — naming them by filename in this file trips the drift check. Count files with `ls | wc -l` before quoting a number: the v0.12.0 close-out miscounted 13 as 14.
- When a flag is deleted, reject it explicitly rather than ignore it: Astra caught `sprint-init.js` silently turning a former `--dry-run` call into a real write (#457). The rejection test is a guard on irreversible state, not a rewrite of the retired surface.
- Cross-family reviewers grade against the issue's literal acceptance numbers; when an estimate (≤ 100 lines) is beaten by the "cut, don't refactor" rule, amend the acceptance on the issue and record the override on the PR instead of trimming to hit the number.
- Wave 4 (v0.14.0, epic #467): `next.sh`/`status.sh` are argument plumbing over `sprint-state.js --format text`; text and JSON come from one parser, so a "next batch" disagreement between them is impossible by construction. Text mode follows the JSON semantics (Plan-only counts, `started`-ordered portfolio, overlap exits 1). When a wrapper stops sourcing `lib.sh`, check what `lib.sh` used to overwrite: Astra caught the wrappers newly honouring a `DEFAULT_BACKLOG_DIR` env var.
- A Node test that asserts a printed path must build it with `path.join`: the Windows CI job prints `nope\sprints`, not `nope/sprints` (#468).
- Measure test-suite totals with one stated glob and keep it across records; the wave-3 and wave-4 conformance records used different globs (4,653 vs 4,604 for the same tree).
- Task authority (v0.15.0, charter rev 20): `.dev-backlog/.tracker` is one line the session reads (absent = github; `backlog`/`files`; `gitlab`); the rails are the SKILL.md Task Authority table. There is no adapter; adding an authority = one table row + one `TASK_AUTHORITIES` entry in `lib.js`, and it needs a measured consumer (GitLab is the stated exception). Scripts never call `gh`/`backlog`/`glab` for task state.
- Two parsers of one file drift: the shell guard in `sprint-close.sh` and `readTaskAuthority` in `lib.js` had to be aligned on BOM, CRLF, dangling symlinks, and "absent vs unreadable" (Astra caught both rounds). If a third reader of `.tracker` is ever needed, route it through node.
- A line budget in an issue ("≤ 15 shipped lines") makes a subagent write 150-column one-liners; state readability rules instead and record the real delta afterwards.
- `gh issue create` does not support `--json`; a `create --json … || create -b "fallback"` chain fails the first call at flag parsing and posts the fallback placeholder as the real issue body. Capture the URL from stdout instead.
- Triage relationship edges are advisory: a merged-PR-link mention is evidence, not a close, which still needs an Obsolete Candidate plus an accepted apply checkbox. Never propose closing an issue referenced in the active sprint's Plan or Running Context, however stale it looks.
- In-flight Plan pointer grammar (parsed by `sprint-state.js`): `→ PR #N (state)` at end of line, or `[branch:name]`. Any other shape reads as unmoored and fails the live smoke assertion.
- When a flag is deleted, reject it explicitly rather than ignore it — a silently ignored `--dry-run` turns a preview into a real write. The rejection test is a guard on irreversible state, not a rewrite of the retired surface.
- Two parsers of one file drift: the `sprint-close.sh` shell guard and `readTaskAuthority` had to be aligned on BOM, CRLF, dangling symlinks, and "absent vs unreadable", and the next batch became reliable only once `next.sh` / `status.sh` rendered through `sprint-state.js --format text`. Route any third reader through node.
- Never chain `gh pr merge` after a grep-filtered test run — capture `$?` from `node --test` and from the smoke test first (the smoke test is fully offline, so a failure there is a real regression, not a network flake). A backtick inside a JS template literal reached main for one commit this way.
- Measure suite totals with one stated glob (`tests/*.test.js tests/*/*.test.js`) and keep it across records; quote no script line counts here, since they go stale every wave and the repo-only helpers under `tests/tools/` sit outside the shipped bundle, so naming them by filename trips the doc-drift check. A Node test asserting a printed path must build it with `path.join`: the Windows job prints `nope\sprints`.
- Numbers stated in an issue drive behaviour: a line budget ("≤ 15 shipped lines") makes a subagent write 150-column one-liners, and cross-family reviewers grade against the literal acceptance numbers. State readability rules instead, and when "cut, don't refactor" beats an estimate, amend the acceptance on the issue and record the override on the PR rather than trimming to hit the number.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Each entry links the GitHub issue (the canonical spec) and the merge PR (the shi

### Changed

- `.dev-backlog/sprints/_context.md` compacted to the current state (55 → 31 lines by `wc -l`): the stale GitHub-only / "`.tracker` is ignored", charter rev-19 tracker-Non-Goal, relay-consumer, deleted-conformance-report, and script line-count inventory lines are dropped, and the duplicated lessons (keep test, cross-family review, measurement hygiene) are merged into one line each. Completed sprint files are untouched.
- `spec/capabilities.md` and the SKILL.md Complete rail: the sprint-close Expected Behavior now matches `sprint-close.sh` (warn on remaining items, never refuse), and the session's close condition is stated the same way — every Plan item `[x]`, or the rest struck or carried with a Progress entry; decision row 2026-09-20; no script change.
- **Relay deprecated.** dev-backlog names no delegate: the `[run:id]` Plan pointer and the `sprint-state` JSON `run_id` field are deleted (an in-flight `[~]` is moored by `→ PR #N (state)` or `[branch:name]`), the JSON is documented as the fresh-session recovery rail rather than a dev-relay contract, and every "Relay optional" sentence in SKILL.md, references, README, and `spec/*` is gone (charter/capabilities decision rows 2026-09-19). BREAKING for any external reader of `run_id` (none known).
- `related-skills` frontmatter drops the deprecated relay skills (dev-backlog: `backlog-triage, spec-charter, spec-grill`; backlog-triage: `dev-backlog, spec-charter`); SKILL.md names "a delegate" instead of dev-relay for in-flight/done markers and delegation.
Expand Down
Loading