diff --git a/.changeset/1864-settings-step-balance.md b/.changeset/1864-settings-step-balance.md new file mode 100644 index 000000000..1dcbdced0 --- /dev/null +++ b/.changeset/1864-settings-step-balance.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 2014 +--- +**`settings-advanced.md` no longer has an orphan `` around §8 Model Policy** — the §8 Model Policy block ended with a closing `` but had no matching opening tag (5 opens / 6 closes), leaving its content as loose inter-step prose that could fail to execute reliably. Added the missing `` opener so the section is a proper step. A new workflow ``-tag-balance regression guard (fenced-code-stripped) now blocks any future orphan tag across all top-level workflows. (#1864) diff --git a/gsd-core/workflows/settings-advanced.md b/gsd-core/workflows/settings-advanced.md index c9534834a..ac550fc62 100644 --- a/gsd-core/workflows/settings-advanced.md +++ b/gsd-core/workflows/settings-advanced.md @@ -568,6 +568,8 @@ route each write through `gsd-tools.cjs query config-set` so sibling preservatio the central setter. + + ### Section 8 — Model Policy This section configures the `model_policy` key in `.planning/config.json`. Model policy diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json index 99a45f98c..fb67bf0dd 100644 --- a/tests/fixtures/golden-install-parity/antigravity.json +++ b/tests/fixtures/golden-install-parity/antigravity.json @@ -282,7 +282,7 @@ "gsd-core/workflows/scan.md": "a7fecd67e5cd655f", "gsd-core/workflows/secure-phase.md": "96b199dfac00e60f", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "43a3875aec529e5c", + "gsd-core/workflows/settings-advanced.md": "f2f8c750101cb9ee", "gsd-core/workflows/settings-integrations.md": "4d3001ad2b5dad8f", "gsd-core/workflows/settings.md": "4017f3f235051902", "gsd-core/workflows/ship.md": "984bd7660e7791fd", diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json index 899c8df8c..c5cfa1494 100644 --- a/tests/fixtures/golden-install-parity/augment.json +++ b/tests/fixtures/golden-install-parity/augment.json @@ -352,7 +352,7 @@ "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "3d86a02a58171cff", + "gsd-core/workflows/settings-advanced.md": "b57c947edee905bc", "gsd-core/workflows/settings-integrations.md": "70515c5838fb9826", "gsd-core/workflows/settings.md": "b2d39d972650f5f3", "gsd-core/workflows/ship.md": "7b8fe9f89143e648", diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json index 3309a4680..2bc34e091 100644 --- a/tests/fixtures/golden-install-parity/claude.json +++ b/tests/fixtures/golden-install-parity/claude.json @@ -281,7 +281,7 @@ "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "59d3c50aba8c9a6c", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "c12cfa7a9e83bba4", + "gsd-core/workflows/settings-advanced.md": "02f7ef84f976bda4", "gsd-core/workflows/settings-integrations.md": "53649313d20694ae", "gsd-core/workflows/settings.md": "3d994d669d3fb321", "gsd-core/workflows/ship.md": "12e8e58c077a891a", diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json index 38c909b5c..8abf17eab 100644 --- a/tests/fixtures/golden-install-parity/cline.json +++ b/tests/fixtures/golden-install-parity/cline.json @@ -285,7 +285,7 @@ "gsd-core/workflows/scan.md": "dfd92717caea0ce7", "gsd-core/workflows/secure-phase.md": "cf78183f06a02582", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "bb070111ecdaf386", + "gsd-core/workflows/settings-advanced.md": "e08245a5a65dcec2", "gsd-core/workflows/settings-integrations.md": "76eee76d6eb57657", "gsd-core/workflows/settings.md": "f6c7aa7ceb99078b", "gsd-core/workflows/ship.md": "5f931a25ea9102a4", diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json index f2a0315b7..145ca0c83 100644 --- a/tests/fixtures/golden-install-parity/codebuddy.json +++ b/tests/fixtures/golden-install-parity/codebuddy.json @@ -352,7 +352,7 @@ "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "3d86a02a58171cff", + "gsd-core/workflows/settings-advanced.md": "b57c947edee905bc", "gsd-core/workflows/settings-integrations.md": "70515c5838fb9826", "gsd-core/workflows/settings.md": "b2d39d972650f5f3", "gsd-core/workflows/ship.md": "7b8fe9f89143e648", diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json index 19e9915a1..762f0ea54 100644 --- a/tests/fixtures/golden-install-parity/codex.json +++ b/tests/fixtures/golden-install-parity/codex.json @@ -317,7 +317,7 @@ "gsd-core/workflows/scan.md": "1a3caa5d724d39e9", "gsd-core/workflows/secure-phase.md": "db91810d16964b1e", "gsd-core/workflows/session-report.md": "dd8fa011c9394075", - "gsd-core/workflows/settings-advanced.md": "19ce35f8fae6b667", + "gsd-core/workflows/settings-advanced.md": "2cbb05c302f1df5e", "gsd-core/workflows/settings-integrations.md": "77730321d3d6d317", "gsd-core/workflows/settings.md": "1e19b0245851afc0", "gsd-core/workflows/ship.md": "d86233c9a365effd", diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json index 2f64325c1..53cb2c767 100644 --- a/tests/fixtures/golden-install-parity/copilot.json +++ b/tests/fixtures/golden-install-parity/copilot.json @@ -283,7 +283,7 @@ "gsd-core/workflows/scan.md": "dcc2f76d0850e2fb", "gsd-core/workflows/secure-phase.md": "d87bd706f85bcad6", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "ae415134000ee1e2", + "gsd-core/workflows/settings-advanced.md": "85f19b66b7d5040f", "gsd-core/workflows/settings-integrations.md": "a1d146d6bfd14db5", "gsd-core/workflows/settings.md": "95aa4a9bc0b51e92", "gsd-core/workflows/ship.md": "b9dc0aaee0ff68e7", diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json index 5d9092d65..c73b33e70 100644 --- a/tests/fixtures/golden-install-parity/cursor.json +++ b/tests/fixtures/golden-install-parity/cursor.json @@ -352,7 +352,7 @@ "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "c55975672c4e1895", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "4887fb7e45ddb3b6", + "gsd-core/workflows/settings-advanced.md": "3817a94612e4b588", "gsd-core/workflows/settings-integrations.md": "166ca51b1f33c2a3", "gsd-core/workflows/settings.md": "1e285d00022e00ce", "gsd-core/workflows/ship.md": "38830806d244fe9c", diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json index 44747652c..ff46c3a89 100644 --- a/tests/fixtures/golden-install-parity/hermes.json +++ b/tests/fixtures/golden-install-parity/hermes.json @@ -282,7 +282,7 @@ "gsd-core/workflows/scan.md": "b28f65d88c522767", "gsd-core/workflows/secure-phase.md": "f2957d4b88fb3746", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "a0629316a41e5d30", + "gsd-core/workflows/settings-advanced.md": "9a45f0e5b8acf83d", "gsd-core/workflows/settings-integrations.md": "1dce76db0aca08a5", "gsd-core/workflows/settings.md": "cb2b6be87c519df9", "gsd-core/workflows/ship.md": "dbf8bf636cb196c0", diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json index 97db9c846..0f4eec62b 100644 --- a/tests/fixtures/golden-install-parity/kilo.json +++ b/tests/fixtures/golden-install-parity/kilo.json @@ -352,7 +352,7 @@ "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "e8855104c1e0417c", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "df9c4835bcb4373f", + "gsd-core/workflows/settings-advanced.md": "03c647793401655b", "gsd-core/workflows/settings-integrations.md": "3ea8095d5fad891b", "gsd-core/workflows/settings.md": "05e35aeb442b7e20", "gsd-core/workflows/ship.md": "afd77be2093535f8", diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json index 7261a9921..73510bb25 100644 --- a/tests/fixtures/golden-install-parity/kimi.json +++ b/tests/fixtures/golden-install-parity/kimi.json @@ -318,7 +318,7 @@ "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "3d86a02a58171cff", + "gsd-core/workflows/settings-advanced.md": "b57c947edee905bc", "gsd-core/workflows/settings-integrations.md": "70515c5838fb9826", "gsd-core/workflows/settings.md": "b2d39d972650f5f3", "gsd-core/workflows/ship.md": "7b8fe9f89143e648", diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json index c2e22d0fc..f82b52840 100644 --- a/tests/fixtures/golden-install-parity/opencode.json +++ b/tests/fixtures/golden-install-parity/opencode.json @@ -352,7 +352,7 @@ "gsd-core/workflows/scan.md": "ad8ebcad4626d4a8", "gsd-core/workflows/secure-phase.md": "e9a488cec3b4efdc", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "72438bf453f7340b", + "gsd-core/workflows/settings-advanced.md": "f62c8e4fa6d3a3d9", "gsd-core/workflows/settings-integrations.md": "d1711a95f44fdbde", "gsd-core/workflows/settings.md": "bbbcad8d95d5e813", "gsd-core/workflows/ship.md": "924c79e3cfd1e42b", diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json index 5ba5797d3..27d5f2ac1 100644 --- a/tests/fixtures/golden-install-parity/qwen.json +++ b/tests/fixtures/golden-install-parity/qwen.json @@ -282,7 +282,7 @@ "gsd-core/workflows/scan.md": "949692db4834dd27", "gsd-core/workflows/secure-phase.md": "6758f1acf4113e9e", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "008b5b3f4942e8a1", + "gsd-core/workflows/settings-advanced.md": "8d850d15f1d06dd6", "gsd-core/workflows/settings-integrations.md": "29c5de27fbbb18e9", "gsd-core/workflows/settings.md": "da0b7567d648b80a", "gsd-core/workflows/ship.md": "9f94d0b155eb041e", diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json index a0313445d..519e256b8 100644 --- a/tests/fixtures/golden-install-parity/trae.json +++ b/tests/fixtures/golden-install-parity/trae.json @@ -282,7 +282,7 @@ "gsd-core/workflows/scan.md": "63631467651d9ca8", "gsd-core/workflows/secure-phase.md": "6cc236e53c2e7d56", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "ed6ebaf5fb33a790", + "gsd-core/workflows/settings-advanced.md": "252d3f848c95220e", "gsd-core/workflows/settings-integrations.md": "f8f756709ec02363", "gsd-core/workflows/settings.md": "01cf9ffd340cff64", "gsd-core/workflows/ship.md": "c08f0fe3025d2c86", diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json index dcc68da02..3ecb91e02 100644 --- a/tests/fixtures/golden-install-parity/windsurf.json +++ b/tests/fixtures/golden-install-parity/windsurf.json @@ -282,7 +282,7 @@ "gsd-core/workflows/scan.md": "12c11b2edc165df9", "gsd-core/workflows/secure-phase.md": "7bf923689bf58288", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", - "gsd-core/workflows/settings-advanced.md": "c00c99b938e45272", + "gsd-core/workflows/settings-advanced.md": "4c030adc097d1746", "gsd-core/workflows/settings-integrations.md": "b082fc518b484c07", "gsd-core/workflows/settings.md": "13ca50dba129c17b", "gsd-core/workflows/ship.md": "c035bb8b3bb7efbb", diff --git a/tests/workflow-size-baseline.json b/tests/workflow-size-baseline.json index 3e6e26011..bf1ba001e 100644 --- a/tests/workflow-size-baseline.json +++ b/tests/workflow-size-baseline.json @@ -67,7 +67,7 @@ "scan.md": 7732, "secure-phase.md": 13520, "session-report.md": 4044, - "settings-advanced.md": 39690, + "settings-advanced.md": 39718, "settings-integrations.md": 15892, "settings.md": 33457, "ship.md": 24691, diff --git a/tests/workflow-step-tag-balance.test.cjs b/tests/workflow-step-tag-balance.test.cjs new file mode 100644 index 000000000..ae401a954 --- /dev/null +++ b/tests/workflow-step-tag-balance.test.cjs @@ -0,0 +1,50 @@ +'use strict'; + +// Regression guard for #1864 — settings-advanced.md had an orphan +// (6 closes / 5 opens) because §8 Model Policy lacked an opening . +// Asserts EVERY top-level workflow .md has balanced / tags so an +// unbalanced workflow can never land again. Fenced code blocks are stripped +// first so legitimate examples inside ``` fences don't false-positive. + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const { stripFencedCode } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs'); + +const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows'); + +function topLevelWorkflowFiles() { + return fs.readdirSync(WORKFLOWS_DIR) + .filter((f) => f.endsWith('.md')) + .map((f) => path.join(WORKFLOWS_DIR, f)); +} + +function countTags(text, re) { + // matchAll requires the global flag; force it so the iterator terminates. + const flags = re.flags.includes('g') ? re.flags : re.flags + 'g'; + return [...text.matchAll(new RegExp(re.source, flags))].length; +} + +describe('workflow tag balance (#1864)', () => { + test('every top-level workflow has equal opens and closes (code fences stripped)', () => { + const offenders = []; + for (const file of topLevelWorkflowFiles()) { + const raw = fs.readFileSync(file, 'utf8'); + const stripped = stripFencedCode(raw).text; + const opens = countTags(stripped, /)/); + const closes = countTags(stripped, /<\/step>/); + if (opens !== closes) offenders.push(`${path.basename(file)}: ${opens} opens / ${closes} closes`); + } + assert.deepEqual(offenders, [], `unbalanced tags:\n${offenders.join('\n')}`); + }); + + test('settings-advanced.md §8 Model Policy is wrapped in a model_policy step (#1864)', () => { + const file = path.join(WORKFLOWS_DIR, 'settings-advanced.md'); + const raw = fs.readFileSync(file, 'utf8'); + const stripped = stripFencedCode(raw).text; + assert.match(stripped, /[\s\S]*?### Section 8 — Model Policy[\s\S]*?<\/step>/, + '§8 must be wrapped in …'); + }); +});