fix(#2431): self-suppress TDD Audit section when all commits are missing (#2467)

* fix(#2431): self-suppress TDD Audit section when all commits are missing

The TDD Audit section in ship.md step 8 was always emitted — but the
execute pipeline only writes gate_status: git trailers when TDD mode is
active. Without TDD mode (the default), every commit's trailer is absent
and the section normalizes to 100% missing, producing a noise table with
no way to disable it.

Fix: add a self-suppress instruction at the point where gate_status
values are normalized. When every commit in the scan normalizes to
'missing', skip both step 8 (TDD Audit section) and step 9 (aggregate
gate_status trailer) entirely. Only emit when at least one commit carries
a real value (skill, fallback, or exempt).

This is data-driven, NOT config-gated. An earlier iteration used inline
'gsd_run query config-get workflow.tdd_mode' — but workflow.tdd_mode is
owned by the tdd capability, and ADR-857 Phase 6 forbids host loop
workflows from reading capability-owned keys via inline config-get. The
self-suppress approach avoids any config-get entirely; it checks the
actual trailer data and skips when there's nothing real to report.

Matches the triage's suggested approach: 'have the audit gracefully
degrade (skip the section)' when there is no real signal.

Tests: tests/workflow-compat.test.cjs gains 3 #2431 assertions:
- documents self-suppress when every commit is missing
- step 9 (aggregate trailer) is also gated on real values existing
- does NOT read workflow.tdd_mode inline (ADR-857 Phase 6 compliant)

The existing feat-41 assertions still pass — the section content is
preserved, only gated by the self-suppress instruction.

References: #2431; PR #585 (consumer shipped, producer never wired);
ADR-857 Phase 6 (capability-owned config keys must not be read inline by
host loop workflows).

* chore(#2431): backfill pr:2467 in .changeset/clever-moles-frolic.md
This commit is contained in:
Tom Boucher
2026-07-20 19:24:08 -04:00
committed by GitHub
parent 6140627f5c
commit bb97ffb5aa
22 changed files with 60 additions and 20 deletions

View File

@@ -143,6 +143,39 @@ describe('feat-41: ship.md TDD Audit gate_status extraction', () => {
assert.match(workflow, /final line|last line/i);
});
// ─── #2431: TDD Audit section self-suppresses when all commits are missing ─
//
// The execute pipeline only writes `gate_status:` git trailers when TDD mode
// is active. Without TDD mode, every commit is `missing` and the section is
// pure noise. The fix adds a self-suppress instruction: skip the section
// entirely when every commit normalizes to `missing`. This is data-driven
// (no inline config-get of a capability-owned key — Phase 6 compliant).
test('#2431: documents self-suppress when every commit is missing', () => {
assert.match(workflow, /self-suppress/i,
'ship.md must instruct the agent to self-suppress the TDD Audit when all commits are missing (#2431)');
assert.match(workflow, /100%.?missing|every commit.*missing/i,
'ship.md must explain that a 100%-missing table is noise and should be skipped (#2431)');
});
test('#2431: step 9 (aggregate trailer) is also gated on real values existing', () => {
// The aggregate gate_status trailer is the companion to the TDD Audit
// section; both must be skipped together when no real gate_status exists.
const step9 = workflow.match(/\*\*9\.\s*Aggregate gate_status trailer[\s\S]*?(?=\*\*10\.|\z)/);
assert.ok(step9, 'step 9 must exist in the workflow');
assert.match(step9[0], /step 8|at least one|real/i,
'step 9 must reference step 8 or require at least one real gate_status value (#2431)');
});
test('#2431: does NOT read workflow.tdd_mode inline (ADR-857 Phase 6 compliant)', () => {
// ADR-857 Phase 6 forbids host loop workflows from reading capability-owned
// config keys via inline config-get. workflow.tdd_mode is owned by the tdd
// capability. The self-suppress approach avoids any config-get — it's
// purely data-driven (check the actual trailer values).
assert.doesNotMatch(workflow, /config-get\s+workflow\.tdd_mode/,
'ship.md must NOT read workflow.tdd_mode via inline config-get (ADR-857 Phase 6 violation — use self-suppress instead, #2431)');
});
test('does not disturb the frozen #3167 core section order (Key Decisions precedes the new section)', () => {
assert.match(workflow, /## Key Decisions[\s\S]*## TDD Audit/);
});