Merge branch 'next' into fix/2028-phase-complete-milestone-end-and-workstream-guard
This commit is contained in:
@@ -94,7 +94,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "de81380316d8a37f",
|
||||
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
|
||||
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
|
||||
@@ -207,13 +207,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "603ce2019835f00e",
|
||||
"gsd-core/workflows/check-todos.md": "5a62092df800ad7c",
|
||||
"gsd-core/workflows/cleanup.md": "12c8fd85d3010fe6",
|
||||
"gsd-core/workflows/code-review-fix.md": "5738cb929c995008",
|
||||
"gsd-core/workflows/code-review.md": "d454365f1704d0bc",
|
||||
"gsd-core/workflows/code-review-fix.md": "60640e633b0a124b",
|
||||
"gsd-core/workflows/code-review.md": "5c40505c01871153",
|
||||
"gsd-core/workflows/complete-milestone.md": "aaf272074acec69d",
|
||||
"gsd-core/workflows/debug.md": "68f1ddc74886ebe5",
|
||||
"gsd-core/workflows/diagnose-issues.md": "c8c41993c277363c",
|
||||
"gsd-core/workflows/discovery-phase.md": "3de990caffdde4f8",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "42210a0cbe1bac70",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "8581fd77def7ce84",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
|
||||
"gsd-core/workflows/discuss-phase.md": "c0a977154470b7f5",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "ab0c68941386998b",
|
||||
@@ -279,7 +279,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "dc5598ae8accdecd",
|
||||
"gsd-core/workflows/profile-user.md": "355af92ac285567f",
|
||||
"gsd-core/workflows/progress.md": "79a11ce798082054",
|
||||
"gsd-core/workflows/quick.md": "35582887917ef938",
|
||||
"gsd-core/workflows/quick.md": "7108075f69e88e7c",
|
||||
"gsd-core/workflows/reapply-patches.md": "4dcd6117d0a507ca",
|
||||
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
|
||||
"gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798",
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
|
||||
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
|
||||
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
|
||||
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
|
||||
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
|
||||
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
|
||||
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
|
||||
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
|
||||
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
|
||||
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
|
||||
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
|
||||
"gsd-core/workflows/diagnose-issues.md": "6cc3900891dfb927",
|
||||
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
|
||||
"gsd-core/workflows/discuss-phase.md": "5c5e8d8c2c9d95aa",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "14263db831230142",
|
||||
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
|
||||
"gsd-core/workflows/quick.md": "5790ceb09aa685be",
|
||||
"gsd-core/workflows/quick.md": "069fe37d083a94a3",
|
||||
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/claude.json
vendored
10
tests/fixtures/golden-install-parity/claude.json
vendored
@@ -93,7 +93,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "90e2c9bc577278fd",
|
||||
"gsd-core/references/model-profiles.md": "c249163663bbea53",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -206,13 +206,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "722397c04272dfaa",
|
||||
"gsd-core/workflows/check-todos.md": "6c2a43d1d3e86589",
|
||||
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
|
||||
"gsd-core/workflows/code-review-fix.md": "adb62c695b39ef61",
|
||||
"gsd-core/workflows/code-review.md": "6c40b95e799907d0",
|
||||
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
|
||||
"gsd-core/workflows/code-review.md": "506412604f767adc",
|
||||
"gsd-core/workflows/complete-milestone.md": "dcf1182398efb1ca",
|
||||
"gsd-core/workflows/debug.md": "a9397fd35cca2240",
|
||||
"gsd-core/workflows/diagnose-issues.md": "75ffc381ac3059ff",
|
||||
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "46876f83547db40a",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "a8cd1db094fefd35",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "df56c8cd170b2150",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -278,7 +278,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
|
||||
"gsd-core/workflows/profile-user.md": "ff3820a27731ceb8",
|
||||
"gsd-core/workflows/progress.md": "bd1ecf9207331bda",
|
||||
"gsd-core/workflows/quick.md": "da83b1a15b7f1bf7",
|
||||
"gsd-core/workflows/quick.md": "3363bdfefd403686",
|
||||
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/cline.json
vendored
10
tests/fixtures/golden-install-parity/cline.json
vendored
@@ -97,7 +97,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "eb144b83e31196b8",
|
||||
"gsd-core/references/model-profiles.md": "1794ad3d9854129e",
|
||||
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -210,13 +210,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "cc5217b1b2238a4c",
|
||||
"gsd-core/workflows/check-todos.md": "32fdf33f5dc8bdde",
|
||||
"gsd-core/workflows/cleanup.md": "b0ebfc48792b407b",
|
||||
"gsd-core/workflows/code-review-fix.md": "b946fe7bcb303852",
|
||||
"gsd-core/workflows/code-review.md": "0b24efcfa71a2ed2",
|
||||
"gsd-core/workflows/code-review-fix.md": "e4549af672e74e6f",
|
||||
"gsd-core/workflows/code-review.md": "a65e3e869508f89e",
|
||||
"gsd-core/workflows/complete-milestone.md": "c0808127038a8f86",
|
||||
"gsd-core/workflows/debug.md": "f42e8e3cbc298694",
|
||||
"gsd-core/workflows/diagnose-issues.md": "e616d0d730328d68",
|
||||
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "89987fd183e0d552",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "9efcb2ef6a9245b3",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
|
||||
"gsd-core/workflows/discuss-phase.md": "a290d5ee19607bb7",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "250de9aae90daebc",
|
||||
@@ -282,7 +282,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "9923878a4f6a2d91",
|
||||
"gsd-core/workflows/profile-user.md": "26f74db0a7fcd268",
|
||||
"gsd-core/workflows/progress.md": "9f326d63afb4b76b",
|
||||
"gsd-core/workflows/quick.md": "68a9dfcd53f0859f",
|
||||
"gsd-core/workflows/quick.md": "2afc046e94daad0a",
|
||||
"gsd-core/workflows/reapply-patches.md": "eb4272145a117904",
|
||||
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
|
||||
"gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90",
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
|
||||
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
|
||||
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
|
||||
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
|
||||
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
|
||||
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
|
||||
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
|
||||
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
|
||||
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
|
||||
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
|
||||
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
|
||||
"gsd-core/workflows/diagnose-issues.md": "77d98ac07c4a26ff",
|
||||
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
|
||||
"gsd-core/workflows/discuss-phase.md": "02a5381c9a2173be",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "4fa910d15dea5695",
|
||||
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
|
||||
"gsd-core/workflows/quick.md": "9033dbe58443af36",
|
||||
"gsd-core/workflows/quick.md": "cc530299ba461539",
|
||||
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/codex.json
vendored
10
tests/fixtures/golden-install-parity/codex.json
vendored
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "86e81f17c2f23fff",
|
||||
"gsd-core/references/model-profiles.md": "bd90862f68007f2e",
|
||||
"gsd-core/references/mvp-concepts.md": "23201c8118fb074a",
|
||||
"gsd-core/references/phase-argument-parsing.md": "531176f66da49c98",
|
||||
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
|
||||
@@ -242,13 +242,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "92f08626d4aea668",
|
||||
"gsd-core/workflows/check-todos.md": "b2b103e8638e760a",
|
||||
"gsd-core/workflows/cleanup.md": "5d48d64664222a3e",
|
||||
"gsd-core/workflows/code-review-fix.md": "3ff5f7eac2269ca3",
|
||||
"gsd-core/workflows/code-review.md": "82858ab037b9176e",
|
||||
"gsd-core/workflows/code-review-fix.md": "ae7f9c6b39a23c12",
|
||||
"gsd-core/workflows/code-review.md": "eadada9e0a89adf2",
|
||||
"gsd-core/workflows/complete-milestone.md": "017df7443bd08da5",
|
||||
"gsd-core/workflows/debug.md": "cc7b2d2fd4307a78",
|
||||
"gsd-core/workflows/diagnose-issues.md": "e38bb21d06dff077",
|
||||
"gsd-core/workflows/discovery-phase.md": "71a4b78ff876a854",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f23abfe76623ed63",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "0d936ec25299917c",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "3f8b83dc6be2e9d5",
|
||||
"gsd-core/workflows/discuss-phase.md": "7c9df6656b81fda9",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -314,7 +314,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "d13e1cc81de40896",
|
||||
"gsd-core/workflows/profile-user.md": "05828c8cc61ef384",
|
||||
"gsd-core/workflows/progress.md": "7bedc431bb55edb8",
|
||||
"gsd-core/workflows/quick.md": "3e7686705da2af19",
|
||||
"gsd-core/workflows/quick.md": "62b9e138dc70b479",
|
||||
"gsd-core/workflows/reapply-patches.md": "26297b84736e66a4",
|
||||
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
|
||||
"gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "1d392e37a746742a",
|
||||
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
|
||||
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
|
||||
@@ -208,13 +208,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "dd972ddde9663295",
|
||||
"gsd-core/workflows/check-todos.md": "be9b50b5f28d7504",
|
||||
"gsd-core/workflows/cleanup.md": "8233b05ebf011bec",
|
||||
"gsd-core/workflows/code-review-fix.md": "ab2b467b41522c10",
|
||||
"gsd-core/workflows/code-review.md": "1bbbe61f45fccf4c",
|
||||
"gsd-core/workflows/code-review-fix.md": "fda53892ae4b17fc",
|
||||
"gsd-core/workflows/code-review.md": "f1c045ec4d33abc8",
|
||||
"gsd-core/workflows/complete-milestone.md": "470cf39261400ee2",
|
||||
"gsd-core/workflows/debug.md": "36cb536be452d79e",
|
||||
"gsd-core/workflows/diagnose-issues.md": "42acbe2a43fc886e",
|
||||
"gsd-core/workflows/discovery-phase.md": "8e99da61fb2b7074",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "ab2d26b194805ac1",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "ab0c432b84038681",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
|
||||
"gsd-core/workflows/discuss-phase.md": "31a79ea11c1cdd5f",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "654cdaf1138d5e27",
|
||||
@@ -280,7 +280,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "2833905f119b5722",
|
||||
"gsd-core/workflows/profile-user.md": "5cc032206c99ef71",
|
||||
"gsd-core/workflows/progress.md": "6b9a84a43dc55af5",
|
||||
"gsd-core/workflows/quick.md": "7ddfe17f048541ec",
|
||||
"gsd-core/workflows/quick.md": "eb68ea8748546331",
|
||||
"gsd-core/workflows/reapply-patches.md": "8fd59e24b486f180",
|
||||
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
|
||||
"gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/cursor.json
vendored
10
tests/fixtures/golden-install-parity/cursor.json
vendored
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "f4c013e700c52b08",
|
||||
"gsd-core/references/model-profiles.md": "5452b2e19e19f77e",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "4b20eda9a0b587ce",
|
||||
"gsd-core/workflows/check-todos.md": "1a67337d1630848f",
|
||||
"gsd-core/workflows/cleanup.md": "db47963f103c2748",
|
||||
"gsd-core/workflows/code-review-fix.md": "49e7f024c551b3a5",
|
||||
"gsd-core/workflows/code-review.md": "6a9fd2996a6b600e",
|
||||
"gsd-core/workflows/code-review-fix.md": "c57af378033b1b58",
|
||||
"gsd-core/workflows/code-review.md": "32c37bcbec8b8698",
|
||||
"gsd-core/workflows/complete-milestone.md": "1400a4856f592f3e",
|
||||
"gsd-core/workflows/debug.md": "a73d8018986131ba",
|
||||
"gsd-core/workflows/diagnose-issues.md": "cd582747131726e3",
|
||||
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "373788c6b7eab272",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "c25c6a6c633d71b6",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "14688ff19ce2184c",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "30612a2de153cb5b",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "c67d90c65da47168",
|
||||
"gsd-core/workflows/profile-user.md": "8c943983241260b5",
|
||||
"gsd-core/workflows/progress.md": "65aabee5e8a6dd82",
|
||||
"gsd-core/workflows/quick.md": "762256cf6d177c06",
|
||||
"gsd-core/workflows/quick.md": "32a71f62041c8510",
|
||||
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "433affcd1a200826",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/hermes.json
vendored
10
tests/fixtures/golden-install-parity/hermes.json
vendored
@@ -94,7 +94,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "8472d453a325cc1b",
|
||||
"gsd-core/references/model-profiles.md": "6012c3b53473f04f",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -207,13 +207,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "dd572ca89862e5ec",
|
||||
"gsd-core/workflows/check-todos.md": "ea9a303c48a5d752",
|
||||
"gsd-core/workflows/cleanup.md": "6c488059fc152a49",
|
||||
"gsd-core/workflows/code-review-fix.md": "04b143a963067236",
|
||||
"gsd-core/workflows/code-review.md": "ff28724fc216972c",
|
||||
"gsd-core/workflows/code-review-fix.md": "e829d3baf9901b54",
|
||||
"gsd-core/workflows/code-review.md": "50a05ab8957bd05f",
|
||||
"gsd-core/workflows/complete-milestone.md": "f1866541148dc291",
|
||||
"gsd-core/workflows/debug.md": "15cf6999e85dcc8c",
|
||||
"gsd-core/workflows/diagnose-issues.md": "16f2d2a85335641f",
|
||||
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "db60ec68b59a40eb",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "3a1e215890d2b3f4",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "0aa052ae4ee70bf6",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "536e2fa842f1bc95",
|
||||
@@ -279,7 +279,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "ecabd55e4eabf229",
|
||||
"gsd-core/workflows/profile-user.md": "de5030437226cf2c",
|
||||
"gsd-core/workflows/progress.md": "f18db000584d9cb1",
|
||||
"gsd-core/workflows/quick.md": "5044ce4e7512e174",
|
||||
"gsd-core/workflows/quick.md": "e7a395b9e7786b56",
|
||||
"gsd-core/workflows/reapply-patches.md": "158083a310859594",
|
||||
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
|
||||
"gsd-core/workflows/remove-workspace.md": "8facde381657dd71",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/kilo.json
vendored
10
tests/fixtures/golden-install-parity/kilo.json
vendored
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
|
||||
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "73f429f7472c9949",
|
||||
"gsd-core/workflows/check-todos.md": "ed4b4eb12be222d7",
|
||||
"gsd-core/workflows/cleanup.md": "c5f1bf186395d67d",
|
||||
"gsd-core/workflows/code-review-fix.md": "adb62c695b39ef61",
|
||||
"gsd-core/workflows/code-review.md": "6c40b95e799907d0",
|
||||
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
|
||||
"gsd-core/workflows/code-review.md": "506412604f767adc",
|
||||
"gsd-core/workflows/complete-milestone.md": "59753bf44d4300da",
|
||||
"gsd-core/workflows/debug.md": "a72d830ac3df74aa",
|
||||
"gsd-core/workflows/diagnose-issues.md": "210b5b313e8a559a",
|
||||
"gsd-core/workflows/discovery-phase.md": "ca7b2be46e59e862",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "36f7edcb666745f2",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "3ef1df313e715387",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
|
||||
"gsd-core/workflows/discuss-phase.md": "bd4b26ba168bb51f",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "f8ae26ba4e07672b",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
|
||||
"gsd-core/workflows/profile-user.md": "203ebe3f8f3876a8",
|
||||
"gsd-core/workflows/progress.md": "9381c59676ccb937",
|
||||
"gsd-core/workflows/quick.md": "fd7461e5a92fe450",
|
||||
"gsd-core/workflows/quick.md": "e71c92cf0463a461",
|
||||
"gsd-core/workflows/reapply-patches.md": "becf9728cdb124c4",
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/kimi.json
vendored
10
tests/fixtures/golden-install-parity/kimi.json
vendored
@@ -130,7 +130,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
|
||||
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
|
||||
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -243,13 +243,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
|
||||
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
|
||||
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
|
||||
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
|
||||
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
|
||||
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
|
||||
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
|
||||
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
|
||||
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
|
||||
"gsd-core/workflows/diagnose-issues.md": "67c058fc7ae6026b",
|
||||
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
|
||||
"gsd-core/workflows/discuss-phase.md": "0c0465ba668adb33",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -315,7 +315,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "5abfae83739fa978",
|
||||
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
|
||||
"gsd-core/workflows/quick.md": "e265e01cfe117215",
|
||||
"gsd-core/workflows/quick.md": "09d88dd1c0b3e03a",
|
||||
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "858c16730df68ac2",
|
||||
"gsd-core/references/model-profiles.md": "d6ed560db6357ad2",
|
||||
"gsd-core/references/model-profiles.md": "6568ca29b6ee00d8",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "2459c40bfc8a0ea8",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "6cb0c014f5f56965",
|
||||
"gsd-core/workflows/check-todos.md": "6526b64ee88c7d2e",
|
||||
"gsd-core/workflows/cleanup.md": "94d771d26f62dc86",
|
||||
"gsd-core/workflows/code-review-fix.md": "5d97b960da4fef73",
|
||||
"gsd-core/workflows/code-review.md": "aec8ef4de1829607",
|
||||
"gsd-core/workflows/code-review-fix.md": "adb9388bb157610c",
|
||||
"gsd-core/workflows/code-review.md": "ae6bcbd1575aeec4",
|
||||
"gsd-core/workflows/complete-milestone.md": "614299b2c08e66c3",
|
||||
"gsd-core/workflows/debug.md": "9d4a8afc8d36be93",
|
||||
"gsd-core/workflows/diagnose-issues.md": "2971c699d52f1b85",
|
||||
"gsd-core/workflows/discovery-phase.md": "724408336596c50c",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "310b08dc31710cbc",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "92e43cd12200c610",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "5c0d1ca6abda0383",
|
||||
"gsd-core/workflows/discuss-phase.md": "3a62a8d4c374c69f",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "83ca3ea4fac785af",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "929b7cb0c99c7b9e",
|
||||
"gsd-core/workflows/profile-user.md": "248d59a31948e0ed",
|
||||
"gsd-core/workflows/progress.md": "8fc3404087f50b95",
|
||||
"gsd-core/workflows/quick.md": "10cc8ddd4bc3f0ee",
|
||||
"gsd-core/workflows/quick.md": "9bdccb7c6a5530c9",
|
||||
"gsd-core/workflows/reapply-patches.md": "a0e9b53f90abceb2",
|
||||
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
|
||||
"gsd-core/workflows/remove-workspace.md": "446847e71aa52504",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/qwen.json
vendored
10
tests/fixtures/golden-install-parity/qwen.json
vendored
@@ -94,7 +94,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "cc1efc2942164744",
|
||||
"gsd-core/references/model-profiles.md": "0b7e06ed2e4abac8",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -207,13 +207,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "3014ff90115f0daf",
|
||||
"gsd-core/workflows/check-todos.md": "ec8c22920f6b2df1",
|
||||
"gsd-core/workflows/cleanup.md": "6a18b165752e3087",
|
||||
"gsd-core/workflows/code-review-fix.md": "e14afce87cf0d420",
|
||||
"gsd-core/workflows/code-review.md": "d6d385b6dadae5a0",
|
||||
"gsd-core/workflows/code-review-fix.md": "f3725ae9d685bed2",
|
||||
"gsd-core/workflows/code-review.md": "29125604bed2c467",
|
||||
"gsd-core/workflows/complete-milestone.md": "40085d32b15805c8",
|
||||
"gsd-core/workflows/debug.md": "c23d067580b09f63",
|
||||
"gsd-core/workflows/diagnose-issues.md": "652ae26975f82242",
|
||||
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "3865afc23e9cd46a",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "18712f78bb960ec8",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "8644b72386a51241",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "3102430dfad9c012",
|
||||
@@ -279,7 +279,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "cef0f65b16d500b4",
|
||||
"gsd-core/workflows/profile-user.md": "263c0693563d98da",
|
||||
"gsd-core/workflows/progress.md": "3b1b2142a74af85c",
|
||||
"gsd-core/workflows/quick.md": "0a9a7dacc73f8e53",
|
||||
"gsd-core/workflows/quick.md": "bb9a4a145a7edc36",
|
||||
"gsd-core/workflows/reapply-patches.md": "de0ee8acfe7245b2",
|
||||
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
|
||||
"gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/trae.json
vendored
10
tests/fixtures/golden-install-parity/trae.json
vendored
@@ -94,7 +94,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "41b0af6f5f77af81",
|
||||
"gsd-core/references/model-profiles.md": "b4527b0f255d193f",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -207,13 +207,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "c482645c5d1ead46",
|
||||
"gsd-core/workflows/check-todos.md": "0dda8236355e8c9c",
|
||||
"gsd-core/workflows/cleanup.md": "82f65f5214ecd748",
|
||||
"gsd-core/workflows/code-review-fix.md": "959a65773b325ed2",
|
||||
"gsd-core/workflows/code-review.md": "b31bbd7dbe5ed303",
|
||||
"gsd-core/workflows/code-review-fix.md": "f2761f7f8c4a5674",
|
||||
"gsd-core/workflows/code-review.md": "47663a2922756c5e",
|
||||
"gsd-core/workflows/complete-milestone.md": "6e918b72bd885426",
|
||||
"gsd-core/workflows/debug.md": "197d3642a6704de5",
|
||||
"gsd-core/workflows/diagnose-issues.md": "9274b11a3db98c65",
|
||||
"gsd-core/workflows/discovery-phase.md": "b32b6197b66c9a13",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "3d05b08eae75c7bd",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "e376b1cf29379df4",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "4509657816c5566a",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "c9cd7db62e76ebcb",
|
||||
@@ -279,7 +279,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "79fd55b88ea2db9c",
|
||||
"gsd-core/workflows/profile-user.md": "672821e6b1266645",
|
||||
"gsd-core/workflows/progress.md": "94768f835b0b8908",
|
||||
"gsd-core/workflows/quick.md": "9bbf907e39688638",
|
||||
"gsd-core/workflows/quick.md": "a568015cccb53615",
|
||||
"gsd-core/workflows/reapply-patches.md": "21b38c374f19fd78",
|
||||
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
|
||||
"gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663",
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "b3ac5dc094bce1dc",
|
||||
"gsd-core/references/model-profiles.md": "09aa53e3f1764a41",
|
||||
"gsd-core/references/mvp-concepts.md": "3464783eaaef5c10",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e562947d1bf5c5a4",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -207,13 +207,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "fe7bb7c978f305a2",
|
||||
"gsd-core/workflows/check-todos.md": "afc840fceb07bf99",
|
||||
"gsd-core/workflows/cleanup.md": "93c14107979a4428",
|
||||
"gsd-core/workflows/code-review-fix.md": "9b2de93a6087774d",
|
||||
"gsd-core/workflows/code-review.md": "2f0ebfb7a2e33764",
|
||||
"gsd-core/workflows/code-review-fix.md": "b99b1f20bb27c291",
|
||||
"gsd-core/workflows/code-review.md": "faa87faf07ae765a",
|
||||
"gsd-core/workflows/complete-milestone.md": "f463bf4e86ac26f6",
|
||||
"gsd-core/workflows/debug.md": "52243eb936a43150",
|
||||
"gsd-core/workflows/diagnose-issues.md": "447072aa72385271",
|
||||
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f57000d5c5fb178a",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "b091b3d3e580dd29",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "290c0d83d783f9f6",
|
||||
"gsd-core/workflows/discuss-phase.md": "ce4e6abce9cb2d4e",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "cb49f485c4a1f09e",
|
||||
@@ -279,7 +279,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "acd59f915d018ad4",
|
||||
"gsd-core/workflows/profile-user.md": "c4313672b81b5bcd",
|
||||
"gsd-core/workflows/progress.md": "18813a345bd2343a",
|
||||
"gsd-core/workflows/quick.md": "3acd391cc0c3813a",
|
||||
"gsd-core/workflows/quick.md": "b7813e1810c683e1",
|
||||
"gsd-core/workflows/reapply-patches.md": "d449a23d3acf6379",
|
||||
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
|
||||
"gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/zcode.json
vendored
10
tests/fixtures/golden-install-parity/zcode.json
vendored
@@ -165,7 +165,7 @@
|
||||
"gsd-core/references/loop-hook-dispatch.md": "32e5dfb4dba76987",
|
||||
"gsd-core/references/mandatory-initial-read.md": "fe59abce693717cf",
|
||||
"gsd-core/references/model-profile-resolution.md": "f32bb05102839767",
|
||||
"gsd-core/references/model-profiles.md": "8484ad9799b7f680",
|
||||
"gsd-core/references/model-profiles.md": "e067ad3df6770db1",
|
||||
"gsd-core/references/mvp-concepts.md": "72f7e8b1f8ae7118",
|
||||
"gsd-core/references/phase-argument-parsing.md": "e5bbb985f3bc3e34",
|
||||
"gsd-core/references/planner-antipatterns.md": "7ed54ec1e2cc54ac",
|
||||
@@ -278,13 +278,13 @@
|
||||
"gsd-core/workflows/autonomous.md": "7e5683728ef33707",
|
||||
"gsd-core/workflows/check-todos.md": "bdeaf43f9c61e0cc",
|
||||
"gsd-core/workflows/cleanup.md": "0daa2f2720c111f1",
|
||||
"gsd-core/workflows/code-review-fix.md": "c323f7098b671bd6",
|
||||
"gsd-core/workflows/code-review.md": "ad450c6ef8459dad",
|
||||
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
|
||||
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
|
||||
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
|
||||
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
|
||||
"gsd-core/workflows/diagnose-issues.md": "aa8d787db8f3c46c",
|
||||
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "1e229f6d21831d60",
|
||||
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
|
||||
"gsd-core/workflows/discuss-phase-power.md": "0841f7dc6e9a054a",
|
||||
"gsd-core/workflows/discuss-phase.md": "d0a67b6f77b4d339",
|
||||
"gsd-core/workflows/discuss-phase/modes/advisor.md": "db488d74d080c653",
|
||||
@@ -350,7 +350,7 @@
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "e23bea0a69c0bb4b",
|
||||
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
|
||||
"gsd-core/workflows/quick.md": "5747c2d8a37affbb",
|
||||
"gsd-core/workflows/quick.md": "12917fdc9a624a0c",
|
||||
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
|
||||
@@ -1039,79 +1039,136 @@ describe('Phase 4 regression: capabilityStatesById gates on active (not enabled)
|
||||
});
|
||||
});
|
||||
|
||||
// ─── ADR-1244 D2 fail-closed gate injection ────────────────────────────────────
|
||||
// ─── ADR-1244 D2: load-failed capability gates FAIL OPEN with a loud warning (#2009) ──
|
||||
|
||||
describe('ADR-1244 D2: fail-closed gate injection for skipped overlay caps with gates', () => {
|
||||
// Verifies that cmdLoopRenderHooks injects a BLOCKING synthetic gate at the
|
||||
// declared point when an overlay capability that declares a gate is skipped at
|
||||
// load time due to an incompatible engines.gsd version constraint.
|
||||
//
|
||||
// Fixture: overlay cap declares a gate at execute:wave:post with engines.gsd: ">=99.0.0"
|
||||
// → loadRegistry skips it → records it in _overlay.blockedGates
|
||||
// → cmdLoopRenderHooks injects a blocking=true, onError=halt gate at execute:wave:post
|
||||
describe('ADR-1244 D2: load-failed capability gates fail OPEN with a loud warning (#2009)', () => {
|
||||
// Decision (#2009): a capability that fails to LOAD must not block the loop.
|
||||
// cmdLoopRenderHooks injects NO gate (the loop proceeds — fail open) and emits a
|
||||
// loud warning naming the load reason and the exact `gsd capability remove <id>`
|
||||
// remediation, both to STDERR (the channel host workflows actually surface) and
|
||||
// in the envelope's `warnings` array. Previously it injected a blocking=true,
|
||||
// onError=halt gate that halted every declared point project-wide.
|
||||
|
||||
test('skipped gate-kind overlay cap → BLOCKING synthetic gate at its declared point', (t) => {
|
||||
const overlayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'loop-fail-closed-'));
|
||||
t.after(() => cleanup(overlayHome));
|
||||
|
||||
// Write an overlay capability that:
|
||||
// - declares a gate at execute:wave:post
|
||||
// - has engines.gsd: ">=99.0.0" (incompatible → will be skipped at load)
|
||||
const capId = 'fail-closed-gate-cap';
|
||||
// Helper: write an overlay cap with an incompatible engines.gsd (→ skipped at load)
|
||||
// that declares gate(s) at the given point(s). `capId` may be an invalid id to
|
||||
// exercise the sanitization path.
|
||||
function writeSkippedGateCap(overlayHome, capId, gatePoints) {
|
||||
const capDir = path.join(overlayHome, '.gsd', 'capabilities', capId);
|
||||
fs.mkdirSync(capDir, { recursive: true });
|
||||
const capManifest = {
|
||||
id: capId,
|
||||
role: 'feature',
|
||||
version: '1.0.0',
|
||||
title: 'Fail Closed Gate Cap',
|
||||
description: 'ADR-1244 D2 fail-closed wiring test',
|
||||
title: 'Load-Failed Gate Cap',
|
||||
description: 'ADR-1244 D2 fail-open wiring test',
|
||||
tier: 'standard',
|
||||
requires: [],
|
||||
engines: { gsd: '>=99.0.0' }, // intentionally incompatible → always skipped
|
||||
engines: { gsd: '>=99.0.0' }, // intentionally incompatible → always skipped at load
|
||||
runtimeCompat: { supported: ['*'], unsupported: [] },
|
||||
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [],
|
||||
gates: [{ point: 'execute:wave:post', check: 'always-pass', blocking: true, onError: 'halt' }],
|
||||
gates: gatePoints.map((point) => ({ point, check: { query: 'always-pass' }, blocking: true, onError: 'halt' })),
|
||||
};
|
||||
fs.writeFileSync(path.join(capDir, 'capability.json'), JSON.stringify(capManifest), 'utf8');
|
||||
}
|
||||
|
||||
// Invoke gsd-tools via subprocess so stdout is the real fd-1 (io.cjs writes via writeSync).
|
||||
// Set GSD_HOME to the overlay home so loadRegistry picks up the incompatible cap.
|
||||
function renderHooks(overlayHome, point, extraArgs = []) {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[GSD_TOOLS, 'loop', 'render-hooks', 'execute:wave:post', '--cwd', overlayHome],
|
||||
{
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env, GSD_HOME: overlayHome },
|
||||
},
|
||||
[GSD_TOOLS, 'loop', 'render-hooks', point, '--cwd', overlayHome, ...extraArgs],
|
||||
{ cwd: ROOT, encoding: 'utf8', env: { ...process.env, GSD_HOME: overlayHome } },
|
||||
);
|
||||
assert.strictEqual(result.status, 0, `Expected exit 0 at ${point}. stderr: ` + (result.stderr || ''));
|
||||
return result;
|
||||
}
|
||||
|
||||
assert.strictEqual(result.status, 0, 'Expected exit 0. stderr: ' + (result.stderr || ''));
|
||||
|
||||
let envelope;
|
||||
function parseEnvelope(result) {
|
||||
try {
|
||||
envelope = JSON.parse(result.stdout.trim());
|
||||
return JSON.parse(result.stdout.trim());
|
||||
} catch {
|
||||
assert.fail('loop render-hooks output must be valid JSON; got: ' + result.stdout.slice(0, 300));
|
||||
}
|
||||
}
|
||||
|
||||
// The synthetic blocking gate must be present in activeHooks
|
||||
const syntheticGate = Array.isArray(envelope.activeHooks)
|
||||
? envelope.activeHooks.find((h) => h.capId === capId && h.kind === 'gate')
|
||||
test('load-failed gate-kind overlay cap → NO gate injected (fail open) + loud warning on stderr and in envelope', (t) => {
|
||||
const overlayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'loop-fail-open-'));
|
||||
t.after(() => cleanup(overlayHome));
|
||||
|
||||
const capId = 'load-failed-gate-cap';
|
||||
writeSkippedGateCap(overlayHome, capId, ['execute:wave:post']);
|
||||
|
||||
const result = renderHooks(overlayHome, 'execute:wave:post');
|
||||
const envelope = parseEnvelope(result);
|
||||
|
||||
// AC2 / AC-a: fail OPEN — NO gate (blocking or otherwise) is injected for the
|
||||
// load-failed cap, so the loop proceeds.
|
||||
const anyGate = Array.isArray(envelope.activeHooks)
|
||||
? envelope.activeHooks.find((h) => h.capId === capId)
|
||||
: undefined;
|
||||
assert.ok(
|
||||
syntheticGate !== undefined,
|
||||
`activeHooks must contain a synthetic gate attributed to ${capId} (fail-closed injection). ` +
|
||||
'Got: ' + JSON.stringify(envelope.activeHooks),
|
||||
assert.strictEqual(
|
||||
anyGate, undefined,
|
||||
'no hook must be injected for a load-failed cap (fail open). Got: ' + JSON.stringify(envelope.activeHooks),
|
||||
);
|
||||
assert.strictEqual(syntheticGate.blocking, true, 'synthetic gate must be blocking=true');
|
||||
assert.strictEqual(syntheticGate.onError, 'halt', 'synthetic gate must have onError=halt');
|
||||
|
||||
// The rendered markdown must also reference the gate cap
|
||||
// AC-b: a loud warning is surfaced in the envelope `warnings` channel...
|
||||
const warnEnv = (envelope.warnings || []).find((w) => w.includes(capId));
|
||||
assert.ok(warnEnv, 'envelope.warnings must name the load-failed cap. Got: ' + JSON.stringify(envelope.warnings));
|
||||
// AC1: ...carrying the exact remediation, and making clear the gate is not enforced.
|
||||
assert.ok(
|
||||
typeof envelope.rendered === 'string' && envelope.rendered.includes(capId),
|
||||
'rendered output must reference the fail-closed gate cap. Got: ' + envelope.rendered,
|
||||
warnEnv.includes(`gsd capability remove ${capId}`),
|
||||
'warning must include the `gsd capability remove <id>` remediation. Got: ' + warnEnv,
|
||||
);
|
||||
assert.match(warnEnv, /skipped|not enforced|failing open/i, 'warning must say the gate is not enforced. Got: ' + warnEnv);
|
||||
|
||||
// ...and ALSO to stderr (the channel host workflows actually see).
|
||||
assert.ok(
|
||||
result.stderr.includes(`gsd capability remove ${capId}`),
|
||||
'stderr must carry the loud fail-open warning with remediation. Got: ' + result.stderr,
|
||||
);
|
||||
});
|
||||
|
||||
test('load-failed cap declaring gates at ship:pre AND verify:post → neither point blocks (project can ship & verify)', (t) => {
|
||||
const overlayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'loop-fail-open-2pt-'));
|
||||
t.after(() => cleanup(overlayHome));
|
||||
|
||||
const capId = 'load-failed-two-point-cap';
|
||||
writeSkippedGateCap(overlayHome, capId, ['ship:pre', 'verify:post']);
|
||||
|
||||
for (const point of ['ship:pre', 'verify:post']) {
|
||||
const result = renderHooks(overlayHome, point);
|
||||
const envelope = parseEnvelope(result);
|
||||
const injected = envelope.activeHooks.find((h) => h.capId === capId);
|
||||
assert.strictEqual(
|
||||
injected, undefined,
|
||||
`${point} must NOT inject any hook for a load-failed cap (fail open). Got: ` + JSON.stringify(envelope.activeHooks),
|
||||
);
|
||||
const warn = (envelope.warnings || []).find((w) => w.includes(`gsd capability remove ${capId}`));
|
||||
assert.ok(warn, `${point} must surface a fail-open warning with remediation. Got: ` + JSON.stringify(envelope.warnings));
|
||||
}
|
||||
});
|
||||
|
||||
test('security: an invalid (non-kebab) capability id is withheld — no runnable remove command is rendered (#2009 review)', (t) => {
|
||||
const overlayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'loop-fail-open-evil-'));
|
||||
t.after(() => cleanup(overlayHome));
|
||||
|
||||
// A directory name that is a valid POSIX filename but NOT a valid capability
|
||||
// id, and contains a shell metacharacter. It must never appear inside a
|
||||
// runnable `gsd capability remove <id>` command in the surfaced warning.
|
||||
const evilId = 'Bad;Cap';
|
||||
writeSkippedGateCap(overlayHome, evilId, ['ship:pre']);
|
||||
|
||||
const result = renderHooks(overlayHome, 'ship:pre');
|
||||
const envelope = parseEnvelope(result);
|
||||
|
||||
const combined = (envelope.warnings || []).join('\n') + '\n' + result.stderr;
|
||||
// The raw metacharacter id must not be embedded in a runnable remove command.
|
||||
assert.ok(
|
||||
!combined.includes(`gsd capability remove ${evilId}`),
|
||||
'invalid capability id must NOT be placed in a runnable remove command. Got: ' + combined,
|
||||
);
|
||||
// A warning is still surfaced (fail-open is loud), using the withheld-id form.
|
||||
assert.match(
|
||||
combined, /invalid id \(withheld\)/,
|
||||
'an invalid id must be reported via the withheld-id placeholder. Got: ' + combined,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,6 +120,36 @@ describe('resolveModelInternal', () => {
|
||||
assert.ok(typeof model === 'string' && model.length > 0);
|
||||
});
|
||||
|
||||
// #2072 acceptance: these two catalog agents' config MUST resolve — the bug was
|
||||
// that the workflows never threaded the resolved value, not that the resolver
|
||||
// ignored it. These assert the value the (now-threaded) spawns receive.
|
||||
test('#2072: model_overrides applies to gsd-code-reviewer', () => {
|
||||
writeConfig(tmpDir, { model_overrides: { 'gsd-code-reviewer': 'my-custom-model' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-reviewer'), 'my-custom-model');
|
||||
});
|
||||
|
||||
test('#2072: model_overrides applies to gsd-assumptions-analyzer', () => {
|
||||
writeConfig(tmpDir, { model_overrides: { 'gsd-assumptions-analyzer': 'my-custom-model' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-assumptions-analyzer'), 'my-custom-model');
|
||||
});
|
||||
|
||||
test('#2072: models.verification tier applies to gsd-code-reviewer', () => {
|
||||
writeConfig(tmpDir, { models: { verification: 'opus' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-reviewer'), 'opus');
|
||||
});
|
||||
|
||||
test('#2072: models.discuss tier applies to gsd-assumptions-analyzer', () => {
|
||||
writeConfig(tmpDir, { models: { discuss: 'opus' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-assumptions-analyzer'), 'opus');
|
||||
});
|
||||
|
||||
test('#2072: model_overrides + models.execution apply to gsd-code-fixer', () => {
|
||||
writeConfig(tmpDir, { model_overrides: { 'gsd-code-fixer': 'my-custom-model' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-fixer'), 'my-custom-model');
|
||||
writeConfig(tmpDir, { models: { execution: 'opus' } });
|
||||
assert.strictEqual(resolveModelInternal(tmpDir, 'gsd-code-fixer'), 'opus');
|
||||
});
|
||||
|
||||
test('runtime non-claude + model_profile_overrides for runtime tier', () => {
|
||||
writeConfig(tmpDir, {
|
||||
runtime: 'codex',
|
||||
|
||||
150
tests/model-routing-spawn-threading.test.cjs
Normal file
150
tests/model-routing-spawn-threading.test.cjs
Normal file
@@ -0,0 +1,150 @@
|
||||
// allow-test-rule: source-text-is-the-product #2072
|
||||
// Workflow .md files ARE the deployed orchestration contract the runtime executes;
|
||||
// asserting that a spawn threads a resolved model= is asserting the deployed contract.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* #2072 — model_overrides / models.<phaseType> were silently inert for
|
||||
* gsd-assumptions-analyzer and gsd-code-reviewer on Claude: the resolver honored
|
||||
* them, but the workflows spawned the agents with NO model= param, so the resolved
|
||||
* value never reached the Agent tool and the agents inherited the session model.
|
||||
*
|
||||
* Fix contract: every spawn of these two catalog agents must thread a resolved
|
||||
* model=, and the workflow must obtain that model (inline `resolve-model` for the
|
||||
* single-agent workflows, or the `reviewer_model` field of the init.quick bundle).
|
||||
*/
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const WF = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const read = (rel) => fs.readFileSync(path.join(WF, rel), 'utf-8');
|
||||
|
||||
// Every .md under gsd-core/workflows (incl. nested steps/ and modes/).
|
||||
function allWorkflowMd() {
|
||||
const out = [];
|
||||
(function walk(dir, rel) {
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
if (e.isDirectory()) walk(path.join(dir, e.name), path.join(rel, e.name));
|
||||
else if (e.name.endsWith('.md')) out.push(path.join(rel, e.name));
|
||||
}
|
||||
})(WF, '');
|
||||
return out;
|
||||
}
|
||||
|
||||
// Return the full text of every `Agent( … )` call in `content`, tracking string
|
||||
// state (triple- and single-double-quoted) and paren depth so a prompt body's own
|
||||
// parens/quotes don't end the call early. Order-independent: a call's params are
|
||||
// captured whether subagent_type= appears before or after the prompt.
|
||||
function agentCalls(content) {
|
||||
const calls = [];
|
||||
const re = /Agent\(/g;
|
||||
let m;
|
||||
while ((m = re.exec(content)) !== null) {
|
||||
let i = m.index + m[0].length;
|
||||
let depth = 1;
|
||||
let tq = false; // inside """ … """
|
||||
let sq = false; // inside " … "
|
||||
while (i < content.length && depth > 0) {
|
||||
if (tq) {
|
||||
if (content.startsWith('"""', i)) { tq = false; i += 3; continue; }
|
||||
i++; continue;
|
||||
}
|
||||
if (sq) {
|
||||
if (content[i] === '\\') { i += 2; continue; }
|
||||
if (content[i] === '"') { sq = false; }
|
||||
i++; continue;
|
||||
}
|
||||
if (content.startsWith('"""', i)) { tq = true; i += 3; continue; }
|
||||
if (content[i] === '"') { sq = true; i++; continue; }
|
||||
if (content[i] === '(') { depth++; }
|
||||
else if (content[i] === ')') { depth--; }
|
||||
i++;
|
||||
}
|
||||
calls.push(content.slice(m.index, i));
|
||||
re.lastIndex = i; // don't re-scan inside this call
|
||||
}
|
||||
return calls;
|
||||
}
|
||||
|
||||
describe('#2072: routed-agent spawns thread the resolved model', () => {
|
||||
test('discuss-phase-assumptions.md resolves + threads gsd-assumptions-analyzer model', () => {
|
||||
const c = read('discuss-phase-assumptions.md');
|
||||
assert.match(c, /resolve-model gsd-assumptions-analyzer/, 'must resolve the routed model');
|
||||
assert.match(
|
||||
c,
|
||||
/subagent_type="gsd-assumptions-analyzer",\s*model="\{ANALYZER_MODEL\}"/,
|
||||
'spawn must thread the resolved model=',
|
||||
);
|
||||
});
|
||||
|
||||
test('code-review.md resolves + threads gsd-code-reviewer model', () => {
|
||||
const c = read('code-review.md');
|
||||
assert.match(c, /resolve-model gsd-code-reviewer/);
|
||||
assert.match(c, /subagent_type="gsd-code-reviewer",\s*model="\{REVIEWER_MODEL\}"/);
|
||||
});
|
||||
|
||||
test('code-review-fix.md re-review resolves + threads gsd-code-reviewer model', () => {
|
||||
const c = read('code-review-fix.md');
|
||||
assert.match(c, /resolve-model gsd-code-reviewer/);
|
||||
assert.match(c, /subagent_type="gsd-code-reviewer",\s*model="\{REVIEWER_MODEL\}"/);
|
||||
});
|
||||
|
||||
test('quick.md review step threads gsd-code-reviewer own model (not executor_model)', () => {
|
||||
const c = read('quick.md');
|
||||
// reviewer_model comes from the init.quick bundle; the spawn must use it.
|
||||
assert.match(c, /subagent_type="gsd-code-reviewer",\s*\n\s*model="\{reviewer_model\}"/);
|
||||
assert.doesNotMatch(
|
||||
c,
|
||||
/subagent_type="gsd-code-reviewer",\s*\n\s*model="\{executor_model\}"/,
|
||||
'reviewer must not reuse the executor model (own model_overrides would be ignored)',
|
||||
);
|
||||
});
|
||||
|
||||
test('code-review-fix.md threads gsd-code-fixer model at both fixer spawns', () => {
|
||||
const c = read('code-review-fix.md');
|
||||
assert.match(c, /resolve-model gsd-code-fixer/, 'must resolve the fixer model');
|
||||
const fixerSpawns = c.match(/subagent_type="gsd-code-fixer", model="\{FIXER_MODEL\}"/g) || [];
|
||||
assert.strictEqual(fixerSpawns.length, 2, 'both gsd-code-fixer spawns must thread FIXER_MODEL');
|
||||
});
|
||||
|
||||
// Parity guard: EVERY spawn of the fixed routed agents across ALL workflows must
|
||||
// carry a model= in its Agent(...) call, so a new silently-inert spawn cannot
|
||||
// regress. Uses a quote/paren-aware scan of the WHOLE Agent(...) call, so it holds
|
||||
// for single-line and multi-line calls, multiple spawns per file, and either param
|
||||
// ordering (subagent_type= before OR after the prompt body).
|
||||
test('no spawn of the fixed routed agents is missing a model= (parity guard)', () => {
|
||||
const ROUTED = /subagent_type="(gsd-code-reviewer|gsd-assumptions-analyzer|gsd-code-fixer)"/;
|
||||
const offenders = [];
|
||||
for (const rel of allWorkflowMd()) {
|
||||
for (const call of agentCalls(read(rel))) {
|
||||
const routed = call.match(ROUTED);
|
||||
if (routed && !/\bmodel\s*=/.test(call)) {
|
||||
offenders.push(`${rel}: ${routed[1]} spawn missing model=`);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.deepEqual(offenders, [], `routed-agent spawn(s) missing model=:\n${offenders.join('\n')}`);
|
||||
});
|
||||
|
||||
// The scanner itself must catch a prompt-first, un-threaded spawn (the exact blind
|
||||
// spot a naive "params before prompt=" heuristic misses) — otherwise the guard
|
||||
// above could pass vacuously.
|
||||
test('parity guard detects a prompt-first spawn that omits model=', () => {
|
||||
const synthetic = [
|
||||
'Agent(',
|
||||
' prompt="""do the thing (with parens) and a " quote""",',
|
||||
' subagent_type="gsd-code-reviewer"',
|
||||
')',
|
||||
].join('\n');
|
||||
const [call] = agentCalls(synthetic);
|
||||
assert.ok(/subagent_type="gsd-code-reviewer"/.test(call), 'scanner must capture the prompt-first subagent_type');
|
||||
assert.ok(!/\bmodel\s*=/.test(call), 'and correctly see that model= is absent');
|
||||
// A well-formed prompt-first spawn WITH model= must be accepted.
|
||||
const ok = agentCalls(synthetic.replace(')', ' model="{reviewer_model}"\n)'))[0];
|
||||
assert.ok(/\bmodel\s*=/.test(ok));
|
||||
});
|
||||
});
|
||||
@@ -57,9 +57,13 @@ const fs = require('fs');
|
||||
// Write pid to lock file so acquireStateLock sees a live pid and retries.
|
||||
fs.writeFileSync(workerData.lockPath, String(process.pid));
|
||||
parentPort.postMessage({ pid: process.pid });
|
||||
// Synchronous sleep — blocks this worker thread for holdMs ms.
|
||||
const buf = new Int32Array(new SharedArrayBuffer(4));
|
||||
Atomics.wait(buf, 0, 0, workerData.holdMs);
|
||||
// Hold the lock until the writer signals it has made its first FAILED lock
|
||||
// attempt (deterministic contention), or until holdMs elapses as a safety cap.
|
||||
// Atomics.wait blocks this thread while releaseFlag[0] === 0. A fixed timer here
|
||||
// was racy: on a slow/loaded runner the writer's spawn+init could exceed the
|
||||
// timer, so the lock released before the writer ever contended (lockAttempts:1).
|
||||
const releaseFlag = new Int32Array(workerData.releaseSab);
|
||||
Atomics.wait(releaseFlag, 0, 0, workerData.holdMs);
|
||||
// Release the lock.
|
||||
try { fs.unlinkSync(workerData.lockPath); } catch { /* already gone */ }
|
||||
parentPort.postMessage({ done: true });
|
||||
@@ -88,17 +92,29 @@ global.SharedArrayBuffer = StubSAB;
|
||||
// path — without this witness, a no-retry success would yield sabCount === 1
|
||||
// from BOTH pre-fix and post-fix code (the SAB is allocated unconditionally
|
||||
// post-fix, and exactly once for the single successful open pre-fix), giving
|
||||
// a false-pass against the bug. The 1000ms holdMs + 200ms SUT retry delay
|
||||
// guarantees >=4 attempts even on the slowest CI runners.
|
||||
// a false-pass against the bug. Contention is deterministic here: this worker
|
||||
// signals the holder to release only after its first failed attempt, so a
|
||||
// retry always occurs (lockAttempts >= 2) regardless of runner speed.
|
||||
const realOpenSync = fs.openSync.bind(fs);
|
||||
const releaseFlag = new Int32Array(workerData.releaseSab);
|
||||
let lockAttempts = 0;
|
||||
fs.openSync = function(filePath, flags, mode) {
|
||||
if (typeof filePath === 'string' && filePath.endsWith('.lock') &&
|
||||
const isLockCreate = typeof filePath === 'string' && filePath.endsWith('.lock') &&
|
||||
typeof flags === 'number' &&
|
||||
(flags & fs.constants.O_CREAT) && (flags & fs.constants.O_EXCL)) {
|
||||
lockAttempts++;
|
||||
(flags & fs.constants.O_CREAT) && (flags & fs.constants.O_EXCL);
|
||||
if (isLockCreate) lockAttempts++;
|
||||
try {
|
||||
return realOpenSync(filePath, flags, mode);
|
||||
} catch (e) {
|
||||
// On the FIRST failed atomic-create (lock is held → contention proven),
|
||||
// signal the holder worker to release so the next retry succeeds. Makes the
|
||||
// retry path deterministic regardless of worker-spawn latency.
|
||||
if (isLockCreate && lockAttempts === 1) {
|
||||
Atomics.store(releaseFlag, 0, 1);
|
||||
Atomics.notify(releaseFlag, 0);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return realOpenSync(filePath, flags, mode);
|
||||
};
|
||||
|
||||
// Delete cache entry to ensure a fresh require picks up the stubbed constructor.
|
||||
@@ -160,17 +176,18 @@ describe('perf #316: acquireStateLock hoists sleep buffer — exactly one SAB pe
|
||||
{ timeout: 8000 },
|
||||
async () => {
|
||||
// ── Worker A: hold the lock for 1000ms ─────────────────────────────────
|
||||
// state.cjs retry delay = 200ms + 0-50ms jitter; 1000ms hold guarantees
|
||||
// >=4 retries even on the slowest CI worker (~200ms spawn + 4 retry
|
||||
// intervals ~1000ms ≈ hold duration). The lockAttempts assertion below
|
||||
// proves the retry path was exercised end-to-end.
|
||||
// The holder releases the lock only when the writer signals its first
|
||||
// failed lock attempt (see WRITER_WORKER_CODE), so the writer is guaranteed
|
||||
// to contend at least once regardless of worker-spawn latency. holdMs is now
|
||||
// only a safety cap in case that signal never arrives.
|
||||
const holdMs = 1000;
|
||||
const releaseSab = new SharedArrayBuffer(4);
|
||||
let resolveLockWritten;
|
||||
const lockWritten = new Promise((resolve) => { resolveLockWritten = resolve; });
|
||||
const holderDone = new Promise((resolve, reject) => {
|
||||
holderWorker = new Worker(HOLDER_WORKER_CODE, {
|
||||
eval: true,
|
||||
workerData: { lockPath, holdMs },
|
||||
workerData: { lockPath, holdMs, releaseSab },
|
||||
});
|
||||
holderWorker.on('message', (msg) => {
|
||||
if (msg.pid !== undefined) resolveLockWritten();
|
||||
@@ -223,6 +240,7 @@ describe('perf #316: acquireStateLock hoists sleep buffer — exactly one SAB pe
|
||||
statePath,
|
||||
content: MINIMAL_STATE_MD,
|
||||
tmpDir,
|
||||
releaseSab,
|
||||
},
|
||||
});
|
||||
writerWorker.on('message', resolve);
|
||||
@@ -259,8 +277,8 @@ describe('perf #316: acquireStateLock hoists sleep buffer — exactly one SAB pe
|
||||
assert.ok(
|
||||
writeResult.lockAttempts >= 2,
|
||||
'SUT must have entered the retry path (>=1 failed lock attempt before success). ' +
|
||||
'Got lockAttempts: ' + writeResult.lockAttempts + '. The 1000ms holdMs + 200ms ' +
|
||||
'SUT retry delay guarantees >=2 attempts on any CI runner.'
|
||||
'Got lockAttempts: ' + writeResult.lockAttempts + '. The holder releases only ' +
|
||||
'after the writer signals its first failed attempt, so contention is deterministic.'
|
||||
);
|
||||
|
||||
// THE KEY INVARIANT:
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
"autonomous.md": 42474,
|
||||
"check-todos.md": 9475,
|
||||
"cleanup.md": 9941,
|
||||
"code-review-fix.md": 23934,
|
||||
"code-review.md": 31646,
|
||||
"code-review-fix.md": 24320,
|
||||
"code-review.md": 31916,
|
||||
"complete-milestone.md": 31071,
|
||||
"debug.md": 13549,
|
||||
"diagnose-issues.md": 12864,
|
||||
"discovery-phase.md": 8651,
|
||||
"discuss-phase-assumptions.md": 27028,
|
||||
"discuss-phase-assumptions.md": 27302,
|
||||
"discuss-phase-power.md": 11273,
|
||||
"discuss-phase.md": 31986,
|
||||
"do.md": 10353,
|
||||
@@ -59,7 +59,7 @@
|
||||
"pr-branch.md": 15963,
|
||||
"profile-user.md": 21246,
|
||||
"progress.md": 30599,
|
||||
"quick.md": 50452,
|
||||
"quick.md": 50470,
|
||||
"reapply-patches.md": 20312,
|
||||
"remove-phase.md": 8513,
|
||||
"remove-workspace.md": 7551,
|
||||
|
||||
Reference in New Issue
Block a user