diff --git a/.changeset/broken-windows-description-2787.md b/.changeset/broken-windows-description-2787.md new file mode 100644 index 000000000..2d1f2ac34 --- /dev/null +++ b/.changeset/broken-windows-description-2787.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 2814 +--- +**`broken-windows` capability no longer claims ship blocking is unconditional** — the description now states that `/gsd-ship` blocking applies only when `workflow.windows_enforce` is enabled (default `false`); ledger tracking is unaffected. (#2787) diff --git a/capabilities/broken-windows/capability.json b/capabilities/broken-windows/capability.json index a26cb976f..fc75224e3 100644 --- a/capabilities/broken-windows/capability.json +++ b/capabilities/broken-windows/capability.json @@ -3,7 +3,7 @@ "role": "feature", "version": "1.9.1", "title": "Broken-windows ledger", - "description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. Blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked, enforced artifact (issue #1950).", + "description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. When enforcement is enabled, it blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked artifact (issue #1950).", "tier": "full", "requires": [], "engines": { diff --git a/gsd-core/bin/lib/capability-registry.cjs b/gsd-core/bin/lib/capability-registry.cjs index b6bae0631..c33a32c85 100644 --- a/gsd-core/bin/lib/capability-registry.cjs +++ b/gsd-core/bin/lib/capability-registry.cjs @@ -433,7 +433,7 @@ const capabilities = { "role": "feature", "version": "1.9.1", "title": "Broken-windows ledger", - "description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. Blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked, enforced artifact (issue #1950).", + "description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. When enforcement is enabled, it blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked artifact (issue #1950).", "tier": "full", "requires": [], "engines": { diff --git a/src/broken-windows.cts b/src/broken-windows.cts index 032c4e6b7..fb404cb5f 100644 --- a/src/broken-windows.cts +++ b/src/broken-windows.cts @@ -1,10 +1,10 @@ /** - * Broken-windows ledger — enforced cross-phase defect register (issue #1950). + * Broken-windows ledger — optionally enforced cross-phase defect register (issue #1950). * * Manages `.planning/WINDOWS.md`: a cross-phase ledger of small defects (stubs, * TODOs, skipped tests, lint warnings, unrun verifies, unmet truths, deviations). - * `/gsd-ship` blocks while any entry is `open`; an entry can be `waived` only - * with a recorded reason or `fixed`. + * When `workflow.windows_enforce` is true, `/gsd-ship` blocks while any entry is + * `open`; an entry can be `waived` only with a recorded reason or `fixed`. * * LEAF MODULE — imports ONLY: node:fs, node:path. No other src/ imports. * @@ -596,7 +596,7 @@ export function renderLedger(ledger: Ledger): string { const header = [ '# Broken Windows Ledger', '', - '> Cross-phase defect register. `/gsd-ship` blocks while `open_count > 0`.', + '> Cross-phase defect register. With `workflow.windows_enforce` enabled, `/gsd-ship` blocks while `open_count > 0`.', '> Waive with `gsd-tools windows waive ""` (reason required).', '> Mark fixed with `gsd-tools windows fixed `.', '', diff --git a/tests/broken-windows-description.test.cjs b/tests/broken-windows-description.test.cjs new file mode 100644 index 000000000..8e999d4c9 --- /dev/null +++ b/tests/broken-windows-description.test.cjs @@ -0,0 +1,43 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +test('broken-windows capability description specifies enforcement is opt-in', () => { + const capabilityPath = path.join(__dirname, '..', 'capabilities', 'broken-windows', 'capability.json'); + const capabilityJson = JSON.parse(fs.readFileSync(capabilityPath, 'utf8')); + + assert.ok( + capabilityJson.description.includes('When enforcement is enabled') || capabilityJson.description.includes('workflow.windows_enforce'), + `capability.json description should state enforcement is opt-in, got: ${capabilityJson.description}` + ); + + // Re-anchored: the unqualified sentence on next ("Blocks /gsd-ship while any window is open") must not be present + assert.strictEqual( + capabilityJson.description.includes('Blocks /gsd-ship while any window is open'), + false, + 'description must not contain unqualified unconditional ship blocking sentence' + ); + + // Behavioral gate contract test: verify the ship:pre gate is gated on workflow.windows_enforce + const shipGate = capabilityJson.gates.find(g => g.point === 'ship:pre'); + assert.ok(shipGate, 'broken-windows capability must declare ship:pre gate'); + assert.strictEqual( + shipGate.when, + 'workflow.windows_enforce', + 'ship:pre gate must be gated on workflow.windows_enforce config key' + ); + + // Behavioral test for renderLedger + const bw = require('../gsd-core/bin/lib/broken-windows.cjs'); + const rendered = bw.renderLedger(bw.emptyLedger()); + assert.strictEqual( + rendered.includes('> Cross-phase defect register. `/gsd-ship` blocks while `open_count > 0`.'), + false, + 'renderLedger must not contain unqualified blocking sentence' + ); + assert.ok( + rendered.includes('With `workflow.windows_enforce` enabled, `/gsd-ship` blocks'), + 'renderLedger must document opt-in enforcement' + ); +});