Skip to content

Tracker Phase 2 — contract/mechanics split (GitHub only) #324

Description

@dean0x

Initial Request

src/assets/agents/git.md is 938 L / 59,376 ch [verified @ e726874], re-sent on every Git spawn (PF-026: a shared agent prompt file is billed PER SPAWN). Roughly 9,400 of those characters are GitHub-specific mechanicsgh invocations, header names, rate-limit detectors — interleaved with the provider-independent contract (each op's **Input:**, its **Output:** template, its **Degradation (D4):** clause, the D4/D11 invariants).

Three live consequences:

  1. PF-023 has ~30 filename-composition sinks instead of one convergence point (GAP-10, CRITICAL). There is no single place where a provider is resolved, so any future provider token would have to be threaded through every sink.
  2. The always-loaded D4/D11 cross-cutting text is GitHub-specific while §F.1 declares it provider-independent (GAP-03, CRITICAL) — two authorities on the secret-redaction path. This is the same shape as the live D9 divergence Phase 0 repaired.
  3. src/assets/skills/git/SKILL.md (283 L / 9,236 ch) carries two live safety contradictions: :196 if [ "$REMAINING" -lt 10 ]; then sleep 60; fi [verified] directly contradicts D4's STOP-the-fan-out rule at git.md:25; and :211 gh release create … --notes "$NOTES" [verified — [drift: plan-1, plan-2 and §B.2 all cite :212; the live line is :211]] is an inline-body recipe where create-release mandates --notes-file after a scrub whose failure is a HARD fail. Both are invisible to every existing guard: INLINE_BODY_RE (tests/git-agent.test.ts:412 [verified]) matches only gh (?:pr|issue), and GIT_AGENT_PATH scans only git.md.

Phase 2 splits contract from mechanics for GitHub only, moving mechanics into generated per-op skill references addressed skill-relatively, adds a ≤40-line provider-resolution preamble at git.md:29 [verified: :29 is the blank line after the D4 block :22-28 and before ## Publication gate (D10) at :30], replaces five divergent inline issue-parse rules in the command layer with one neutral vocabulary partial, and installs the generated references with a converge-not-merge staged swap that a shadow cannot subvert.

Honest user-visible-change statement (the phase label)

No change to any GitHub-rendered artifact. Tracked = #{n}, Depends on: #{n}, 42-jwt-auth.{ts}.md and issue: 42 are byte-identical to Phase 0 under github, and the frozen github-status-lines.txt fixture is untouched. New files appear under the installed skill directory (~/.claude/skills/devflow:git/references/tracker/github/); no new file appears in any user's project tree and no new prompt is asked. No Jira or Linear literal exists anywhere yet.

Phase 2 therefore delivers zero user-facing value and introduces two new failure modes (an overlay that can partially fail; a generated reference that can be absent at runtime) — GAP-45. That is recorded deliberately, not hidden: the release-coupling decision is in §2, release readiness.

Execution: SEQUENTIAL_CODE_AGENTS · context-risk: CRITICAL · Design artifact: .devflow/docs/design/tracker-phase-2.2026-09-05_1155.md (authoritative; local, gitignored — a 60k-truncated copy is posted as a collapsed comment).

Product Requirements

Depends on

Phase 1 (#323) merged to main. Tracking issue: #321.

Acceptance criteria

  • AC-2.1 — the golden diff contains only the intended text moves; the frozen status-line fixture unchanged [DR-27(b)] the frozen-fixture half stays mechanical; the "only intended moves" half is bound to the containment-test exemption list created in P2-S6 ([DR-17]) — an asserted-non-empty, per-range justified list of deliberately rewritten line ranges
  • AC-2.2 — containment passes with zero unaccounted lines (incl. moved github-api.md/SKILL.md lines)
  • AC-2.3 — the D11 guards stay green with the floor at >= 8 over the joined corpus, not lowered, and fail on a known-bad sample [DR-18] the D11 forward guard extracts with mode 'union' over the joined corpus, named explicitly at the call site
  • AC-2.4a — a shadowed devflow:git receives the canonical GitHub references
  • AC-2.4bREWORDED [DR-05]: an unreadable generated reference ⇒ that provider's directory is left unchanged, InstallReport.overlayFailures names the provider, and the install succeeds; an absent canonical GitHub reference ⇒ loud failure with a build hint — the provider directory is atomic: a per-file failure aborts that provider's swap, rms the tmp tree, and pushes the provider onto overlayFailures — it never renames a partial tree over a good one
  • AC-2.4c — a shadow-supplied file under references/tracker/** not in the manifest is absent after install
  • AC-2.5 (re-derived, §F.4)bytes(dist/agents/git.md) ≤ 49600, bytes(skills/git/SKILL.md) ≤ 6600, loaded set ≤ 71562 on the worst-case tracker spawn, each a named constant with its derivation in a comment, evaluated as a four-shape table test; the only reference-naming line outside a tracker-op section is the single preamble load instruction [DR-12] the loaded-set formula gains + max over ops of (sum of every reference file that op's load instructions can name in one spawn), with a bidirectional structural check. [DR-13(a)] the preamble ceiling is promoted into this AC: "the preamble at git.md:29 is ≤40 lines". [DR-27(c)] the second clause gets its own assertion: "exactly one line in dist/agents/git.md names a references/tracker/ path, and it is inside the preamble", non-vacuous against a seeded second naming line
  • AC-2.6 — all git-agent.test.ts guards green and the guard count has not decreased; registry Guard 6 green with op names unchanged [DR-20] the guard-count accounting explicitly records the D10 scope guard's successor pair so the replacement is visibly not a net loss
  • AC-2.7 — every generated Phase-2 reference line is reachable on the gh path; no _mcp.md exists after a GitHub-only build; no Jira/Linear literal anywhere amended to the positive form: every generated Phase-2 reference line is reachable on the gh path; after a GitHub-only build not exists('references/tracker/_mcp.md') and '_mcp.md' not in read(references/tracker/github/{op}.md) for every op. Per Gate-2 D-D (clause (iii) read per phase), _mcp.md is NOT generated in Phase 2the guard text is re-scoped in 3a-4 (Conflict C5 / H7); Phase 2's wording is the correct Phase-2 form
  • AC-2.8 — the retired-wording grep is clean against the Phase-2 allowlist
  • AC-2.9_tracker.mds ships the two-armed GitHub foreign-shape rule — ships two-armed from day one so no single-arm guard ever exists (clause (iii))
  • AC-2.10Tracked = #{n}, Depends on: #{n}, 42-jwt-auth.{ts}.md and issue: 42 byte-identical to Phase 0 under github
  • AC-2.11 — UAC-28 (shadow overlay) is a release blocker, not merely an AC — Gate-2 D-B: the test is AC-2.4a; the "release blocker" label is a process label recorded in the release-readiness subsection — the label is (process-only — PR checklist)
  • AC-2.12 (new)collectSkillRefFiles is recursive, so the references/tracker/** guards are non-vacuous — lands in T1, in the same commit that creates the subtree (Conflict C7)
  • AC-2.13 (new) — the D4 and D11 legend rows survive in git.md; no surviving label lacks its definition — asserted as a set relation, not per-row
  • Prefix-shippability — After phase N: (i) npm run build && npm test && npm run test:integration && npm pack --dry-run are green; (ii) a fresh install from the tarball drives /plan → /implement → /code-review → /resolve → /release on a GitHub project with no new prompt and no new file in git status; (iii) every artifact in the tree has at least one reachable consumer in this phase's end-state — no file, define, list element, frontmatter key or conditional arm exists solely for a later phase (ADR-003); (iv) the frozen github-status-lines.txt fixture is byte-identical to its Phase-0 capture.

Subtasks

# Domain Owned files (with anchors) Steps Depends on Gate before next Handoff contents
T1 build script / prompt assets / tests scripts/build-mds.ts (flat (module, op) list, N ≥ 8 from its first commit) · new src/assets/mds/tracker/_github.mds · src/assets/agents/git.mds preamble insert at :29 (≤40 lines) · src/core/mds-variants.ts (expandVariants introduced here, [DR-16]) · src/core/assets.ts (compiledSkillRefsDir()) · new tests/tracker/byte-budget.test.ts · tests/mds-variants.test.ts (the expandVariants describe moved in from Phase 1) · tests/skill-references.test.ts:96-108 (collectSkillRefFilesrecursive) P2-S1, P2-S2, P2-S3, P2-S13 Phase 1 merged to main G2.1 the (module, op) manifest as shipped (N ≥ 8 names); the preamble's final line count against the ≤40 ceiling and byte count; the load-instruction literal verbatim, so T2 restates it identically per op; the four-shape table's named rows incl. learn-conventions.md and publication-gate.md ([DR-12]); the explicit statement that the golden is expected RED from here
T2 prompt assets src/assets/agents/git.mds (10 ops' **Process:** blocks; D4 :22-28 / D11 :43-59 invariant-detector split; legend :84-98 per E10; D10 :30-41; learn-conventions :517-565; the two new Output-block producer lines) · src/assets/skills/git/SKILL.md (:73, :115-127, :152, :192-199, :206-214, :232-251, :252-264, :265-273) · src/assets/skills/git/references/github-api.md (5 tracker sections) P2-S4, P2-S5, P2-S6, P2-S7, P2-S8 T1 G2.2 the per-op retained/moved table (a Phase-2 gate deliverable per GAP-01) — one row per op × {moved lines, retained lines, extraction mode 'union'/'sole' used by each guard that reads it ([DR-18])}; the containment-test exemption list with a justification per rewritten range ([DR-17]); the exact - **PR link line**: and - **Branch token**: Output-block literals now emitted by setup-task and fetch-issue (verbatim, for T3); measured bytes(dist/agents/git.md) and bytes(SKILL.md) against the pins ≤ 49,600 / ≤ 6,600
T3 prompt assets (commands + partials) new src/assets/commands/_partials/_tracker.mds · plan.mds · implement.mds · debug.mds · dynamic-build.mds (:73, :466-474, :522) · dynamic-plan.mds · _partials/_ticket_template.mds · _partials/_wave.mds · _partials/_engine.mds:232 · _partials/_preamble.mds:31, :75 · src/assets/agents/code.md (:35, :93, :95, :99) · src/assets/skills/docs-framework/SKILL.md (:45, :106, :144) · resolve.mds (:244, :352, :499, :508, :539, :619) · plan.mds (:350, :404, :440) P2-S9, P2-S10, P2-S11, P2-S12 T2 (producers land in T2's Output blocks; T3 wires the consumers — Conflict C11) G2.3 the five hosts that adopted _tracker.mds and their @import line numbers; confirmation that dynamic-build.mds:522's marker literal is gone and build-mds.test.ts:853 was replaced by the negative guard; the two disposition lines (_engine.mds:232 guard-with-test; _preamble.mds:75 explicit NO-CHANGE)
T4 installer + CLI src/targets/claude-code/installer.ts:571 + :37-45 · new recursive path-keyed prune (beside src/core/orphan-sweep.ts) · src/cli/commands/init.ts:1911 (formatOverlaySummary) · new tests/installer/reference-overlay.test.ts · tests/installer-new.test.ts (error-text repoint only) P2-S14 T1 (needs the generated tree in dist) G2.4 InstallReport's new field names (overlaidRefs, overlayFailures) and the render site; the manifest path the prune converges against; the atomic-provider-swap contract ([DR-05]) as implemented
T5 tests + docs tests/git-agent.test.ts (corpus follows the moved text) · tests/build-mds.test.ts · new tests/tracker/containment.test.ts · new tests/dynamic/depends-on-grammar.test.ts · new tests/guards/capability-hoist.test.ts ([DR-11] — renamed from identity-hoist) · widened INLINE_BODY_RE · the negative-guard battery · then, in its own commit, tests/fixtures/golden/git-agent.md regenerated · docs sweep P2-S15, P2-S16, P2-S17 T2–T4 G2.5 — (terminal)

Gates

Gate Must be green before Assertion
G2.1 T2 starts npm run build exits 0 with the new (module, op) list; the preamble compiles into dist/agents/git.md and is ≤40 lines; collectSkillRefFiles is recursive and sees the new subtree; expandVariants has ≥ 8 real pairs. The golden is expected RED from here — record the expectation explicitly in the handoff so no agent "fixes" it.
G2.2 T3 starts Containment: zero unaccounted lines across git.md ∪ dist/skills/git/references/** incl. the moved github-api.md / SKILL.md lines (AC-2.2), with the exemption list asserted non-empty and justified. Byte-budget table green: ≤ 49600 / ≤ 6600 / loaded set ≤ 71562 (AC-2.5). D11 floor >= 8 over the joined corpus in mode 'union', not lowered (AC-2.3). AC-2.13 (D4/D11 legend rows survive).
G2.3 T4 starts _tracker.mds two-armed rule present (AC-2.9); Tracked = #{n}, Depends on: #{n}, 42-jwt-auth.{ts}.md, issue: 42 byte-identical under github (AC-2.10); frozen status lines unchanged.
G2.4 T5's golden-regeneration commit AC-2.4a/b/c all green (shadow overlay, atomic unreadable-reference isolation, injected-file prune); the references/tracker/** guards proven non-vacuous (AC-2.12).
G2.5 PR opens Full prefix-shippability (i)–(iv); git-agent.test.ts guard count has not decreased (AC-2.6); the golden regeneration is a standalone commit reviewed as a text diff.

/implement spawn notes

  • One Code agent per subtask, strictly sequential; each verifies with npm run build && npm test && npm run test:integration && npm pack --dry-run before handing off (R6: every commit is green on its own).
  • T2 is spawned with the per-op commit instruction as its default (D-G(a)) — 10 pathspec-scoped commits, containment test after each. gather-release-evidence is two commits (A then B) per [DR-17], so T2 lands 11 commits.
  • Every prompt carries the H2 standing instruction and the "golden expected RED" statement verbatim.
  • Every prompt carries §14's normative appendix inline. Do not pass a path to this artifact as a substitute — .devflow/docs/ is gitignored.
  • CREATE_PR: false and PUSH: false on T1–T4; CREATE_PR: true and PUSH: true on T5, whose terminal act (after the golden-regeneration commit) opens the PR at G2.5. PUSH is a Code-agent input (code.md:34) that implement.mds's spawn template does not carry — state it explicitly in each spawn prompt.
  • HANDOFF_REQUIRED: true on T1–T4; each writes .devflow/docs/handoff-{branch_slug}.md with the Handoff-contents column of the §6 table; T5 reads it.

Execution strategy

SEQUENTIAL_CODE_AGENTS — 5 subtasks (T1 → T5). Context risk: CRITICAL.

Largest phase by volume (~40 files, ~2,500–3,500 lines) across four distinct domains, bound by one invariant that spans all of them: the containment test's zero unaccounted lines across git.md ∪ generated references.

Two parallel candidates were evaluated and rejected:

  • Installer overlay (TS) ∥ prompt split (assets) — write-disjoint, but the overlay's tests install from dist/skills/git/references/tracker/**, which only exists after the split's build manifest lands. Read closure overlaps completely.
  • Command layer (_tracker.mds + 5 hosts) ∥ git.md op split — write-disjoint, but both mutate tests/build-mds.test.ts (partial manifest, escaped-brace guard, DIST_FILES scope) and both sit inside the seam test's and the frozen fixture's corpus.

Plus the feature-wide disqualifier (PF-020 closure rule, DR-23 ALREADY-ADDRESSED): npm run build is rm -rf dist && … (package.json:20) in a shared worktree, and /implement has no worktree isolation (grep for worktree in implement.mds returns zero hits). Every Code agent's verification step deletes the shared dist/ while a sibling is mid-suite, and Phase 0 deliberately makes every dist-scanning guard fail loud on a missing directory (R3 / AC-0.16). Any concurrent pair manufactures nondeterministic red. This is a tooling limitation, not a plan limitation, and it holds for all four phases.

PR title

refactor(git-agent): split tracker mechanics into generated GitHub references

Implementation Plan

Design artifact (authoritative): .devflow/docs/design/tracker-phase-2.2026-09-05_1155.md — local and gitignored. A copy truncated at the 60k comment cap is posted as a collapsed comment: #324 (comment). Run with /implement .devflow/docs/design/tracker-phase-2.2026-09-05_1155.md. Tracking issue: #321.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureFeature request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions