Skip to content

Tracker Phase 0 — harness first + live-defect fixes #322

Description

@dean0x

Initial Request

Devflow's command→agent seam for issue handling is untyped and currently broken in five user-visible ways, and the test harness that Phases 1–3 will lean on does not yet exist in a form that survives an agent rename. /plan #42 parses an issue reference and never fetches it; /debug #42 passes a key (ISSUE:) that the fetch-issue op does not declare; three command-layer sites still invoke or describe gh issue view directly; resolve.mds:378 states a D9 thread-resolution rule that contradicts git.md:96's single authority; and release.md:138 promises a post-release "close milestone" step that does not exist. Phase 0 fixes those defects and lands the harness — the shared dist-preferred agent-source resolver, the two-sided seam test, the D11 guard-class union, the two goldens, and test:integration in CI — against the fixed corpus, so that a red guard in any later phase is unambiguous evidence of a regression rather than of a pre-existing defect.

Phase label (honest, verbatim): "no change to the GitHub rendering of any existing traceability artifact."

This label is deliberately narrower than "no behaviour change". Phase 0 is a fix, not a refactor, precisely because it does change five user-visible behaviours. Claiming otherwise would be the untruthful-traceability-claim defect this phase exists to delete (intent #22).

The five user-visible changes (enumerated with before/after in the issue body and CHANGELOG.md ### Fixed, AC-0.15):

  1. /plan #N now actually fetches the issue (it previously consumed a body it never fetched).
  2. /debug's issue path works for the first time (ISSUE:ISSUE_INPUT:).
  3. dynamic-plan issue bodies are now wrapped <untrusted-issue-body> at the sink.
  4. resolution-summary.md's Tracked = (pending) now names a reason.
  5. release.md's untruthful "close milestone" claim is deleted.

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

Product Requirements

Depends on

none — first phase (branch from main). Tracking issue: #321.

Acceptance criteria

  • AC-0.1 — seam test red on debug.mds before, green after; covers Git and Code fences both directions; excludes Produces/Requires; ≥1 fence scanned per agent type
  • AC-0.2 — both goldens exist, captured after A1; npm run test:golden:update documented; CI never auto-regenerates
  • AC-0.3/plan #42OPERATION: fetch-issue; /plan #12 #15 #18OPERATION: fetch-issues-batch with ≤50 pinned, TRUNCATED ({n} not processed) beyond it, ## Issues Batch ({n} issues) asserted
  • AC-0.4 — no gh issue invocation or descriptive mention in dist/commands/*.md outside a Git spawn fence; the two gh pr view sites recorded as exceptions
  • AC-0.5resolve.mds D9 matches git.md:96 verbatim, pinned in source and dist/commands/resolve.md
  • AC-0.6 — tracker-unavailable manage-debt emits DEGRADED and resolution-summary.md keeps Tracked = (pending) with the reason named at all four sites; every REQUIRED_OP doing remote I/O has a D4 line
  • AC-0.7 — every agent-corpus scan asserts registry completeness through the shared resolver; no new guard contains a literal agent path
  • AC-0.8 — D11 guards scan the sink class with postingOps.length >= 8 from git.md alone and fail on a known-bad sample
  • AC-0.9github-status-lines.txt byte-identical before and after Phase 0
  • AC-0.10 — every op Output block rendering a remote-sourced field carries <untrusted-issue-body> (non-vacuous, ≥3 ops); no reply/summary template interpolates a remote body field
  • AC-0.11 (amended)fetch-issues-batch declares ISSUE_REFS at git.md:69 and git.md:292; plan.mds:32 renamed; fetch-issue keeps ISSUE_INPUT; debug.mds passes ISSUE_INPUT; both ops in REQUIRED_OPS; fetch-issues-batch out of INTERNAL_OPS with a rationale comment; no ISSUE_NUMBERS literal remains repo-wide, and ISSUE_NUMBER (singular) is untouched at all 14 sites
  • AC-0.12 — Guard 2 pins manage-debt: 60000 before the literal moves
  • AC-0.13 — test-inventory AC: each new guard named and proven non-vacuous with a red case (process-only — PR checklist) §0.10 / D-B
  • AC-0.14release.md:138 "close milestone" deleted
  • AC-0.15 — the phase label, issue body and CHANGELOG enumerate the five user-visible changes with before/after (process-only — PR checklist) §0.10 / D-B
  • AC-0.16 — every dist-scanning guard has a missing-directory case asserting a thrown error with a build hint
  • AC-0.17 — all existing suites green; no threshold lowered anywhere ... now mechanised [DR-27a]: tests/guards/numeric-floor-manifest.test.ts compares every pinned numeric floor in tests/** against a committed manifest and fails when any entry decreases (new entries allowed, decreases never). If the manifest guard is not landed in P0-S22, AC-0.17 falls back to (process-only — PR checklist) per D-B
  • 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.
  • Commit orderPhase 0's commit order A1 → A2 → A3 → A4 is itself an acceptance criterion. A2 (the golden capture) must contain nothing but the two fixtures, and must be captured from the post-A1 tree. A1 edits git.md; capturing first pins a tree Phase 0 immediately moves past (GAP-06 / B-9). Verified by reviewing the commit list on the PR: exactly four subtask commits in that order, A2's diff limited to tests/fixtures/golden/**.

Subtasks

# Domain Owned files (exclusive, with anchors) Steps Depends on Gate green before next Handoff contents
A1 prompt assets (commands, agents, partials) + hooks src/assets/commands/resolve.mds (:352,:378,:499,:508,:539) · debug.mds (:17,:24,:46,:51) · plan.mds (:27,:32, new Git fence inserted at #### Phase 1: Gate 0 before :63) · dynamic-plan.mds:103 · dynamic-build.mds:73 · _partials/_wave.mds (:4,:9) · implement.mds:73 · release.md:138 · src/assets/agents/git.md (:69,:268-284,:288-300,:292,:294-298,:300-318,:379-411,:723,:922) · src/assets/scripts/hooks/ensure-devflow-init:23 P0-S1 (prep) · P0-S2 … P0-S13 G0.1 post-A1 git.md SHA-256 + line count; the list of every line A1 changed in git.md (so A2's capture is spot-checkable); confirmation no test file was touched
A2 tests (fixtures only) new tests/fixtures/golden/git-agent.md · new tests/fixtures/golden/github-status-lines.txt P0-S14, P0-S15 A1 committed on the branch G0.2 the two fixture paths + byte counts + the explicit statement "these fixtures are immutable for the remainder of Phase 0–3; a mismatch means the source is wrong, never the fixture"
A3 tests + CI tests/helpers.ts · new tests/seams/command-agent-input.test.ts · new tests/goldens/git-agent-golden.test.ts · new tests/goldens/github-status-lines.test.ts · new tests/guards/agent-source-resolver.test.ts · new tests/guards/extended-references.test.ts · new tests/guards/retired-wording.test.ts · new tests/guards/numeric-floor-manifest.test.ts + its committed manifest [DR-27a] · tests/git-agent.test.ts · tests/registry-integrity.test.ts · tests/agent-name-guards.test.ts · tests/agent-frontmatter.test.ts · tests/skill-references.test.ts · tests/installer-new.test.ts · tests/build-mds.test.ts · tests/shell-hooks.test.ts (gitignore fixture only) · tests/init-logic.test.ts (gitignore fixture only) · .github/workflows/ci.yml:23 · package.json (test:golden:update) P0-S16 … P0-S25 A1, A2 G0.3 every new guard by name, its file, and its proven-red mechanism; the five user-visible behaviour changes with before/after strings; the two recorded exceptions (release.md:85 conventions read; gh pr view at code-review.mds:76-78 / bug-analysis.mds:43-45); the mode ('union' / 'sole') each extractOpSectionFromCorpus consumer names [DR-18]
A4 docs new docs/reference/platform-assumptions.md · .devflow/features/dynamic-workflow-engine/KNOWLEDGE.md · CHANGELOG.md · the seven drifted citations in .devflow/features/*/KNOWLEDGE.md · the Phase-0 issue body P0-S26 A1–A3 (sweep the branch's final tree, not the commit — PF-025) G0.4 — (terminal)

Gates

Gate Must be green before Command / assertion
G0.1 A2 starts npm run build && npm test — full suite green at the post-A1 tree. No new test exists yet; this proves A1 broke nothing.
G0.2 A3 starts tests/fixtures/golden/git-agent.md byte-equals src/assets/agents/git.md (verify with cmp, not by reading); github-status-lines.txt non-empty and every extracted literal present in the source at the cited line.
G0.3 A4 starts npm run build && npm test && npm run test:integration green; each new guard demonstrated non-vacuous with a recorded red case (AC-0.13); postingOps.length >= 8 satisfied by git.md alone (AC-0.8); resolveAllAgents() ⊇ getAllAgentNames() = 16 (AC-0.7).
G0.4 PR opens Prefix-shippability sequence (§14) + github-status-lines.txt byte-identical to its A2 capture (AC-0.9) + git status --porcelain empty.

/implement spawn notes

  • Spawn one Code agent per subtask, in order: A1 → A2 → A3 (optionally A3a → A3b) → A4. Never two concurrently.
  • CREATE_PR: false on A1, A2, and A3 (and A3a when the split is taken); CREATE_PR: true on A4, the terminal agent, which opens the PR with the complete test-inventory table and the RED proofs from all subtasks (implement.mds:409). A4 must therefore receive every prior handoff.
  • HANDOFF_REQUIRED: true on every subtask but the last (A1, A2, A3 — and A3a when the split is taken). A4 is terminal and produces no handoff.
  • Every Code agent reads .devflow/docs/handoff-{branch_slug}.md via HANDOFF_FILE; each writes its handoff contents (column 7 above) after its own gate passes, never before.
  • Commit with an explicit pathspec of the subtask's owned files only: git commit -- <owned paths> (PF-020's validated cheap mitigation; per-commit provenance survives the squash-merge).
  • Standing instruction in every Code prompt: "a golden mismatch means the source is wrong, never the fixture." (H2.)

Execution strategy

SEQUENTIAL_CODE_AGENTS — 4 subtasks (A1 → A2 → A3 → A4). Context risk: HIGH.

Rationale — three independent forcings (any one sufficient)

  1. Commit order is itself an acceptance criterion (§F.5-I). A1 edits git.md; capturing the golden first pins a tree Phase 0 immediately moves past (GAP-06). A single Code agent has no mechanism that guarantees commit ordering inside one run; sequential agents make each ordinal a spawn boundary.
  2. A2 must touch nothing else. The golden capture is a byte-exact 59,376-character copy plus a hand-extracted frozen fixture. Interleaving it with any behaviour change reproduces the exact defect GAP-06 exists to prevent, and the "never regenerate alongside a behaviour change" rule (B-05) is unenforceable inside one agent's context.
  3. Working set. ~30 files / ~1,400–1,900 changed lines spanning four unrelated domains (prompt text, fixtures, test harness, docs). A3 alone requires simultaneous knowledge of 12 test files' internal conventions.

Why PARALLEL_CODE_AGENTS is rejected (feature-wide, PF-020 closure rule)

PF-020's 2026-08-30 extension binds — "disjointness has to hold over each agent's READ/EXECUTE closure, not merely its WRITE set." Three facts collapse every candidate partition:

  1. npm run build is rm -rf dist && … (package.json:20). Every Code agent's verification step deletes and rebuilds the one shared dist/. A sibling agent mid-suite observes a missing dist tree — and Phase 0 deliberately makes every dist-scanning guard fail loud on a missing directory (AC-0.16, R3). Parallel agents therefore manufacture nondeterministic red suites that look exactly like real regressions.
  2. /implement has no worktree isolation — grep for worktree in implement.mds returns zero hits. All Code agents share one worktree, one git index, one dist/, one node_modules, one vitest run.
  3. The test corpus spans every partition. The seam test, the D11 sink-class guard, the containment test and the frozen fixture all read git.md ∪ dist/commands/*.md ∪ dist/skills/git/references/** — the union of every proposed subtask's outputs. Disjoint write sets, 100% overlapping read closure.

This disqualifier is independent of write-set disjointness, so it holds for all four phases. It is a tooling limitation, not a plan limitation [DR-23, ALREADY-ADDRESSED].

PR title

fix(traceability): repair issue-seam defects and land the tracker test harness

Implementation Plan

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

Test Inventory (AC-0.13)

Each new guard landed in Phase 0, named with its file and proven non-vacuous via a known-bad inline sample (mechanic 2, per H10).

Guard File What it guards Non-vacuity red case (mechanic 2)
Command→agent seam: forward check tests/seams/command-agent-input.test.ts Every KEY: passed in a caller fence is declared in the op's **Input:** — fixes debug.mds ISSUE: / ISSUE_INPUT: mismatch (AC-0.1) Inline corpus with OPERATION: fetch-issue\nISSUE: 42\n → exactly 1 violation naming ISSUE
Command→agent seam: reverse check tests/seams/command-agent-input.test.ts Every required **Input:** value declared by an op is passed by at least one caller fence Same inline corpus
Command→agent seam: third direction (producer) tests/seams/command-agent-input.test.ts Every issue_capture_contract() value has a caller-side producer Same inline corpus
resolveAllAgents() ⊇ getAllAgentNames() tests/guards/agent-source-resolver.test.ts Agent roster completeness — 16 agents (AC-0.7) Would fail if an agent is registered in plugins.ts but has no source file
resolveAgentSource dist-preferred tests/guards/agent-source-resolver.test.ts When dist/agents/ exists, resolver prefers it over src/assets/agents/ Synthetic dist tree with a different content → dist version returned
resolveAgentSource build hint tests/guards/agent-source-resolver.test.ts Throws with `/Run \`npm run build\ `` when neither src nor dist file exists resolveAgentSource('_nonexistent_agent_') → throws containing the build hint
extractOpSectionFromCorpus 'sole' mode tests/guards/agent-source-resolver.test.ts Throws on duplicate anchor across corpus files (DR-18) Two-file corpus with duplicate ## Operation: X heading → throws naming both file paths
extractOpSectionFromCorpus 'union' mode tests/guards/agent-source-resolver.test.ts Returns all matching sections with a match count (DR-18) Same two-file corpus → matchCount = 2, not 1
git-agent golden tests/goldens/git-agent-golden.test.ts tests/fixtures/golden/git-agent.md is byte-identical to src/assets/agents/git.md Fixture absent → throws with regeneration command hint
github-status-lines frozen fixture tests/goldens/github-status-lines.test.ts Frozen fixture byte-identical; test:golden:update refuses to overwrite without --unfreeze node scripts/update-golden.js github-status-lines → non-zero exit + §0.2 rule text printed
Guard 1: REQUIRED_OPS coverage (17 ops) tests/git-agent.test.ts All 17 operations exist in git.md including fetch-issue and fetch-issues-batch (AC-0.11) Removing either new op from REQUIRED_OPS → test fails naming the missing op
Guard 2: manage-debt: 60000 bound tests/git-agent.test.ts manage-debt section contains the 60000-char archive threshold before Phase 2 moves it (AC-0.12) Removing the literal → test fails with "missing 60000-char archive threshold"
INTERNAL_OPS: fetch-issues-batch removed tests/registry-integrity.test.ts INTERNAL_OPS allowlist is ['learn-conventions'] only — fetch-issues-batch removed with a rationale comment (AC-0.11, SG-11) Re-adding fetch-issues-batch without updating the test → reverse-check finds it unlisted in compiled commands
DIST_FILES non-vacuity tests/build-mds.test.ts §20 DIST_FILES.length === 14 (13 compiled hosts + release.md, SG-13) Would fail if dist/commands/ is absent (throw) or if release.md is removed from the constant
compliance_gate() adoption guard tests/build-mds.test.ts §20 ≥6 compiled hosts import the {compliance_gate()} partial (P0-S22) hostsScanned >= 6 — fails if all imports are removed
gh-issue scope guard tests/build-mds.test.ts §21 No gh issue invocation or descriptive mention in any DIST_FILE outside a Git spawn fence (AC-0.4) Inline corpus OPERATION: fetch-issue\ngh issue view 42\n → violation detected (not inside spawn fence)
Retired wording: ISSUE_NUMBERS tests/guards/retired-wording.test.ts ISSUE_NUMBERS (renamed to ISSUE_REFS in Phase 0) is absent from all Phase-0 output files (GAP-32, AC-0.17) Synthetic file seeded with ISSUE_NUMBERSwouldFlag === true
Numeric floor manifest tests/guards/numeric-floor-manifest.test.ts No pinned numeric floor in tests/** decreases vs the committed manifest tests/fixtures/numeric-floors.json (AC-0.17, DR-27a) Copy of manifest with one entry decremented → allPatternsExist assertion fails
Extended references resolver tests/guards/extended-references.test.ts Every ## Extended References row in any skill resolves to a real file on disk Synthetic skill with a nonexistent reference path → violations array non-empty

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