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

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2467
---
**`/gsd-ship` no longer emits a 100%-missing TDD Audit noise table** — the TDD Audit PR-body section 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 was counted `missing` and the table was pure noise with no way to disable it. The section is now gated behind `workflow.tdd_mode`: when TDD mode is off, both the TDD Audit section and the aggregate `gate_status:` trailer are skipped entirely; when on, the existing behavior is preserved.

View File

@@ -289,6 +289,8 @@ Pair commits by their conventional-commit type (the `type:` prefix of the subjec
Surface each commit's `gate_status:` value, normalized to exactly one of `skill`, `fallback`, `exempt`, or `missing` — never the raw trailer text. A commit whose trailer is absent, whose value is none of the first three, or which carries more than one `gate_status:` trailer (ambiguous) is counted as **missing** and still listed. This section is informational; it never blocks the ship.
**Self-suppress when every commit is missing (#2431):** the execute pipeline only writes `gate_status:` trailers when TDD mode is active. If every commit in the scan normalizes to `missing`, skip this section and the aggregate trailer (step 9) entirely — a 100%-missing table is pure noise. Only emit when at least one commit carries a real value (`skill`, `fallback`, or `exempt`).
Harden every table cell against injection, not just subjects: escape `|` as `\|` and strip `\r`/`\n` from both commit subjects and the rendered `gate_status` value. Prefer NUL (`-z` / `%x00`) record separation, and reject any record whose fields contain the `\x1f`/`\x1e` delimiters, so an adversarial commit message cannot corrupt record or field boundaries.
```markdown
@@ -305,7 +307,7 @@ Aggregate: 2 skill, 1 fallback, 1 exempt — 0 missing.
This `## TDD Audit` section is the final body section — it renders after the configured `pr_body_sections`, immediately before the aggregate trailer — so the frozen core sections and the append-only configured sections both keep their existing order.
**9. Aggregate gate_status trailer (final line):**
**9. Aggregate gate_status trailer (final line)** (only when step 8 was emitted — i.e., at least one real `gate_status` value exists):
After every other section — including any configured `pr_body_sections` — emit the audit aggregate as a single Git trailer on the **final line** of the PR body, preceded by a blank line so it parses as a valid trailer:

View File

@@ -304,7 +304,7 @@
"gsd-core/workflows/settings-advanced.md": "6d25100a9de15b31",
"gsd-core/workflows/settings-integrations.md": "e1fda52e8c9afa5f",
"gsd-core/workflows/settings.md": "9b7e0a34f4f41a80",
"gsd-core/workflows/ship.md": "0eca9ab4bcdeeb17",
"gsd-core/workflows/ship.md": "ea549dd991b366b9",
"gsd-core/workflows/sketch-wrap-up.md": "0f842a609851a401",
"gsd-core/workflows/sketch.md": "c42d993c1e9a6240",
"gsd-core/workflows/smart-entry.md": "6d887ca5d9df9185",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "effcef778d4a855f",
"gsd-core/workflows/settings.md": "85ceb482dc713169",
"gsd-core/workflows/ship.md": "09056fccdf20e41b",
"gsd-core/workflows/ship.md": "6693c61fb6a5cd9b",
"gsd-core/workflows/sketch-wrap-up.md": "5f5ebb6a80d610c6",
"gsd-core/workflows/sketch.md": "efc5a794b4bd6e78",
"gsd-core/workflows/smart-entry.md": "004b4060eab97cb9",

View File

@@ -374,7 +374,7 @@
"gsd-core/workflows/settings-advanced.md": "94d61da368e9f85b",
"gsd-core/workflows/settings-integrations.md": "9e0108e01e78832c",
"gsd-core/workflows/settings.md": "a1a35e58da33b23b",
"gsd-core/workflows/ship.md": "205632a5eb12e473",
"gsd-core/workflows/ship.md": "dca31cebcd682c02",
"gsd-core/workflows/sketch-wrap-up.md": "d52a5462bafda830",
"gsd-core/workflows/sketch.md": "9bc12018344f8110",
"gsd-core/workflows/smart-entry.md": "70445afde3a9ead6",

View File

@@ -303,7 +303,7 @@
"gsd-core/workflows/settings-advanced.md": "339def28c34b0797",
"gsd-core/workflows/settings-integrations.md": "9c9db643112b2371",
"gsd-core/workflows/settings.md": "d3f05e22f21bfb3a",
"gsd-core/workflows/ship.md": "965497167a9df93b",
"gsd-core/workflows/ship.md": "2643b8a3fdc524ce",
"gsd-core/workflows/sketch-wrap-up.md": "121ed4b8127abf04",
"gsd-core/workflows/sketch.md": "ac7a5265c6970bdf",
"gsd-core/workflows/smart-entry.md": "99c73236890605a1",

View File

@@ -307,7 +307,7 @@
"gsd-core/workflows/settings-advanced.md": "69f3a19bf2c61160",
"gsd-core/workflows/settings-integrations.md": "a2682897e663fec4",
"gsd-core/workflows/settings.md": "17a5de032a64eb22",
"gsd-core/workflows/ship.md": "d094208aca181f9c",
"gsd-core/workflows/ship.md": "1e74e872bd310c27",
"gsd-core/workflows/sketch-wrap-up.md": "1f44789553180d84",
"gsd-core/workflows/sketch.md": "fadc2cbe6763c010",
"gsd-core/workflows/smart-entry.md": "22ef18e897b67907",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "effcef778d4a855f",
"gsd-core/workflows/settings.md": "85ceb482dc713169",
"gsd-core/workflows/ship.md": "09056fccdf20e41b",
"gsd-core/workflows/ship.md": "6693c61fb6a5cd9b",
"gsd-core/workflows/sketch-wrap-up.md": "2aba89ecd8f41a0d",
"gsd-core/workflows/sketch.md": "2913d90416a3c0f0",
"gsd-core/workflows/smart-entry.md": "004b4060eab97cb9",

View File

@@ -410,7 +410,7 @@
"gsd-core/workflows/settings-advanced.md": "2431433811616f76",
"gsd-core/workflows/settings-integrations.md": "d6d222af8690d09b",
"gsd-core/workflows/settings.md": "3c8bd45b123b5db6",
"gsd-core/workflows/ship.md": "a54841f23fd14626",
"gsd-core/workflows/ship.md": "06bf9b4228299ffb",
"gsd-core/workflows/sketch-wrap-up.md": "07724a390fbb43f6",
"gsd-core/workflows/sketch.md": "3f6ed885d2ee5528",
"gsd-core/workflows/smart-entry.md": "2914f9d54d365931",

View File

@@ -305,7 +305,7 @@
"gsd-core/workflows/settings-advanced.md": "230a658de9c017a6",
"gsd-core/workflows/settings-integrations.md": "58acee11162b4a4e",
"gsd-core/workflows/settings.md": "876e954acd64cb18",
"gsd-core/workflows/ship.md": "24fd52a74258aaad",
"gsd-core/workflows/ship.md": "8358720a4df9b386",
"gsd-core/workflows/sketch-wrap-up.md": "f2590cb6ddbfad94",
"gsd-core/workflows/sketch.md": "9cbf5860b6a005e5",
"gsd-core/workflows/smart-entry.md": "93f90cd7a12dbbc7",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "bf8ea69c8f7ae019",
"gsd-core/workflows/settings-integrations.md": "37d4212d83664224",
"gsd-core/workflows/settings.md": "f90f106a9490c7c9",
"gsd-core/workflows/ship.md": "4be3c89ccda1d856",
"gsd-core/workflows/ship.md": "2afce4d73247fbe4",
"gsd-core/workflows/sketch-wrap-up.md": "5be73b7bdf96b539",
"gsd-core/workflows/sketch.md": "64543e59b00637d4",
"gsd-core/workflows/smart-entry.md": "fdc5113754223df8",

View File

@@ -304,7 +304,7 @@
"gsd-core/workflows/settings-advanced.md": "49be159144d7f426",
"gsd-core/workflows/settings-integrations.md": "75405795c8462a8a",
"gsd-core/workflows/settings.md": "41c0f737bfd311fd",
"gsd-core/workflows/ship.md": "7f27ba3add159c13",
"gsd-core/workflows/ship.md": "cb1ac552708dda9b",
"gsd-core/workflows/sketch-wrap-up.md": "f1ece50ac65ea281",
"gsd-core/workflows/sketch.md": "592283400d70317b",
"gsd-core/workflows/smart-entry.md": "4dc50d4af659c651",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "edd858cd6cfddaf1",
"gsd-core/workflows/settings-integrations.md": "3ec4f77c421b87b4",
"gsd-core/workflows/settings.md": "0e3cf91e952b48a2",
"gsd-core/workflows/ship.md": "aab2ad926f4613c6",
"gsd-core/workflows/ship.md": "e6852cb78fce1582",
"gsd-core/workflows/sketch-wrap-up.md": "888c0548e63197b3",
"gsd-core/workflows/sketch.md": "75ffbd233fd9a762",
"gsd-core/workflows/smart-entry.md": "2ad5b63a9deea0ce",

View File

@@ -368,7 +368,7 @@
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "effcef778d4a855f",
"gsd-core/workflows/settings.md": "85ceb482dc713169",
"gsd-core/workflows/ship.md": "09056fccdf20e41b",
"gsd-core/workflows/ship.md": "6693c61fb6a5cd9b",
"gsd-core/workflows/sketch-wrap-up.md": "b767a1d3db129a8a",
"gsd-core/workflows/sketch.md": "5f0a7d3640cdff54",
"gsd-core/workflows/smart-entry.md": "004b4060eab97cb9",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "252b0d3edc315339",
"gsd-core/workflows/settings-integrations.md": "1c3997dc6953e7eb",
"gsd-core/workflows/settings.md": "a244850ce2f7b8dd",
"gsd-core/workflows/ship.md": "b3dedb0506ba3ab6",
"gsd-core/workflows/ship.md": "a4f14d4154451663",
"gsd-core/workflows/sketch-wrap-up.md": "681800323681c5c6",
"gsd-core/workflows/sketch.md": "da82c9be7074545c",
"gsd-core/workflows/smart-entry.md": "c4c780d3aa2124f8",

View File

@@ -271,7 +271,7 @@
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "effcef778d4a855f",
"gsd-core/workflows/settings.md": "85ceb482dc713169",
"gsd-core/workflows/ship.md": "09056fccdf20e41b",
"gsd-core/workflows/ship.md": "6693c61fb6a5cd9b",
"gsd-core/workflows/sketch-wrap-up.md": "838c701bd072ae73",
"gsd-core/workflows/sketch.md": "70df86d9d8eeba0f",
"gsd-core/workflows/smart-entry.md": "004b4060eab97cb9",

View File

@@ -304,7 +304,7 @@
"gsd-core/workflows/settings-advanced.md": "5e05212fb5cba531",
"gsd-core/workflows/settings-integrations.md": "c9fb71d26fd93527",
"gsd-core/workflows/settings.md": "83a13de5926ead14",
"gsd-core/workflows/ship.md": "8a4fe0b69f5c72f9",
"gsd-core/workflows/ship.md": "59c6c30887206a69",
"gsd-core/workflows/sketch-wrap-up.md": "89e0eab2af946b04",
"gsd-core/workflows/sketch.md": "6e68bbff5c1e6db0",
"gsd-core/workflows/smart-entry.md": "072ac6efe8aafc38",

View File

@@ -304,7 +304,7 @@
"gsd-core/workflows/settings-advanced.md": "39e66386f6c48025",
"gsd-core/workflows/settings-integrations.md": "8bb1c2bdebbc56e6",
"gsd-core/workflows/settings.md": "db6121276c6f63a4",
"gsd-core/workflows/ship.md": "6c97dde34a8edcd7",
"gsd-core/workflows/ship.md": "4114412c68dd845f",
"gsd-core/workflows/sketch-wrap-up.md": "dbec602d104cb951",
"gsd-core/workflows/sketch.md": "6a6e543f2b667278",
"gsd-core/workflows/smart-entry.md": "5631e2e70b02abba",

View File

@@ -304,7 +304,7 @@
"gsd-core/workflows/settings-advanced.md": "2f86ec7b998f9485",
"gsd-core/workflows/settings-integrations.md": "159d0ae32b511129",
"gsd-core/workflows/settings.md": "26fff45b86d827aa",
"gsd-core/workflows/ship.md": "7682321e294a2574",
"gsd-core/workflows/ship.md": "6883c9a9cc4a1543",
"gsd-core/workflows/sketch-wrap-up.md": "10063f56c2c7f141",
"gsd-core/workflows/sketch.md": "c15716376df41c97",
"gsd-core/workflows/smart-entry.md": "3fed94530109124b",

View File

@@ -375,7 +375,7 @@
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "effcef778d4a855f",
"gsd-core/workflows/settings.md": "85ceb482dc713169",
"gsd-core/workflows/ship.md": "09056fccdf20e41b",
"gsd-core/workflows/ship.md": "6693c61fb6a5cd9b",
"gsd-core/workflows/sketch-wrap-up.md": "86db87b16548117e",
"gsd-core/workflows/sketch.md": "fcd52f8d9076e183",
"gsd-core/workflows/smart-entry.md": "004b4060eab97cb9",

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/);
});

View File

@@ -71,7 +71,7 @@
"settings-advanced.md": 40019,
"settings-integrations.md": 16257,
"settings.md": 33832,
"ship.md": 28310,
"ship.md": 28782,
"sketch-wrap-up.md": 14267,
"sketch.md": 20369,
"smart-entry.md": 11489,