Skip to content

fix(flags): default pin-sonnet-4-6 and disable-bundled-skills to off - #338

Merged
dean0x merged 2 commits into
mainfrom
fix/flag-defaults-sonnet-pin-bundled-skills
Sep 14, 2026
Merged

dean0x merged 2 commits into
mainfrom
fix/flag-defaults-sonnet-pin-bundled-skills

Conversation

@dean0x

@dean0x dean0x commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Summary

User feedback on two recommended-by-default flags:

  • pin-sonnet-4-6 — users did not want Sonnet pinned to 4.6 by default.
  • disable-bundled-skills — users did not want Claude Code's built-in skills and commands removed by default.

Both flags move from the recommended section to the optional section with defaultValue: false. The mechanism is unchanged: opt in with devflow flags --enable pin-sonnet-4-6 / devflow flags --enable disable-bundled-skills.

Behaviour

  • Fresh install: neither ANTHROPIC_DEFAULT_SONNET_MODEL nor disableBundledSkills is written to settings.json.
  • Existing installs: re-init preserves the manifest's existing flag values (ADR-014), so users who already have these ON keep them ON until they toggle via devflow flags.

Changes

  • src/core/flags.tsrecommended: false, defaultValue: false on both entries
  • tests/flags.test.ts, tests/init-seed.test.ts — pinned default records updated
  • docs/cli-reference.md, CLAUDE.md — flag tables/lists reflect the new defaults
  • CHANGELOG.md — Unreleased / Changed entry

Verification

  • npm run build clean
  • npx vitest run tests/flags.test.ts tests/init-seed.test.ts — 334 passed
  • npx tsc --noEmit — zero errors
  • node dist/cli.js flags --list shows both flags under optional

User feedback: pinning Sonnet to 4.6 and removing Claude Code's bundled
skills should be opt-in, not the recommended default. Both flags move to
the optional section with defaultValue false. Existing installs keep
their manifest value (ADR-014); toggle with `devflow flags`.
…FF in init e2e

The old-format-manifest adoption test listed the Sonnet pin among the
default-ON flags that must materialise in settings.json. Both flags are
now optional/default-OFF, so the test proves the opposite: adoption
records false and writes neither the env var nor the setting.
@dean0x
dean0x merged commit 33b730e into main Sep 14, 2026
2 checks passed
@dean0x
dean0x deleted the fix/flag-defaults-sonnet-pin-bundled-skills branch September 14, 2026 18:50
dean0x added a commit that referenced this pull request Sep 16, 2026
…n, BUDGET_GIT_MD lowered after the condensing pass (resolve B35: documentation-06, documentation-08, consistency-13)

documentation-06: the KB claimed PR #339 "landed on `main`". It has not —
origin/main is 33b730e (PR #338) and #339 is open. The branch is only
*aligned* with main by merge commit 10ea0d5. Replaced with an explicit
"Status — NOT landed" note, since seven workflow commands load this KB up
front and could otherwise treat Phase 2 as shipped (PF-010, PF-025).

documentation-08: the per-provider disqualification margin was stated three
incompatible ways with no denominator ("+3.3% -> +8.0% -> +30.3%" in the KB,
"+31% to +41%" in this test). Replaced with one statement on a named basis,
read off the live table rather than hand-typed: shape 3 (88,302 ch) vs the
shipped shape 2 (77,719 ch) = +13.6%; vs shape 1 (65,187 ch) the same rows
read +35.5% and +19.2%. The table now prints BOTH percentage columns
("vs shape 1 (preloaded set)", "vs shape 2 (per-op loaded set)") so a margin
can no longer be lifted from it without its basis, and shape 1's label no
longer calls itself "today's monolith" — it is the current preloaded set and
has shrunk with every mechanics move since T1.

consistency-13 (+documentation-13): the `## Operations` table's "Fetch GitHub
issue" wording is recorded as a Phase-3 reservation in the KB's handoff
contract, together with SKILL.md:188's rate-limit literal. Nothing renamed in
git.mds — that is contract prose and every character lands against thin
headroom.

BUDGET_GIT_MD 55_900 -> 55_750. A ceiling is a regression alarm, re-derived
only DOWNWARD after a pass that actually cut the artifact; B31's Mechanics-
pointer condensing left 236 ch of stale slack, so the alarm was no longer
armed. 55_750 leaves 86 ch over the measured 55_664. The `budget-git-md`
ceiling entry in numeric-floors.json moves down with it (value + pattern
re-pinned together) — the permitted direction for a ceiling, not a floor
lowering; the manifest guard's probe still proves an increment goes red.

Carried hand-offs from earlier batches, all in the KB:
- B28/architecture-03: a prunable flat cross-cutting root recorded as a
  Phase-3 candidate beside consistency-13's row.
- B34: the stale "headroom is 4 chars as of ce491f9" gotcha re-measured.
- B31: the retired 147-char Mechanics pointer replaced by the shipped
  56-char line.
- B20/B32: the tech-debt archive chain brought to its end state — the issue
  number is validated into a local before promotion, and add_tech_debt_item
  appends to the issue BODY via `gh issue edit --body-file`, which is what
  keeps the size check and the archive path reachable.

Verified: npx vitest run tests/tracker/byte-budget.test.ts
tests/guards/numeric-floor-manifest.test.ts tests/guards/retired-wording.test.ts
-> 36/36 pass.
dean0x added a commit that referenced this pull request Sep 16, 2026
…ferences (#339)

Tracker **Phase 2** of four. Internal refactor: the Git agent's GitHub mechanics move out of the always-loaded prompt into generated skill references, addressed skill-relatively through a single resolution point. **No user-visible behaviour changes.**

## Problem Being Solved

`dist/agents/git.md` was **65,677 characters (66,180 bytes, 992 lines, 18 operations)** re-sent on **every** Git spawn — a shared agent prompt file is billed per spawn (PF-026). Roughly **9,400** of those characters are GitHub-specific *mechanics* — `gh` invocations, header names, rate-limit detectors — interleaved with the provider-independent *contract*: each op's `**Input:**`, its `**Output:**` template, its `**Degradation (D4):**` clause, and the D4/D11 invariants.

Three live consequences:

1. **PF-023 had ~30 filename-composition sinks instead of one convergence point** (GAP-10, CRITICAL). There was no single place a provider was resolved, so any future provider token would have to be threaded through every sink.
2. **The always-loaded D4/D11 cross-cutting text was GitHub-specific while the contract declares it provider-independent** (GAP-03, CRITICAL) — two authorities on the secret-redaction path.
3. **`src/assets/skills/git/SKILL.md` (283 L / 9,205 ch, preloaded on the same spawns) carried two live safety contradictions**: `if [ "$REMAINING" -lt 10 ]; then sleep 60; fi` contradicts D4's STOP-the-fan-out rule (waiting out a secondary limit extends the provider's penalty window), and `gh release create … --notes "$NOTES"` is an inline-body recipe where `create-release` mandates `--notes-file` after a scrub whose failure is a hard stop. Both were invisible to every existing guard.

## Key Changes

| Subtask | What landed |
|---|---|
| **T1** | The generation substrate: `expandVariants` + the `(module, op)` registry in `src/core/mds-variants.ts`, `compiledSkillRefsDir()`, a third build destination in `scripts/build-mds.ts`, the **28-line** provider-resolution preamble (ceiling 40), recursive `collectSkillRefFiles`, and the byte-budget four-shape table. |
| **T2a/T2b** | The 10-op split (15 commits, one per op), the D4/D11 invariant-vs-detector separation, the three cross-cutting cuts (`learn-conventions`, the D10 publication gate, the marker legend), the `SKILL.md` cut, and `github-api.md`'s five tracker sections. |
| **T3** | `_partials/_tracker.mds` + five host adoptions, the `{ISSUE_REF}`/`{ISSUE_ID}` vocabulary, the `### Handoff Values` producer/consumer seam, the marker-literal removals, and the two dispositions. |
| **T4** | The installer's converge-not-merge reference overlay with an atomic per-unit swap, a recursive path-keyed prune, `formatOverlaySummary` as a named render site, and the packed-tarball manifest guard. |
| **T5** | This subtask: the guard battery, the extractor retarget, both fixture regenerations, and the docs sweep. |

**The numbers.**

| Measure | Before | After | Gate |
|---|---|---|---|
| `chars(dist/agents/git.md)` | 65,677 | **55,664** (56,075 bytes, 913 L) | ≤ 55,750 ✅ (headroom 86 — ceiling lowered from 55,900 after the Mechanics-pointer condensing pass; ceilings lower-only) |
| `chars(skills/git/SKILL.md)` | 9,205 | **6,581** (213 L) | ≤ 6,600 ✅ (headroom 19) |
| tracker-scoped worst-case loaded set | 77,824 (monolith) | **77,719** | ≤ 77,824 ✅ (headroom 105; `max_op` = `manage-debt` 5,007) |
| Provider-resolution preamble | — | **28 lines** | ≤ 40 ✅ |
| Generated references | 0 | **13** (10 GitHub ops + 3 cross-cutting) | — |
| Containment exemptions | — | **63** (29 + 11 `#340.` + 8 `#341.` + 15 `#339-resolve.`; tracked in `tests/fixtures/containment-exemptions.ts`), each individually justified | asserted non-empty, still-needed **and** ≥ 40-char rationale |
| MDS reference modules | 0 | **2** | — |
| Partials / hosts | 11 / 15 | **12 / 16** | manifest entries, never bumped literals |
| `github-api.md` | 16,052 | **15,812** | — |

The four-shape table was re-recorded multiple times as real content arrived. The per-provider single-file shape's disqualification margin now stands at **+13.6%** over the per-op loaded set (shape 3, 88,302 vs shape 2, 77,719) and **+35.5%** over the preloaded shape (shape 1, 65,187) — decisive rather than an artefact of stubs.

## Breaking Changes

**None user-visible.** New files appear under the *installed* skill directory (`~/.claude/skills/devflow:git/references/`). **Zero new files in any user's project tree; zero new prompts.** `Tracked = #{n}`, `Depends on: #{n}`, `42-jwt-auth.{ts}.md` and `issue: 42` are byte-identical under `github`, each pinned by its own assertion. No Jira or Linear literal exists anywhere outside the provider map.

## Decisions made where the plan was ambiguous

Every one, so a reviewer can reject any of them explicitly rather than discovering it.

**T1 — substrate**

- **(a) `dist/skills/git/references` WAS added to `ALLOWED_OUTPUT_DIRS`** (`D-SKILLREFS-ALLOWLIST`), with a new `HostVariant` member `'skill-refs'`. Routing the third destination around `resolveOutputDir` would have made the allowlist a *partial* gate — true for two destinations, bypassed for the third.
- **(b) ADR-007's "four neutral values" — AMBIGUITY RECORDED.** ADR-007's body does not enumerate four neutral values; its subject is retiring an on-demand script. The transferable part is its *discipline*: a missing artifact degrades to a **neutral value**, never to a **fallback path**. The preamble states four **outcomes**, of which only the first two are neutral. **If a reviewer holds that "four ADR-007 neutral values" meant something else, this is the block to change** — 5 of the 28 preamble lines.
- **(c) T1's reference stubs deliberately carried no `## Operation:` anchor**, because the anchor breaks a `'sole'`-mode lookup until that guard is repointed. T2a landed both halves in one commit, as required.
- **(d) No new printed build line.** An output line nothing asserts is an artifact with no consumer (ADR-003).
- **(e) `output-name:` is REFUSED on a reference module**, not ignored: its filenames come from the op registry, so silently dropping a key honoured on the other two variants is the authoring trap the empty-value refusal already guards against.
- **(f) Reference outputs are pruned recursively**, bounded at `MAX_REFERENCE_SWEEP_DEPTH = 8`; empty directories are left in place (removing them races a concurrent build's `mkdir`).
- **(g) AC-1.2 scope-fence narrowing** — `expandVariants(` and `(module, op)` were legalised and are named in a `LEGALISED_IN_PHASE2` constant, asserted both present there and absent from the forbidden table, so the narrowing reads as a narrowing and not as a hurried deletion.
- **(h) `BUDGET_LOADED_SET` is a frozen literal, not `TOTAL_CHARS`.** Those are equality baselines that move in every regeneration commit; a budget derived from them would follow the artifact down and end up asserting "the current size is the current size".
- **(i) The loaded-set budget could not be met by cutting `git.md` alone** — measured, not assumed. That is why P2-S8's `github-api.md` cut is load-bearing for AC-2.5 rather than tidy-up.

**T2a — the unsampled half**

- **One BEYOND-TABLE `SKILL.md` cut, plus one condensing.** The named cuts alone land ~700 characters over budget. `## Anti-Patterns` was cut (every row restates a rule stated once above it; `references/violations.md` is the named authority). `## When This Skill Activates` was not cut — it was condensed: the five bullets collapsed to the one-line body that still stands at `SKILL.md:22-24`, since the frontmatter `description:` already drives activation. No rule was removed — only a second or third copy, or a restated one. The file lands at 6,581 of the 6,600-char budget, so no further cut is owed. **If a reviewer rejects the `## Anti-Patterns` cut, the budget cannot be met and `BUDGET_SKILL_MD` must be re-derived, never raised.**
- **Nine pre-existing D11 bypasses in `references/github-api.md`** are **frozen by exact text** in `KNOWN_GITHUB_API_INLINE_BODIES` (`D-INLINE-BODY-EXCLUSIONS`) rather than silenced by narrowing the guard back. Both arms are asserted — a tenth offender goes red, **and** an entry that stops matching goes red — so the list can only shrink. Follow-up issue: #340.

**T2b — the sampled half**

- **[DR-20](ii) deviation — ACCEPTED.** The artifact says `gh repo view` appears **only** in `publication-gate.md`. SG-8 forbids moving `post-review-summary` / `post-resolution-summary` mechanics, and their step-3 probe lines are exactly such mechanics, so the literal reading is **unsatisfiable without a move the phase prohibits**. Implemented as *"only in `publication-gate.md` and the two operations that name it"* — the original scope property **plus** the new file, strictly stronger than "the literal exists somewhere". The literal reading requires lifting SG-8, which is a plan change.
- **D11 chain deviation — ACCEPTED.** The scrubber invocation (`node …redact-secrets.cjs …`) **stays inline** in `git.md`; only the `&& gh …` half became `&& <the resolved provider's post command>`. Making the containment control a file the spawn might not have loaded is PF-027's failure mode; making the *post command* provider-specific is the point of the split. Narrower than P2-S4's table wording, deliberately.
- **`D-LOADED-SET-SCOPE`** — the `max over ops` term is taken over `TRACKER_GITHUB_OPS`. AC-2.5 bounds "the worst-case **tracker** spawn"; `fetch-review-threads`' 15,812-character load of `github-api.md` predates the split and is not a cost it introduces. It is **recorded as its own table row** rather than dropped.
- **Conflict C13** — the containment oracle was authored in T2 rather than T5, so the invariant existed before the text that had to satisfy it.

**T3 — the command layer**

- **`_preamble.mds:75` (ADR-008's Iron Rule) — EXPLICIT NO-CHANGE.** It is provider-agnostic in substance: it names no vendor, and every noun in it ("issue reading", "dependency reasoning") is already tracker-neutral. The disposition line **is** the deliverable; no code edit and **no test** — a faked mechanical assertion for a process-only row is what the plan forbids.
- **`dynamic-build.mds:466-474` — RETAINED.** The double-wrap premise does not hold: the op-side containment wraps the *issue body*, while the skeleton's wrap covers the command-constructed `JSON.stringify(remainingTickets)` the Design reader prompt quotes each round, which never passes through the op's Output block. It is the **only** containment at that site; removing it is PF-058's failure mode. Pinned mechanically so a later reader cannot delete it as redundant.
- **`_preamble.mds:31` — `gh` deliberately KEPT.** It is the concrete CLI an author reaches for, and naming it is what makes the sandbox denial legible. What changed is the *scope*: the denial is over any tracker CLI rather than reading as gh-only.
- **A second marker site the plan did not name — `code-review.mds:333`** restated the review-summary marker literal. §14.9's denylist row is unqualified, so it was neutralised the same way rather than allowlisted. An allowlist there would re-open GAP-20 for that marker.

**T4 — the installer overlay**

- **`D-OVERLAY-FLAT-UNIT`** — the flat cross-cutting set is ONE unit, not one per file. The documents land directly in `references/` beside hand-authored files the overlay must never touch, so there is no directory to rename; they get the same *decision* rule as a provider directory and are promoted by one rename per document. Recorded honestly at the code site: **the promotion loop is the one place a mid-flight I/O error could leave the flat set partly refreshed.** One unit per flat file was rejected — three documents always generated and read together would report three independent outcomes.
- **`D-OVERLAY-MODE-SCOPE`** — the whole `references/` tree is normalised to `0644`, not only this run's files, because `copyDirectory` preserves source modes and a reference is read-only instruction text.
- **`sweptOrphans` reuse** — the prune's removals fold into the existing sweep arrays via `recordSweep(report, 'reference', …)` and inherit `formatSweepSummary`'s render site, rather than needing a third report field.

**T5 — this subtask**

- **The `github-status-lines.txt` re-capture (option A), user-authorised 2026-09-14.** See its own section below.
- **`D-STRADDLE-SPLIT`** — the two straddling samples are split into two samples each rather than repointed to one side. `between()` slices one string, so a start anchor in a reference and an end anchor in `git.md` cannot be expressed; dropping either half would silently shrink coverage of that operation.
- **`D-CAPABILITY-PROBE-SCOPE`** — the capability-hoist guard's probe verbs are the **session-scoped** half of §14.3's capability column. The per-item capabilities (fetch by key, list the comments *of one issue*, comment, edit body) are the loop's **payload**; a guard that called them probes would report `backlink-shipped-issues` step 1 as a violation, and the only way to satisfy it would be to stop fetching the data the loop exists to fetch. They are named in a `PER_ITEM_PAYLOAD` constant rather than omitted silently.
- **`D-EXTREF-SCOPE`** — `skills/git/SKILL.md`'s Extended References table keeps its `references/tracker/{provider}/{op}.md` row and gains **no** row for the three flat cross-cutting documents. Each is named from the agent at its point of use, which is the reachable consumer ADR-003 asks for, so a row is documentation — and ~120 characters of it is a real per-spawn cost in the one file preloaded on every Git spawn. Headroom is 19 characters; `BUDGET_SKILL_MD` was **not** touched.
- **`D-PROOF-TRANSITION`** — the Phase-0 faithfulness gate (run the extractor over the `b6928e5` snapshot, require the old fixture back byte-for-byte) cannot be re-run across a deliberate re-capture: the old bytes are the ones the retarget changes. The standing proof is the **derivation** test — the `--unfreeze --out-dir` case re-derives the whole fixture from the live tree on every run and compares byte-for-byte — and the inputs it derives from are themselves frozen (`git.md` by the git-agent golden, the generated references by the containment oracle's `101bda7` baselines). The retarget's own non-vacuity comes from `STATUS_LINE_REFERENCE_FILES`, enforced in both directions. A future extractor rewrite inherits the Phase-0 obligation unchanged, with the re-capture commit as its baseline tree.

## The `github-status-lines.txt` re-captures (option A)

**Two re-captures, both authorised and both spent.**

**First — authorised by the user on 2026-09-14 (`e4876e0`).** §14.7 said the fixture was frozen *"NEVER through Phase 3"*. Preserving it and doing P2-S4 are **mutually exclusive**: P2-S4's split line runs through the middle of two sentences the fixture samples, so no relocation of verbatim text can reconstruct the sampled bytes, and one sampled anchor's disappearance made the extractor *throw* rather than diff.

What the fixture actually measured is why this is affordable: `extractStatusLines` samples **prompt-internal process steps** (`3. Extract items to add:`, `1. Find last tag:`, `   ## Version Names`) — precisely the bytes this phase is chartered to relocate — not the user-visible surface. AC-2.10's four genuinely user-visible byte-identity claims are pinned separately and are untouched.

**AC-2.1's second half and prefix-shippability clause (iv) are contradicted deliberately and are superseded by commit `e4876e0`.**

**This first authorisation was spent** after `e4876e0`: the fixture was frozen again, and §14.7's successor text (recorded here and in the test file's header) said a further re-capture would need its own explicit authorisation.

**Second — authorised by the user on 2026-09-15 (`c0b9860`), for the resolve-wave Mechanics-pointer condensing pass.** That pass shrank two Mechanics-pointer lines the fixture samples; only fixture lines 134 and 161 changed, every other sampled line is untouched. **This second authorisation is also spent.** The fixture is frozen again from `c0b9860`; any further re-capture needs its own explicit authorisation.

**Fixture diff: 11 insertions, 8 deletions. 17,914 → 17,709 bytes; 246 → 249 newlines.** Per sample:

| # | Sample | Outcome |
|---|---|---|
| 1 | D4 contract block | **3 lines rewritten** — P2-S4: `gh` → "the tracker"; the 403/429 + `X-RateLimit-Remaining < 10` signal → "a provider-signalled secondary rate limit"; "GitHub's penalty window" → "the provider's"; the `< 50` rung → "the provider's backpressure rung" |
| 2 | D10 step 2 | **bytes unchanged**, now read from `references/publication-gate.md` |
| 10 | manage-debt | **split**; moved half unchanged, retained half **+3 lines** (`**Process:**`, blank, the `**Mechanics:**` pointer) |
| 13 | gather-release-evidence | **+2 / −1** — the `**Mechanics:**` pointer added, the old per-commit `gh api` fan-out line gone ([DR-17] commit B) |
| 14 | learn-conventions | **split**; moved half unchanged, one blank line lost at the join |
| 19, 20 | backlink-shipped-issues, ensure-traceable-issue | **bytes unchanged**, read from their references |
| 21 | post-wave-report body | **−1 line** — step 2 (local path resolution) stayed in `git.md` while steps 1/3/4 moved, so the reference's contiguous slice no longer spans it |
| 24 | post-wave-report marker | **bytes unchanged**, read from the reference |
| — | `dynamic-build.mds` 1/2 | anchor retargeted; line text changed by P2-S12 (the caller no longer restates the marker) |
| — | `resolve.mds` 4/6 | `Tracked=#` → `Tracked={ISSUE_REF}` (P2-S11) |
| — | the other 14 samples | **byte-identical** |

## Golden-diff hunk classification (AC-2.1, `git diff main...HEAD -- tests/fixtures/golden/git-agent.md`)

Re-issued over the **whole branch diff**, not a single commit: the golden was regenerated twice — `2e019a5` after the contract/mechanics split and `10ac94c` after the Scrutinize-pass agent fixes — so a classification of either commit alone is not a classification of what ships. **252 changed lines; 82 insertions, 170 deletions.**

**170 removed lines — 10 blank / horizontal rule, 160 content:**

- **150 are PURE MOVES** — byte-identical in a generated reference or the git skill: `learn-conventions.md` 47 · `tracker/github/setup-task.md` 24 · `ensure-traceable-issue.md` 15 · `post-wave-report.md` 11 · `publication-gate.md` 10 · `manage-debt.md` 10 · `decision-markers.md` 9 · `backlink-shipped-issues.md` 9 · `ensure-pr-ready.md` 6 · `fetch-issues-batch.md` 6 · `fetch-issue.md` 2 · `create-release.md` 1.
- **10 are NOT moves**, and each falls inside a `CONTAINMENT_EXEMPTIONS` range. All **nine** `git-agent.md` ranges are exercised by this diff, none unexercised: `:24-25` (two lines — the D4 remote-unavailable and secondary-rate-limit sentences), `:28` (the `< 50` backpressure rung), `:45` (the D11 "to GitHub" scope sentence), `:50` (the `&& gh …` half of the scrub chain), `:541` ([DR-17] commit B's per-commit fan-out), `:715` (`resolve-review-threads`' D4 clause — extended, not cut), `:910` (`ensure-traceable-issue`'s D3 pointer — repointed after P2-S7 deleted the section it named), `:968` (`## Principles` item 1), `:990` (`## Boundaries`' `gh pr create`).
- **0 unaccounted.**

**82 added lines — 28 blank / horizontal rule, 54 content:**

| Category | Lines |
|---|---|
| The provider-resolution preamble — a new section (`## Tracker provider resolution` + `## Tracker input contract`) | 21 |
| `**Mechanics:**` pointers, one per op whose body moved | 11 |
| The `### Handoff Values` producer blocks in `setup-task` and `fetch-issue` | 8 |
| D4 rewrites — invariant kept inline, GitHub signal deferred to the provider (`:24-25`, `:28`, `:715`, `:968`) | 5 |
| D11 rewrites — the scope sentence and the provider-post placeholder in the shell-discipline fence (`:45`, `:50`) | 2 |
| The two summary ops naming `references/publication-gate.md` ([DR-20](i)) | 2 |
| Cross-cutting reference namings — the legend pointer to `decision-markers.md`, `setup-task`'s 1b/1c pointer to `learn-conventions.md` | 2 |
| Scrutinize-pass and detector-strip rewrites — `ensure-pr-ready`'s ALWAYS-ON contract line (4b), `ensure-traceable-issue`'s untrusted-input contract (`:910`), `## Boundaries`' replacement (`:990`) | 3 |
| **Unclassified** | **0** |

**Every hunk is a pure move or a named exemption. Nothing else is in the diff.**

## Per-op retained / moved table

`moved` counts the lines of the op's `### Process` block in its generated reference; `retained` is the op's section length in `git.mds`. Each guard that reads the op is listed with the extraction **mode** it uses ([DR-18] — there is no default; `'sole'` throws on a second authority, `'union'` concatenates and counts).

| Op | Moved into `tracker/github/{op}.md` | moved / retained | Stayed in `git.md` | Guards, and their mode |
|---|---|---|---|---|
| `setup-task` | steps 1b, 1c, 2, 3 (conventions probe, issue-first lookup, branch-convention detection, branch-token derivation) | 27 / 58 | heading, prose, Input, steps 1a/4/4b/5, Output incl. `### Traceability` | conventions-commit arm (a) **`'sole'`** over a git.md-only corpus; AC-0.10 issue-body containment **file-scoped**; byte-budget `nameableFrom` scans git.md |
| `fetch-issue` | steps 2, 3 | 4 / 43 | Input, step 1 (the `#`-strip ref rule), D4 clause, Output | conventions-commit arm (d) **`'sole'`**; AC-0.10 **file-scoped** |
| `fetch-issues-batch` | step 2 — the single bounded GraphQL batch query, verbatim [DR-07] | 10 / 56 | Input, steps 1/3/4/5 (`#`-strip, ≤50, `TRUNCATED`, `NOT_FOUND ({refs})`, containment), D4 clause, Output | ≤50 / `TRUNCATED` / `## Issues Batch` pins **`'sole'`** (unchanged); the `gh api graphql` pin **→ `'union'`**; conventions-commit arm (c) **`'sole'`**; AC-0.10 **file-scoped** |
| `manage-debt` | the whole seven-step Process | 12 / 28 | Input, the D4 clause, Output | 60000 archive threshold **`'union'`**; D4 clause + `(pending —` **`'sole'`**; D11 forward/reverse **`'union'`** |
| `create-release` | the `## Closed Issues` enrichment bullet only | 5 / 36 | 1a/1b/2/3/4, the `## Commits` bullet, the 60000 cap, step 6, the D4 carve-out, Output | AC-0.6b remote-I/O + D4 **`'sole'`**; D11 reverse **`'union'`** |
| `gather-release-evidence` | **A:** step 4 verbatim · **B:** rewritten in the reference, batch-first | 7 / 35 | Input, steps 1/2/3/5, the D4 clause (H12-asserted), Output | H12 assertion, **git.md section scan**; [DR-17] `≤25` / no-fan-out, **reference file scan** |
| `backlink-shipped-issues` | the viewer-login hoist and steps 1–4, **plus** the GitHub rate-limit and D11 chain signals from P2-S4 | 16 / 41 | Input, step 0 (validation + VERSION normalisation), the ≤50 loop bound, the D4 clause, Output | ≤50 bound **`'sole'`**; D11 forward/reverse **`'union'`**; P2-S4 one-home-per-detector, **reference tree scan** |
| `ensure-traceable-issue` | the whole three-step Process | 18 / 27 | Input, the D4 clause, the D3 section list, the untrusted-input contract, Output | D11 forward/reverse **`'union'`**; AC-0.6b **`'sole'`** |
| `post-wave-report` | steps 1, 3, 4 (marker probe, body composition + cap, scrubbed post) | 15 / 57 | Input + input descriptions, step 2 (local path resolution), the D4 clause, Output | 60000 cap pin **→ `'union'`**; AC-0.10 external-thread **file-scoped**; D11 forward/reverse **`'union'`** |
| `ensure-pr-ready` | step 4b verbatim (`exists_open` + `render_pr_link`) | 12 / 48 | steps 1/2/3/4a (the D11 PR-body sink)/4c/5/6, a one-line ALWAYS-ON contract, Output | `gh pr create … --body-file` + D11 **`'sole'`**; D11 bypass scan, **corpus-wide** |

Cross-cutting cuts:

| Cut | Moved to | Size | Stayed in `git.md` | Guards, and their mode |
|---|---|---|---|---|
| P2-S5 cut 1 | `references/learn-conventions.md` (57 L, **3,627 ch**) | the whole `**Process:**` block | heading, prose, Input, a pointer saying it loads only when `.devflow/conventions.md` is absent, the D4 clause, Output, `**Commit boundary:**` | Guard 2's **four** bound pins **→ `'union'`**; conventions-commit arm (b) stays sink-wide |
| P2-S5 cut 2 | `references/publication-gate.md` (13 L, **1,114 ch**) | the whole `## Publication gate (D10)` section | nothing — the two summary ops name the file | "section exists" **→ joined corpus**; the [DR-20] successor pair (new); the other eight D10 `it`s unchanged on git.md |
| P2-S4 | `tracker/github/backlink-shipped-issues.md` § *Provider signals (GitHub)* | the D4 rate-limit signals, `gh` availability, the concrete scrub-then-post chain | every D4 and D11 **invariant** | the two threshold pins **→ `'union'`**; a new cross-cutting negative guard, **section-scoped over git.md** |

**Why a guard's mode changed, in one sentence:** a guard whose literal left `git.md` must read the union (GAP-21 — guard classes move with the text); a guard whose literal stayed must **not** widen, because `'sole'` throwing on a duplicated `## Operation:` anchor is the signal that a contract acquired a second authority.

## Test inventory

Every guard, the known-bad sample it must fail on, and the commit in which it was proven red.

| Guard | File | Known-bad sample | Proven red in |
|---|---|---|---|
| Byte budget: six assertions (3 budgets, preamble ceiling, single-naming-line, seeded second line) | `tests/tracker/byte-budget.test.ts` | a seeded second `references/tracker/` naming line appended to the compiled agent | `4a2c11b` (landed with 6 red), `17ffa60` (3 → green) |
| Containment oracle — zero unaccounted lines | `tests/tracker/containment.test.ts` | a baseline line present in no target → `['probe.md:1']`; an exemption silences exactly its own range → unaccounted `[2]`, `linesScanned 1` | `08c1190` |
| Inline-body guard (`INLINE_BODY_SHAPES`, `joinContinuations`) widened on pattern **and** scope | `tests/git-agent.test.ts` | `SKILL.md:211` `gh release create … --notes "$NOTES"` — already in the tree | `fcec744` (**landed red on purpose**) |
| `## Operation:` anchor vs the `'sole'` corpus | `tests/git-agent.test.ts` | the live collector: *"operation 'setup-task' not found in corpus"* ×3 | `6f7230f` |
| GAP-25 `sleep 60` | `tests/tracker/containment.test.ts` | committed probe over the pre-split baseline: exactly **3** sites | permanent probe (H10) |
| GAP-25 `≤50 branches` single authority | `tests/git-agent.test.ts` | a seeded second statement → count 2 (live count 1) | permanent probe |
| Unquoted-heredoc guard | `tests/guards/heredoc-quoting.test.ts` | pattern probe (`<<EOF`, `<<-MSG` match; `<<'EOF'`, `<<"EOF"`, `<<<` do not) **and** a seeded unquoted heredoc through the same collector | `82d8b86` |
| AC-2.13 legend set relation | `tests/git-agent.test.ts` | a seeded `D42` row with no definition → `['D42']` | `ac9bd3e` |
| Frozen-exclusion ratchet (an exclusion that stops matching is red) | `tests/git-agent.test.ts` | fired **for real**: two `github-api.md` exclusions went stale and the guard went red until they were deleted | `16baec2` |
| [DR-17] batch-first release evidence | `tests/tracker/containment.test.ts` | *"the bounded sequential fallback must name its own limit"* (`≤25` absent) and a per-commit `gh api` loop present | before `9c704df`; permanent probe over the baseline finds exactly 1 |
| [DR-20] successor pair (4 `it`s) | `tests/git-agent.test.ts` | `expected [] to equal ['post-resolution-summary', …]`; a seeded third op naming the gate | before `7fedbb1` |
| P2-S4 no provider detector in a cross-cutting section | `tests/git-agent.test.ts` | 6 live sites listed by the collector; permanent probe over the baseline asserts `>= 6` | before `bcff97d` |
| P2-S4 one home per moved detector | `tests/git-agent.test.ts` | `expected [] to have length 1` for `X-RateLimit-Remaining\` header < 10` | before `bcff97d` |
| [DR-19] shared-literal registry, both arms | `tests/tracker/containment.test.ts` | a seeded restatement in a provider file → `['tracker/github/probe.md']` | `7fedbb1` |
| `_tracker.mds` adoption (AC-2.9, two-armed rule) | `tests/build-mds.test.ts` | 10 violations across 5 hosts, both defines missing | before `caae772` |
| Per-define non-emptiness (GAP-44) | `tests/build-mds.test.ts` | a hollowed-out define body through the same slicer | `caae772` |
| No `<!-- devflow:` in any `dist/commands/*.md` | `tests/build-mds.test.ts` | 2 live sites (`dynamic-build.mds`, and `code-review.mds` — which the plan did not name); a seeded restatement in a temp copy of a real dist file | before `bbf0936` |
| `_engine.mds` neutral wording (C9/C9b) | `tests/build-mds.test.ts` | the exact pre-neutralisation literal | before `bbf0936` |
| AC-2.10 byte-identity ×4 + the two-sided grammar pairs | `tests/dynamic/depends-on-grammar.test.ts` | 15 failed / 5 passed against the stashed pre-change tree | before `1953114` |
| Seam Direction 3 (GAP-15 producers) | `tests/seams/command-agent-input.test.ts` | the real collector over the committed pre-split baseline reports exactly `['ISSUE_BRANCH_TOKEN','ISSUE_ID','ISSUE_PR_LINK']` | permanent probe |
| Reference overlay, 20 tests | `tests/installer/reference-overlay.test.ts` | import-level red at `47352d6`, **then** the naive additive-copy shape [DR-05] rejects: **11 of 17 failed** | `47352d6` + a behavioural run against the naive shape (never committed) |
| Packed-tarball reference manifest (Guard 6e) | `tests/packaging.test.ts` | a packed list with one manifest entry removed, through the same collector | `b48919f` |
| **capability-hoist** [DR-11] | `tests/guards/capability-hoist.test.ts` | **two seeded bad fixtures** (identity probe in a loop; a per-item issue-type metadata lookup) **plus** a hoisted-form fixture that must *not* fire; and a position-blind collector run listing the corpus's 7 real hoisted probe sites | `179ba71` |
| **provider-scope**: Jira/Linear outside the provider map | `tests/guards/provider-scope.test.ts` | seeded foreign literals in a module and a dist command; a probe proving the allowlist silences its own block **and nothing beyond it**; a stale-allowlist arm | `179ba71` |
| **provider-scope**: `mcp__` / user-facing "MCP" | `tests/guards/provider-scope.test.ts` | seeded vendor literals; the first live run found `src/assets/agents/test.md`'s legitimate Chrome tool allowlist, which is why the scope is the Git spawn surface | `179ba71` |
| **provider-scope**: no `tools:` key on the Git agent; AC-2.7 `_mcp.md` absence | `tests/guards/provider-scope.test.ts` | frontmatter-key collector; the `tracker/github/` directory asserted present so the absence is an absence, not an unbuilt tree | `179ba71` |
| **AC-2.7 reachability**, both directions | `tests/tracker/containment.test.ts` | a seeded emitted file outside the registry; a mutated load-instruction template turns all three arms red | `179ba71` |
| **guard-census** (AC-2.6) | `tests/guards/guard-census.test.ts` | floor raised to 69 → red at 68; a seeded extra op name → roster red; a removed declaration must lower the count | `179ba71` |
| **retired-wording** Phase-2 denylist, scoped | `tests/guards/retired-wording.test.ts` | with the scope short-circuit removed the collector reports **7** live sites (`_github.mds`, `github-api.md`, `git.mds`, `synthesize.md`) — the scopes are load-bearing, not decoration; plus a dead-scope arm and an in/out-of-scope probe | `179ba71` |
| Extractor retarget non-vacuity | `tests/helpers.ts` | `ref()` refuses an undeclared path; the extractor refuses to return while a declared reference went unread | `dd42ea1` |

## Prefix-shippability (§F.5-G)

*(Suite figures below — Test Files/Tests — re-confirmed at `dd30e3f` (pre-merge scrutiny pass: comment, knowledge-base and CHANGELOG edits only since the `5ad16e6` Verification Gate).)*

```
$ npm run build
  12 partial(s) skipped (no output-dir:)
  16 host(s) to compile:
  …
  compiled: src/assets/mds/git/_references.mds → dist/skills/git/references/ (3 file(s))
  compiled: src/assets/mds/tracker/_github.mds → dist/skills/git/references/ (10 file(s))
MDS: 16 compiled, 0 error(s), 0 warning(s)
  copied:  src/assets/commands/release.md → dist/commands/release.md
MDS commands build complete!
build exit=0

$ npx tsc --noEmit
tsc exit=0

$ npm test
 Test Files  130 passed (130)
      Tests  4541 passed (4541)
   Duration  43.94s
EXIT=0

$ npm run test:integration
 Test Files  5 passed (5)
      Tests  50 passed (50)
   Duration  38.49s
EXIT=0

$ npm pack --dry-run
npm notice total files: 394
npm notice package size: 1.0 MB / unpacked 3.3 MB
13 generated skill reference files packed (dist/skills/git/references/**)
exit=0

$ git status --porcelain
(empty)
```

**Zero failures.** Every red this phase declared is now green: the git-agent golden (red since T1's `17ffa60`) and the three `github-status-lines` derivation `it`s (red since T2b). No flake re-run was needed — none of the known load-sensitive tests (hud-render, capture-hooks memory-worker, compliance-e2e S16b, eager-memory-refresh, redact-secrets, ledger-ops, shell-hooks, decisions-usage-scan, goldens `--out-dir` refusal) failed. Nothing was excluded or skipped.

**No threshold lowered** (AC-2.3 / H3, belt-and-braces). `git diff main...HEAD -- tests/`, every numeric literal that decreased:

```
DECREASED  GIT_AGENT_BYTES:  66180 -> 56075     (regeneration commits 2e019a5, 10ac94c, then the resolve-wave Mechanics-pointer condensing pass at c0b9860)
DECREASED  GIT_MD_CHARS:     65677 -> 55664     (same commits)
DECREASED  GIT_MD_LINES:       992 -> 913       (same commits)
DECREASED  SKILL_GIT_CHARS:   9205 -> 6581      (T2a 82d8b86, same commit as the SKILL.md cut)
DECREASED  SKILL_GIT_LINES:    283 -> 213       (same commit)
DECREASED  FIXTURE_BYTES:    17914 -> 17709     (re-capture commit e4876e0)
raised     FIXTURE_NEWLINES:   246 -> 249
```

All six are **equality baselines**, each moved in the same commit as the file it measures — the only sanctioned moves. No floor was lowered: `partial-count` 11→12, `issue-capture-contract-size` 3→6, and four new entries (`generated-reference-manifest-size` 13, `packed-reference-manifest-size` 13, `git-agent-guard-count` 68, `issue-pr-link-forwarding-sites` 14). No budget constant was touched after it was pinned. `toMatchSnapshot` remains at **0** occurrences repo-wide.

**Snyk: no scan was run.** The Snyk MCP server has failed to connect for every subtask of this branch (`ENOENT … snyk-macos-arm64` — the local wrapper binary is broken, a known issue). Not retried.

## Reviewer Focus Areas

1. **The per-op retained/moved table above** — the Phase-2 gate deliverable. Verify each op's contract lines stayed and **only mechanics moved**, and check each guard's declared extraction mode.
2. **The whole-branch golden diff (`git diff main...HEAD -- tests/fixtures/golden/git-agent.md`), read line by line.** Not either regeneration commit alone — the golden was regenerated twice. The classification is above; the claim is that every hunk is a pure move or one of the 29 named exemptions. Anything else in that diff is a defect.
3. **The containment oracle's zero-unaccounted-lines result**, including the moved `github-api.md` and `SKILL.md` lines, and the exemption list's second arm (no entry may cover a range that is in fact still contained).
4. **The D11 floor is still `>= 8`**, the literal `8` untouched, and no numeric literal decreased anywhere under `tests/` except the six baselines listed above.
5. **AC-2.13** — no surviving `D{N}` label lacks its definition.
6. **The overlay's shadow-independence, atomic per-unit swap and prune behaviour.** AC-2.11 makes UAC-28 a **release blocker**.
7. **The two accepted deviations and the option-A re-captures** — each is written above so it can be rejected explicitly rather than discovered.

## Release readiness

- **AC-2.11 — release blocker.** UAC-28 (a shadowed `devflow:git` must still receive the canonical GitHub references) is a release blocker, not merely an AC. **AC-2.4a is GREEN** — shadow SKILL.md kept, all ten `tracker/github/*.md` byte-equal to the generated source, and all 13 byte-equal on the canonical branch.
- **Two new install-time failure modes**, both reported rather than silent:
  1. *A unit that could not be refreshed* — the previously installed files are left byte-unchanged and the install summary warns `Could not refresh the generated references for "{provider}" ({error}) — the previously installed files were left unchanged`. The install still succeeds (PF-009).
  2. *A declared reference absent from the build* — the one throw path in the overlay: `Generated skill reference not found for declared reference "{relPath}": {absolute}. Run \`npm run build:mds\` to regenerate dist/skills/git/references/ before install.` Shipping an installer that silently omits mechanics the agent is instructed to load would move the failure to every user's first spawn.
- The DEGRADED reason `tracker mechanics unavailable` is **reachable by design** from P2-S14; its runtime arm is Phase 3 (P3a-S14).
- **Phase 2 delivers no user-facing value.** Record it in the release notes as an internal refactor.

## PR checklist (D-B)

- [x] Test-inventory table present, with a red proof per guard
- [x] `CHANGELOG.md` `### Changed` enumerates the internal changes
- [x] Prefix-shippability command sequence run and pasted
- [x] Release-blocker items named in the release-readiness section
- [x] `_preamble.mds:75` **NO-CHANGE** disposition recorded (the disposition line *is* the deliverable; no code edit and no test — a faked mechanical assertion for a process-only row is what the plan forbids)
- [x] `dynamic-build.mds:466-474` **RETAIN** disposition recorded and pinned mechanically
- [x] Clause (ii)'s live five-command walk-through (`/plan → /implement → /code-review → /resolve → /release`) is **formally waived per issue #321 (decision 2026-09-13)**. Its mechanised file-residue half runs in `tests/integration/clause-ii-file-residue.test.ts` and is green.
- [x] Clause (iv) is **REPLACED** by the option-A statement above.
- [ ] Snyk scan — **not run**; the Snyk MCP server failed to connect (`ENOENT … snyk-macos-arm64`). Not retried.

## Follow-ups

- **Nine pre-existing D11 bypasses in `src/assets/skills/git/references/github-api.md`** — generic `gh pr create --body` / `-f body=` examples that predate D11, in a file the Git agent loads on the `fetch-review-threads` path. They are loadable instruction text that shows an agent how to post an unscrubbed body. Frozen by exact text in `KNOWN_GITHUB_API_INLINE_BODIES` so the list can only shrink; filed as #340.
- **SG-7's second, larger `git.md` cut** (8 PR/review op bodies, 381 L / 25,175 ch) is **excluded by design** from every phase of this feature, with SG-8's constraint quoted verbatim: `post-review-summary` / `post-resolution-summary` mechanics may move **only** in a PR that moves their guards, never as a size optimisation.
- #342 (devflow-wide prompt diet — depends on this PR merging; related #326, #333)

### Also fixes #340 — D11 inline-body recipes in `references/github-api.md`

The nine inline-body recipes (`gh pr create … --body "…"`, `gh pr review … --body "…"`, `gh api … -f body=…`, the GraphQL reply mutation) that Phase 2 had frozen by exact text in `KNOWN_GITHUB_API_INLINE_BODIES` are now rewritten to the scrub-then-post chain already used elsewhere in the file: compose to `$DEVFLOW_BODY_RAW` → `redact-secrets.cjs` → `&& gh … --body-file "$DEVFLOW_BODY"` / `-F body=@"$DEVFLOW_BODY"` (release notes stay on `$DEVFLOW_NOTES` / `--notes-file`). A head-of-file D11 note mirrors the existing D4 note and defers to git.md's `## Comment-sink scrub (D11)` — the recipes implement the rule, they do not compete with it. The `# VIOLATION:` teaching samples keep only the defect each one teaches.

- `KNOWN_GITHUB_API_INLINE_BODIES` is now empty. Both guard arms remain and are named collectors (`collectUndeclaredOffenders`, `collectStaleExclusions`) driven by the main guards and by known-bad probes seeded with synthetic offenders — including one at a sibling reference path to prove the file-scoping half — so the reverse arm is non-vacuous over an empty list (PF-018, ADR-024). The guard's matcher and corpus are described under `### Also fixes #341` below (ADR-025: widened in the same commit as the content it catches). `it(` count in `tests/git-agent.test.ts` is 77 (floor 73).
- Eleven `CONTAINMENT_EXEMPTIONS` rows for the rewritten `github-api.md` baseline lines (88, 91, 313, 328, 334, 336, 504, 541, 545, 552, 638); baselines and goldens untouched; no ceiling raised. `github-api.md` is 17,259 chars (was 15,812) — a recorded, non-gated byte-budget row (`D-LOADED-SET-SCOPE`).
- Quality gates: Validate PASS (4485/4485), Simplify, Scrutinize (fixed one P0: the head note paired release notes with the wrong file — now `$DEVFLOW_NOTES` ↔ `--notes-file`), Evaluate ALIGNED 6/6, QA PASS 7/7 including two mutation checks (an injected inline body and an injected stale exclusion each turn exactly one arm red).
- #341 is fixed in this PR — see `### Also fixes #341` below.

Commits: 34b6346, c9180f2, f45d2b6, ed9b494, a715851 (KB refresh).

### Also fixes #341 — the six inline sinks the D11 guard could not see

- The D11 inline-body guard in `tests/git-agent.test.ts` joins backslash-continued commands before matching, names five shapes (`long-flag` `--body|--notes|--comment`; `short-flag` `-b/-n/-c` on posting verbs; `api-field` `-f/-F/--field/--raw-field body=`; `unscrubbed-file` `--body-file/--notes-file` with any argument other than the quoted scrubber variable; `unscrubbed-api-file` `-F body=@` likewise), and scans every installed agent (dist-first), every generated tracker/git reference, every skill file, every compiled command and every rule (~225 files). Three probes prove each arm live: a shape-table probe, a known-bad probe over the never-regenerated 101bda7 baselines (17 hits in `baseline/github-api.md`, 1 in `baseline/SKILL.md`), and a corpus-reach probe on sentinel paths. `KNOWN_GITHUB_API_INLINE_BODIES` stays `[]`; `git-agent-guard-count` floor 68 → 73.
- RED proof at `a715851`: the widened collector found exactly six inline sinks — `ensure-traceable-issue.md` `gh issue create … --body`; `manage-debt.md` `gh issue close … --comment` and `gh issue create … --body`; `skills/git/references/patterns.md` `gh pr create … --body`; `skills/review-methodology/references/patterns.md` `gh api … -f body=`; `skills/git/references/github-api.md` `--notes-file CHANGELOG.md`. All six post the scrubber's output at this head. The tech-debt archive is one `&&` chain (scrub → create → one scrubbed archive comment carrying the real successor number → bare close, no comment body), and the two `gh … create --json number` defects (a flag neither command has) are gone.
- The review-methodology skill holds no posting recipe: reviews write reports; publication is the Git agent's `post-review-summary` under D10 and D11.
- The D11 contract in `git.mds` names a comment attached to a close as a posted body (provider-neutral clause, +49 ch; one fixture-only golden regeneration, `65e5470`).
- No `github-status-lines.txt` re-capture was needed: the fixture samples `manage-debt.md` lines 11–18 and `ensure-traceable-issue.md` lines 15–19; the sinks sit at 54–70 and 26–46. Eight `#341.` containment exemptions (baseline `github-api.md` 149, 153, 189, 191, 193, 196–200, 257, 259) bring the total to 48.
- Gates: full suite 130 files / 4,541 tests green (at `5ad16e6`); loaded set 77,719 vs 77,824; mutation checks confirmed each guard arm goes red on exactly the named file/shape.

**Shape 2b, confirmed 2026-09-15.** The cross-cutting glossary `references/decision-markers.md` (1,681 chars) is an on-demand lookup, not a per-spawn load — the core agent inlines D4 and D11 because those must be loaded before the agent acts and says every other marker "is defined in" the glossary — so the classification `D-CROSS-CUTTING-ON-DEMAND` in `tests/tracker/byte-budget.test.ts` stands, the 78,824 figure remains a recorded row, and the per-spawn ceiling stays as a regression alarm that is lowered after each condensing pass (the devflow-wide diet is #342).

### Also fixes: fence-aware section extraction and the post-wave-report containment gap (PF-063, AC-0.10)

- `extractOpSectionFromCorpus` (tests/helpers.ts) terminates a section only at a `## ` line outside a fenced code block (backtick or tilde fences; an unclosed fence runs to end of text); `collectUnfencedH2` owns the rule and `tests/tracker/reference-structure.test.ts` asserts zero stray unfenced `## ` across all 13 generated references, with known-bad probes in both directions. Census: 20 column-0 `## ` lines, 13 file headings, 7 fenced (`manage-debt.md` `## Items`; `ensure-traceable-issue.md` heredoc and D3 template lines), zero stray. New floor `min-fenced-h2` = 7. Live probes: the extracted `manage-debt` section reaches `gh issue close "$old_issue"`; `ensure-traceable-issue` reaches the create recipe and the D3 template.
- Former file-scoped workarounds in `tests/git-agent.test.ts` (setup-task, fetch-issue, learn-conventions arm b, the `## Issues Batch` header guard) use op-scoped extraction; the seam test's `collectMissingProducers` stays body-scoped because its probes mutate the body under test.
- Guard 10 (AC-0.10 containment) is strictly op-scoped. Making it so exposed that `post-wave-report`, the last op in git.md, had satisfied set (b) only through the shared `## Principles` trailer swept in by a file-scoped slice (true in the pre-split baseline too). Its step 2 now carries `- The wave report MUST NOT reproduce verbatim `<external-thread>` or `<untrusted-issue-body>` content (Principle 8).` (+120 chars in the core agent, not the per-op reference — PF-027). Source commit `667c497` red on exactly the golden byte-equality; fixture-only `ce491f9` regenerated the golden.
- Branch aligned with main by merge commit `10ea0d5` (origin/main `33b730e`, PR #338); no conflicts.
- Knowledge bases `test-harness` and `tracker-references` refreshed in `031e1bd`.

### Resolve wave (cycle 1, 2026-09-15/16)

- **Review `2026-09-15_2146`** — ten focus reviewers, 109 findings triaged: 3 escalated (decided by the user), 73 FIX_NOW, 1 BY_DESIGN, 7 FIX_SEPARATE, 25 duplicates. All 76 fixable findings (73 FIX_NOW + 3 escalated) are fixed, across 33 commits.
- **Escalation decisions:**
  1. The commands no longer claim a Git-agent grammar re-check — the Code agent's `ISSUE_PR_LINK` re-check is the only gate (immaterial once the `$ISSUE` sink is quoted).
  2. The wave round refresh names `fetch-issues-batch`, state fields only, and the op projects `state`.
  3. The tracker input contract states the merged-step-order rule.
- **Headline fixes:**
  - Every compose→scrub→post recipe is one `&&` chain.
  - `manage-debt` appends to the backlog body, so the size check and archive path are live, with the successor number validated before promotion.
  - `github-api.md` has one D4 stop-and-report spelling, fail-closed rate probes, observable batch truncation, a self-contained release recipe, and every expansion quoted.
  - The installer overlay reports four real end states, keeps the `.old` recovery copy when a restore fails, fails loud when the compiled tree is absent, stages process-uniquely under the converged subtree, and bounds `chmodRecursive`.
  - The manifest derivation and skill name live in core; `HostPlan`/`VariantSection` are typed totally.
  - The section extractor anchors both ends through the fence index, `gitOp()` derives through it (byte-neutral); fence grammar is probed and unclosed fences are asserted absent.
  - Guard census counts only live `it(` and refuses skipped/focused spellings; provider detectors are word-bounded; the heredoc matcher covers the shell grammar.
  - CHANGELOG/KB figures are corrected.
- **7 deferred findings** tracked on #23.
- Full resolution summary at `.devflow/docs/reviews/feat-324-tracker-phase-2-contract-mechanics-split/2026-09-15_2146/resolution-summary.md` (local artifact).

## Related Issues

Closes #324
Closes #340
Closes #341
Tracking: #321
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant