@rpamis/comet 0.4.0-beta.4 → 0.4.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -4
  3. package/assets/manifest.json +1 -1
  4. package/assets/skills/comet/SKILL.md +30 -28
  5. package/assets/skills/comet/reference/auto-transition.md +1 -1
  6. package/assets/skills/comet/reference/comet-yaml-fields.md +8 -4
  7. package/assets/skills/comet/reference/context-recovery.md +2 -2
  8. package/assets/skills/comet/reference/decision-point.md +11 -0
  9. package/assets/skills/comet/reference/subagent-dispatch.md +6 -5
  10. package/assets/skills/comet/rules/comet-phase-guard.en.md +23 -28
  11. package/assets/skills/comet/rules/comet-phase-guard.md +22 -28
  12. package/assets/skills/comet/scripts/comet-runtime.mjs +682 -309
  13. package/assets/skills/comet-any/SKILL.md +1 -1
  14. package/assets/skills/comet-any/reference/authored-zone-example.md +10 -5
  15. package/assets/skills/comet-any/reference/subagents/pause-points-author.md +14 -11
  16. package/assets/skills/comet-any/reference/subagents/skill-reviewer.md +2 -0
  17. package/assets/skills/comet-any/reference/subagents/workflow-entry-author.md +5 -4
  18. package/assets/skills/comet-archive/SKILL.md +33 -13
  19. package/assets/skills/comet-build/SKILL.md +37 -29
  20. package/assets/skills/comet-design/SKILL.md +23 -22
  21. package/assets/skills/comet-hotfix/SKILL.md +46 -31
  22. package/assets/skills/comet-open/SKILL.md +97 -58
  23. package/assets/skills/comet-tweak/SKILL.md +34 -19
  24. package/assets/skills/comet-verify/SKILL.md +37 -59
  25. package/assets/skills-zh/comet/SKILL.md +30 -28
  26. package/assets/skills-zh/comet/reference/auto-transition.md +1 -1
  27. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +8 -4
  28. package/assets/skills-zh/comet/reference/context-recovery.md +2 -2
  29. package/assets/skills-zh/comet/reference/decision-point.md +11 -0
  30. package/assets/skills-zh/comet/reference/subagent-dispatch.md +6 -5
  31. package/assets/skills-zh/comet-any/SKILL.md +1 -1
  32. package/assets/skills-zh/comet-any/reference/authored-zone-example.md +10 -5
  33. package/assets/skills-zh/comet-any/reference/subagents/pause-points-author.md +14 -12
  34. package/assets/skills-zh/comet-any/reference/subagents/skill-reviewer.md +2 -0
  35. package/assets/skills-zh/comet-any/reference/subagents/workflow-entry-author.md +5 -4
  36. package/assets/skills-zh/comet-archive/SKILL.md +33 -13
  37. package/assets/skills-zh/comet-build/SKILL.md +37 -29
  38. package/assets/skills-zh/comet-design/SKILL.md +23 -22
  39. package/assets/skills-zh/comet-hotfix/SKILL.md +45 -30
  40. package/assets/skills-zh/comet-open/SKILL.md +96 -57
  41. package/assets/skills-zh/comet-tweak/SKILL.md +33 -18
  42. package/assets/skills-zh/comet-verify/SKILL.md +37 -59
  43. package/dist/app/commands/doctor.d.ts.map +1 -1
  44. package/dist/app/commands/doctor.js +62 -15
  45. package/dist/app/commands/doctor.js.map +1 -1
  46. package/dist/app/commands/init.d.ts +1 -1
  47. package/dist/app/commands/init.d.ts.map +1 -1
  48. package/dist/app/commands/init.js +58 -14
  49. package/dist/app/commands/init.js.map +1 -1
  50. package/dist/app/commands/status.d.ts +1 -0
  51. package/dist/app/commands/status.d.ts.map +1 -1
  52. package/dist/app/commands/status.js +10 -0
  53. package/dist/app/commands/status.js.map +1 -1
  54. package/dist/app/commands/uninstall.d.ts +3 -0
  55. package/dist/app/commands/uninstall.d.ts.map +1 -1
  56. package/dist/app/commands/uninstall.js +70 -36
  57. package/dist/app/commands/uninstall.js.map +1 -1
  58. package/dist/app/commands/update.d.ts.map +1 -1
  59. package/dist/app/commands/update.js +213 -34
  60. package/dist/app/commands/update.js.map +1 -1
  61. package/dist/domains/bundle/bundle-platform.d.ts +1 -0
  62. package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
  63. package/dist/domains/bundle/bundle-platform.js +6 -2
  64. package/dist/domains/bundle/bundle-platform.js.map +1 -1
  65. package/dist/domains/comet-classic/classic-branch-binding.d.ts +51 -0
  66. package/dist/domains/comet-classic/classic-branch-binding.d.ts.map +1 -0
  67. package/dist/domains/comet-classic/classic-branch-binding.js +107 -0
  68. package/dist/domains/comet-classic/classic-branch-binding.js.map +1 -0
  69. package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -1
  70. package/dist/domains/comet-classic/classic-current-change.js +37 -20
  71. package/dist/domains/comet-classic/classic-current-change.js.map +1 -1
  72. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  73. package/dist/domains/comet-classic/classic-guard.js +50 -16
  74. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  75. package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
  76. package/dist/domains/comet-classic/classic-hook-guard.js +138 -21
  77. package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
  78. package/dist/domains/comet-classic/classic-resolver.js +1 -1
  79. package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
  80. package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
  81. package/dist/domains/comet-classic/classic-state-command.js +113 -14
  82. package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
  83. package/dist/domains/comet-classic/classic-state-events.d.ts +1 -1
  84. package/dist/domains/comet-classic/classic-state-events.d.ts.map +1 -1
  85. package/dist/domains/comet-classic/classic-state.d.ts +4 -2
  86. package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
  87. package/dist/domains/comet-classic/classic-state.js +16 -1
  88. package/dist/domains/comet-classic/classic-state.js.map +1 -1
  89. package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
  90. package/dist/domains/comet-classic/classic-transitions.js +17 -2
  91. package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
  92. package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
  93. package/dist/domains/comet-classic/classic-validate-command.js +13 -1
  94. package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
  95. package/dist/domains/dashboard/web/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  96. package/dist/domains/dashboard/web/assets/arc-TGVUG2WN.js +1 -0
  97. package/dist/domains/dashboard/web/assets/architectureDiagram-3BPJPVTR-ASHmiHoP.js +36 -0
  98. package/dist/domains/dashboard/web/assets/blockDiagram-GPEHLZMM-CwASxBKe.js +132 -0
  99. package/dist/domains/dashboard/web/assets/c4Diagram-AAUBKEIU-BU8z_WNk.js +10 -0
  100. package/dist/domains/dashboard/web/assets/channel-kp6Ng2JJ.js +1 -0
  101. package/dist/domains/dashboard/web/assets/chunk-2J33WTMH-CBGlUx4_.js +1 -0
  102. package/dist/domains/dashboard/web/assets/chunk-4BX2VUAB-CS-iGcFz.js +1 -0
  103. package/dist/domains/dashboard/web/assets/chunk-55IACEB6-BUtCVJat.js +1 -0
  104. package/dist/domains/dashboard/web/assets/chunk-727SXJPM-C94tj_pf.js +206 -0
  105. package/dist/domains/dashboard/web/assets/chunk-AQP2D5EJ-BFOKcpdG.js +231 -0
  106. package/dist/domains/dashboard/web/assets/chunk-FMBD7UC4-Dmx8B-st.js +15 -0
  107. package/dist/domains/dashboard/web/assets/chunk-ND2GUHAM-DYV00CNn.js +1 -0
  108. package/dist/domains/dashboard/web/assets/chunk-QZHKN3VN-DMJJyQnw.js +1 -0
  109. package/dist/domains/dashboard/web/assets/classDiagram-4FO5ZUOK-yBDjgMY_.js +1 -0
  110. package/dist/domains/dashboard/web/assets/classDiagram-v2-Q7XG4LA2-yBDjgMY_.js +1 -0
  111. package/dist/domains/dashboard/web/assets/cose-bilkent-S5V4N54A-P4nE2rea.js +1 -0
  112. package/dist/domains/dashboard/web/assets/cynefin-VYW2F7L2-C-R1HXDv.js +166 -0
  113. package/dist/domains/dashboard/web/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
  114. package/dist/domains/dashboard/web/assets/dagre-BM42HDAG-BH8d-C9z.js +4 -0
  115. package/dist/domains/dashboard/web/assets/defaultLocale-DX6XiGOO.js +1 -0
  116. package/dist/domains/dashboard/web/assets/diagram-2AECGRRQ-DpXTgQIH.js +43 -0
  117. package/dist/domains/dashboard/web/assets/diagram-5GNKFQAL-DSiFJzSw.js +10 -0
  118. package/dist/domains/dashboard/web/assets/diagram-KO2AKTUF-DULMkben.js +3 -0
  119. package/dist/domains/dashboard/web/assets/diagram-LMA3HP47-Pi8m_NO9.js +24 -0
  120. package/dist/domains/dashboard/web/assets/diagram-OG6HWLK6-BSe6bCWm.js +24 -0
  121. package/dist/domains/dashboard/web/assets/erDiagram-TEJ5UH35-zb-FEcbq.js +85 -0
  122. package/dist/domains/dashboard/web/assets/flowDiagram-I6XJVG4X-Dr-Gk0z2.js +162 -0
  123. package/dist/domains/dashboard/web/assets/ganttDiagram-6RSMTGT7-qs6Zb2A6.js +292 -0
  124. package/dist/domains/dashboard/web/assets/gitGraphDiagram-PVQCEYII-BQ314orT.js +106 -0
  125. package/dist/domains/dashboard/web/assets/graph--OzhPTMs.js +1 -0
  126. package/dist/domains/dashboard/web/assets/index-BLAfkNTu.css +1 -0
  127. package/dist/domains/dashboard/web/assets/index-DlSYK56b.js +139 -0
  128. package/dist/domains/dashboard/web/assets/index-DtLUOcAW.js +24 -0
  129. package/dist/domains/dashboard/web/assets/index-QO0z6F4w.js +10 -0
  130. package/dist/domains/dashboard/web/assets/infoDiagram-5YYISTIA-O0bQp98A.js +2 -0
  131. package/dist/domains/dashboard/web/assets/init-Gi6I4Gst.js +1 -0
  132. package/dist/domains/dashboard/web/assets/ishikawaDiagram-YF4QCWOH-BPC7wbKh.js +70 -0
  133. package/dist/domains/dashboard/web/assets/journeyDiagram-JHISSGLW-ohViOd4b.js +139 -0
  134. package/dist/domains/dashboard/web/assets/kanban-definition-UN3LZRKU-CHleNdbG.js +89 -0
  135. package/dist/domains/dashboard/web/assets/katex-HP8lGamR.js +257 -0
  136. package/dist/domains/dashboard/web/assets/layout-SsrduOYp.js +1 -0
  137. package/dist/domains/dashboard/web/assets/linear-CyqiW--Q.js +1 -0
  138. package/dist/domains/dashboard/web/assets/marked.esm-DECB3bxB.js +64 -0
  139. package/dist/domains/dashboard/web/assets/mermaid.core-qS9XvtaK.js +301 -0
  140. package/dist/domains/dashboard/web/assets/mindmap-definition-RKZ34NQL-oaGkqO9l.js +96 -0
  141. package/dist/domains/dashboard/web/assets/ordinal-Cboi1Yqb.js +1 -0
  142. package/dist/domains/dashboard/web/assets/pieDiagram-4H26LBE5-CPcB-AFh.js +30 -0
  143. package/dist/domains/dashboard/web/assets/purify.es-VaSPOPhr.js +3 -0
  144. package/dist/domains/dashboard/web/assets/quadrantDiagram-W4KKPZXB-LFy52Qtj.js +7 -0
  145. package/dist/domains/dashboard/web/assets/requirementDiagram-4Y6WPE33-BdjeaYP_.js +84 -0
  146. package/dist/domains/dashboard/web/assets/sankeyDiagram-5OEKKPKP-DDJ4styL.js +40 -0
  147. package/dist/domains/dashboard/web/assets/sequenceDiagram-3UESZ5HK-BL5bFNoz.js +162 -0
  148. package/dist/domains/dashboard/web/assets/stateDiagram-AJRCARHV-BNUfikui.js +1 -0
  149. package/dist/domains/dashboard/web/assets/stateDiagram-v2-BHNVJYJU-DGrsmWts.js +1 -0
  150. package/dist/domains/dashboard/web/assets/timeline-definition-PNZ67QCA--ps2uu6L.js +120 -0
  151. package/dist/domains/dashboard/web/assets/vennDiagram-CIIHVFJN-C2q8q26c.js +34 -0
  152. package/dist/domains/dashboard/web/assets/wardleyDiagram-YWT4CUSO-BUH9gXqd.js +78 -0
  153. package/dist/domains/dashboard/web/assets/xychartDiagram-2RQKCTM6-B-DielEy.js +7 -0
  154. package/dist/domains/dashboard/web/index.html +16 -16
  155. package/dist/domains/factory/package.d.ts.map +1 -1
  156. package/dist/domains/factory/package.js +20 -6
  157. package/dist/domains/factory/package.js.map +1 -1
  158. package/dist/domains/integrations/openspec.d.ts +4 -1
  159. package/dist/domains/integrations/openspec.d.ts.map +1 -1
  160. package/dist/domains/integrations/openspec.js +56 -4
  161. package/dist/domains/integrations/openspec.js.map +1 -1
  162. package/dist/domains/skill/find.d.ts.map +1 -1
  163. package/dist/domains/skill/find.js +7 -1
  164. package/dist/domains/skill/find.js.map +1 -1
  165. package/dist/domains/skill/json-object.d.ts +13 -0
  166. package/dist/domains/skill/json-object.d.ts.map +1 -0
  167. package/dist/domains/skill/json-object.js +24 -0
  168. package/dist/domains/skill/json-object.js.map +1 -0
  169. package/dist/domains/skill/platform-inspect.d.ts +9 -0
  170. package/dist/domains/skill/platform-inspect.d.ts.map +1 -0
  171. package/dist/domains/skill/platform-inspect.js +149 -0
  172. package/dist/domains/skill/platform-inspect.js.map +1 -0
  173. package/dist/domains/skill/platform-install.d.ts +16 -6
  174. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  175. package/dist/domains/skill/platform-install.js +277 -144
  176. package/dist/domains/skill/platform-install.js.map +1 -1
  177. package/dist/domains/skill/uninstall.d.ts.map +1 -1
  178. package/dist/domains/skill/uninstall.js +204 -141
  179. package/dist/domains/skill/uninstall.js.map +1 -1
  180. package/dist/platform/fs/file-system.d.ts +3 -3
  181. package/dist/platform/fs/file-system.d.ts.map +1 -1
  182. package/dist/platform/fs/file-system.js +21 -11
  183. package/dist/platform/fs/file-system.js.map +1 -1
  184. package/dist/platform/install/platforms.d.ts +4 -0
  185. package/dist/platform/install/platforms.d.ts.map +1 -1
  186. package/dist/platform/install/platforms.js +2 -0
  187. package/dist/platform/install/platforms.js.map +1 -1
  188. package/dist/platform/install/project-registry.d.ts +1 -0
  189. package/dist/platform/install/project-registry.d.ts.map +1 -1
  190. package/dist/platform/install/project-registry.js +9 -1
  191. package/dist/platform/install/project-registry.js.map +1 -1
  192. package/dist/platform/install/skill-root-owner.d.ts +15 -0
  193. package/dist/platform/install/skill-root-owner.d.ts.map +1 -0
  194. package/dist/platform/install/skill-root-owner.js +68 -0
  195. package/dist/platform/install/skill-root-owner.js.map +1 -0
  196. package/package.json +6 -1
  197. package/dist/domains/dashboard/web/assets/index-CIvN-PL8.js +0 -22
  198. package/dist/domains/dashboard/web/assets/index-DcyRPBnT.css +0 -1
  199. package/eval/.env +0 -28
@@ -0,0 +1,51 @@
1
+ export declare function liveGitBranch(cwd: string): string | null;
2
+ export declare function isGitWorkTree(cwd: string): boolean;
3
+ export type BranchBindingVerdict = {
4
+ status: 'not-applicable';
5
+ } | {
6
+ status: 'ok';
7
+ } | {
8
+ status: 'needs-heal';
9
+ branch: string;
10
+ } | {
11
+ status: 'unbound-detached';
12
+ } | {
13
+ status: 'drift';
14
+ boundBranch: string;
15
+ currentBranch: string | null;
16
+ };
17
+ export declare const BOUND_BRANCH_ISOLATIONS: readonly ["current", "branch", "worktree"];
18
+ export declare function requiresBranchBinding(isolation: string | null): boolean;
19
+ export declare function evaluateBranchBinding(input: {
20
+ isolation: string | null;
21
+ boundBranch: string | null;
22
+ currentBranch: string | null;
23
+ gitWorkTree?: boolean;
24
+ }): BranchBindingVerdict;
25
+ /**
26
+ * A `BranchBindingVerdict` joined with the live git facts it was computed
27
+ * from, so callers never re-derive them (or re-read the state file).
28
+ */
29
+ export type BranchBindingOutcome = (BranchBindingVerdict | {
30
+ status: 'healed';
31
+ branch: string;
32
+ }) & {
33
+ bindingRequired: boolean;
34
+ currentBranch: string | null;
35
+ };
36
+ /**
37
+ * Single entry point for every consumer of the branch binding: reads
38
+ * isolation/bound_branch straight from `.comet.yaml` (typed — a yaml `null`
39
+ * is a JS null, never a "null" string), spawns git lazily, and optionally
40
+ * performs the lazy heal. Read-only paths (the PreToolUse hook) must pass
41
+ * `heal: false` so resolving never writes to disk.
42
+ */
43
+ export declare function resolveBranchBinding(changeDir: string, options: {
44
+ heal: boolean;
45
+ cwd: string;
46
+ }): Promise<BranchBindingOutcome>;
47
+ export declare function healBoundBranch(changeDir: string, branch: string): Promise<void>;
48
+ export declare function driftBlockedMessage(change: string, boundBranch: string, currentBranch: string | null): string;
49
+ export declare function driftStaleReason(change: string, boundBranch: string, currentBranch: string | null): string;
50
+ export declare function unboundDetachedMessage(change: string): string;
51
+ //# sourceMappingURL=classic-branch-binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classic-branch-binding.d.ts","sourceRoot":"","sources":["../../../domains/comet-classic/classic-branch-binding.ts"],"names":[],"mappings":"AAMA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWxD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYlD;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC5B;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC9B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE3E,eAAO,MAAM,uBAAuB,4CAA6C,CAAC;AAElF,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEvE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,oBAAoB,CAYvB;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,oBAAoB,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACjG,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,oBAAoB,CAAC,CAwB/B;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYtF;AAMD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,CAER;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,107 @@
1
+ import { execFileSync } from 'child_process';
2
+ import { randomUUID } from 'crypto';
3
+ import { promises as fs } from 'fs';
4
+ import path from 'path';
5
+ import { parseDocument } from 'yaml';
6
+ export function liveGitBranch(cwd) {
7
+ try {
8
+ const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
9
+ cwd,
10
+ encoding: 'utf8',
11
+ stdio: ['ignore', 'pipe', 'ignore'],
12
+ }).trim();
13
+ return branch && branch !== 'HEAD' ? branch : null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export function isGitWorkTree(cwd) {
20
+ try {
21
+ return (execFileSync('git', ['rev-parse', '--is-inside-work-tree'], {
22
+ cwd,
23
+ encoding: 'utf8',
24
+ stdio: ['ignore', 'pipe', 'ignore'],
25
+ }).trim() === 'true');
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ export const BOUND_BRANCH_ISOLATIONS = ['current', 'branch', 'worktree'];
32
+ export function requiresBranchBinding(isolation) {
33
+ return BOUND_BRANCH_ISOLATIONS.includes(isolation);
34
+ }
35
+ export function evaluateBranchBinding(input) {
36
+ if (!requiresBranchBinding(input.isolation))
37
+ return { status: 'not-applicable' };
38
+ if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) {
39
+ return { status: 'not-applicable' };
40
+ }
41
+ if (input.boundBranch === null) {
42
+ return input.currentBranch === null
43
+ ? { status: 'unbound-detached' }
44
+ : { status: 'needs-heal', branch: input.currentBranch };
45
+ }
46
+ if (input.currentBranch === input.boundBranch)
47
+ return { status: 'ok' };
48
+ return { status: 'drift', boundBranch: input.boundBranch, currentBranch: input.currentBranch };
49
+ }
50
+ /**
51
+ * Single entry point for every consumer of the branch binding: reads
52
+ * isolation/bound_branch straight from `.comet.yaml` (typed — a yaml `null`
53
+ * is a JS null, never a "null" string), spawns git lazily, and optionally
54
+ * performs the lazy heal. Read-only paths (the PreToolUse hook) must pass
55
+ * `heal: false` so resolving never writes to disk.
56
+ */
57
+ export async function resolveBranchBinding(changeDir, options) {
58
+ const file = path.join(changeDir, '.comet.yaml');
59
+ const document = parseDocument(await fs.readFile(file, 'utf8'), { uniqueKeys: false });
60
+ if (document.errors.length > 0) {
61
+ throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`);
62
+ }
63
+ const record = (document.toJS() ?? {});
64
+ const isolation = typeof record.isolation === 'string' ? record.isolation : null;
65
+ const boundBranch = typeof record.bound_branch === 'string' && record.bound_branch !== ''
66
+ ? record.bound_branch
67
+ : null;
68
+ const bindingRequired = requiresBranchBinding(isolation);
69
+ const currentBranch = liveGitBranch(options.cwd);
70
+ const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null
71
+ ? isGitWorkTree(options.cwd)
72
+ : true;
73
+ const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree });
74
+ if (verdict.status === 'needs-heal' && options.heal) {
75
+ await healBoundBranch(changeDir, verdict.branch);
76
+ return { status: 'healed', branch: verdict.branch, bindingRequired, currentBranch };
77
+ }
78
+ return { ...verdict, bindingRequired, currentBranch };
79
+ }
80
+ export async function healBoundBranch(changeDir, branch) {
81
+ const file = path.join(changeDir, '.comet.yaml');
82
+ const document = parseDocument(await fs.readFile(file, 'utf8'), { uniqueKeys: false });
83
+ document.set('bound_branch', branch);
84
+ const temporary = `${file}.${randomUUID()}.tmp`;
85
+ try {
86
+ await fs.writeFile(temporary, document.toString(), 'utf8');
87
+ await fs.rename(temporary, file);
88
+ }
89
+ catch (error) {
90
+ await fs.rm(temporary, { force: true });
91
+ throw error;
92
+ }
93
+ }
94
+ function branchLabel(currentBranch) {
95
+ return currentBranch ?? 'detached HEAD';
96
+ }
97
+ export function driftBlockedMessage(change, boundBranch, currentBranch) {
98
+ return (`change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'.\n` +
99
+ `Next: ask the user to confirm — switch back to '${boundBranch}', or run \`comet state rebind ${change}\` after explicit confirmation.`);
100
+ }
101
+ export function driftStaleReason(change, boundBranch, currentBranch) {
102
+ return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`;
103
+ }
104
+ export function unboundDetachedMessage(change) {
105
+ return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`;
106
+ }
107
+ //# sourceMappingURL=classic-branch-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classic-branch-binding.js","sourceRoot":"","sources":["../../../domains/comet-classic/classic-branch-binding.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"classic-current-change.d.ts","sourceRoot":"","sources":["../../../domains/comet-classic/classic-current-change.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,sBAAsB,CAAA;CAAE,GACzD;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE7D;AA4ED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,sBAAsB,CAAC,CAkBjC;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA+BhG;AAED,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E"}
1
+ {"version":3,"file":"classic-current-change.d.ts","sourceRoot":"","sources":["../../../domains/comet-classic/classic-current-change.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,sBAAsB,CAAA;CAAE,GACzD;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE7D;AAgED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,sBAAsB,CAAC,CA4BjC;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAkDhG;AAED,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E"}
@@ -1,25 +1,12 @@
1
- import { execFileSync } from 'child_process';
2
1
  import { randomUUID } from 'crypto';
3
2
  import { promises as fs } from 'fs';
4
3
  import path from 'path';
4
+ import { driftStaleReason, resolveBranchBinding, unboundDetachedMessage, } from './classic-branch-binding.js';
5
5
  import { assertOpenSpecChangeName } from './classic-paths.js';
6
6
  import { readClassicState } from './classic-store.js';
7
7
  export function currentChangeFile(projectRoot) {
8
8
  return path.join(projectRoot, '.comet', 'current-change.json');
9
9
  }
10
- function currentBranch(projectRoot) {
11
- try {
12
- const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
13
- cwd: projectRoot,
14
- encoding: 'utf8',
15
- stdio: ['ignore', 'pipe', 'ignore'],
16
- }).trim();
17
- return branch && branch !== 'HEAD' ? branch : null;
18
- }
19
- catch {
20
- return null;
21
- }
22
- }
23
10
  function changeDirectory(projectRoot, changeName) {
24
11
  return path.join(projectRoot, 'openspec', 'changes', changeName);
25
12
  }
@@ -64,21 +51,32 @@ function parseSelection(source) {
64
51
  throw new Error('current change selection change must be a string');
65
52
  }
66
53
  assertOpenSpecChangeName(record.change);
67
- if (record.branch !== null && typeof record.branch !== 'string') {
54
+ // `branch` may be absent in files written by early 0.4.0-beta.6 builds.
55
+ if (record.branch !== undefined && record.branch !== null && typeof record.branch !== 'string') {
68
56
  throw new Error('current change selection branch must be a string or null');
69
57
  }
70
58
  return {
71
59
  version: 1,
72
60
  change: record.change,
73
- branch: record.branch,
61
+ branch: record.branch ?? null,
74
62
  };
75
63
  }
76
64
  export async function selectCurrentChange(projectRoot, changeName) {
77
65
  await validateActiveChange(projectRoot, changeName);
66
+ const outcome = await resolveBranchBinding(changeDirectory(projectRoot, changeName), {
67
+ heal: true,
68
+ cwd: projectRoot,
69
+ });
70
+ if (outcome.status === 'drift') {
71
+ throw new Error(driftStaleReason(changeName, outcome.boundBranch, outcome.currentBranch));
72
+ }
73
+ if (outcome.status === 'unbound-detached') {
74
+ throw new Error(unboundDetachedMessage(changeName));
75
+ }
78
76
  const selection = {
79
77
  version: 1,
80
78
  change: changeName,
81
- branch: currentBranch(projectRoot),
79
+ branch: outcome.currentBranch,
82
80
  };
83
81
  const file = currentChangeFile(projectRoot);
84
82
  const temporary = `${file}.${randomUUID()}.tmp`;
@@ -117,11 +115,30 @@ export async function resolveCurrentChange(projectRoot) {
117
115
  reason: error instanceof Error ? error.message : String(error),
118
116
  };
119
117
  }
120
- const branch = currentBranch(projectRoot);
121
- if (selection.branch !== null && branch !== selection.branch) {
118
+ // Resolution is a read path (the PreToolUse hook runs it on every tool
119
+ // call), so it never heals: heal happens on select/check/guard instead.
120
+ const outcome = await resolveBranchBinding(changeDirectory(projectRoot, selection.change), {
121
+ heal: false,
122
+ cwd: projectRoot,
123
+ });
124
+ if (outcome.status === 'drift') {
125
+ return {
126
+ status: 'stale',
127
+ reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch),
128
+ };
129
+ }
130
+ if (outcome.status === 'unbound-detached') {
131
+ return { status: 'stale', reason: unboundDetachedMessage(selection.change) };
132
+ }
133
+ if (outcome.status === 'ok') {
134
+ return { status: 'selected', selection };
135
+ }
136
+ // No bound branch governs yet (isolation unset or binding not healed):
137
+ // fall back to comparing against the branch recorded at selection time.
138
+ if (selection.branch !== null && outcome.currentBranch !== selection.branch) {
122
139
  return {
123
140
  status: 'stale',
124
- reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${branch ?? 'detached HEAD'}'`,
141
+ reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? 'detached HEAD'}'`,
125
142
  };
126
143
  }
127
144
  return { status: 'selected', selection };
@@ -1 +1 @@
1
- {"version":3,"file":"classic-current-change.js","sourceRoot":"","sources":["../../../domains/comet-classic/classic-current-change.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"classic-current-change.js","sourceRoot":"","sources":["../../../domains/comet-classic/classic-current-change.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"classic-guard.d.ts","sourceRoot":"","sources":["../../../domains/comet-classic/classic-guard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,kBAAkB,CAAC;AA64BpF,eAAO,MAAM,mBAAmB,EAAE,qBAmDjC,CAAC"}
1
+ {"version":3,"file":"classic-guard.d.ts","sourceRoot":"","sources":["../../../domains/comet-classic/classic-guard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,kBAAkB,CAAC;AAu7BpF,eAAO,MAAM,mBAAmB,EAAE,qBAmDjC,CAAC"}
@@ -12,6 +12,7 @@ import { CLASSIC_GUARD_TRANSITION_EVENT, applyClassicTransition } from './classi
12
12
  import { classicValidateCommand } from './classic-validate-command.js';
13
13
  import { readClassicState } from './classic-store.js';
14
14
  import { readClassicConfigValue } from './classic-project-config.js';
15
+ import { driftBlockedMessage, resolveBranchBinding, unboundDetachedMessage, } from './classic-branch-binding.js';
15
16
  const GREEN = '\u001b[32m';
16
17
  const RED = '\u001b[31m';
17
18
  const YELLOW = '\u001b[33m';
@@ -411,17 +412,43 @@ async function planTasksAllDone(changeDir) {
411
412
  }
412
413
  return pass();
413
414
  }
415
+ async function boundBranchMatches(changeDir, change) {
416
+ let outcome;
417
+ try {
418
+ outcome = await resolveBranchBinding(changeDir, { heal: true, cwd: process.cwd() });
419
+ }
420
+ catch (error) {
421
+ throw new GuardFailure(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
422
+ }
423
+ switch (outcome.status) {
424
+ case 'drift':
425
+ return fail(driftBlockedMessage(change, outcome.boundBranch, outcome.currentBranch));
426
+ case 'unbound-detached':
427
+ return fail(unboundDetachedMessage(change));
428
+ case 'healed':
429
+ return pass(`bound_branch lazily set to ${outcome.branch}`);
430
+ case 'needs-heal':
431
+ case 'ok':
432
+ case 'not-applicable':
433
+ return pass();
434
+ default: {
435
+ const exhaustive = outcome;
436
+ throw new Error(`unhandled branch binding status: ${JSON.stringify(exhaustive)}`);
437
+ }
438
+ }
439
+ }
414
440
  async function isolationSelected(changeDir, change) {
415
441
  const isolation = await readField(changeDir, 'isolation');
416
- if (isolation === 'branch' || isolation === 'worktree')
442
+ if (isolation === 'current' || isolation === 'branch' || isolation === 'worktree')
417
443
  return pass();
418
- return fail(`isolation must be branch or worktree, got '${isolation || 'null'}'\nNext: ask the user to choose branch or worktree, create the chosen isolation, then run:\n node "$COMET_STATE" set ${change} isolation <branch|worktree>`);
444
+ const allowedValues = '<current|branch|worktree>';
445
+ return fail(`isolation must be current, branch, or worktree, got '${isolation || 'null'}'\nNext: choose a valid workspace mode, prepare it when needed, then run:\n comet state set ${change} isolation ${allowedValues}`);
419
446
  }
420
447
  async function buildModeSelected(changeDir, change) {
421
448
  const buildMode = await readField(changeDir, 'build_mode');
422
449
  if (['subagent-driven-development', 'executing-plans', 'direct'].includes(buildMode))
423
450
  return pass();
424
- return fail(`build_mode must be selected before leaving build, got '${buildMode || 'null'}'\nNext: ask the user to choose an execution mode, then run:\n node "$COMET_STATE" set ${change} build_mode <subagent-driven-development|executing-plans>`);
451
+ return fail(`build_mode must be selected before leaving build, got '${buildMode || 'null'}'\nNext: ask the user to choose an execution mode, then run:\n comet state set ${change} build_mode <subagent-driven-development|executing-plans>`);
425
452
  }
426
453
  async function buildModeAllowedForWorkflow(changeDir) {
427
454
  const workflow = await readField(changeDir, 'workflow');
@@ -442,7 +469,7 @@ async function subagentDispatchConfirmed(changeDir, change) {
442
469
  return pass();
443
470
  if (subagentDispatch === 'confirmed')
444
471
  return pass();
445
- return fail(`subagent_dispatch must be confirmed before using build_mode=subagent-driven-development\nNext: confirm the current platform has a real background subagent/Task/multi-agent dispatcher, then run:\n node "$COMET_STATE" set ${change} subagent_dispatch confirmed\nOr ask the user to switch to executing-plans and run:\n node "$COMET_STATE" set ${change} build_mode executing-plans`);
472
+ return fail(`subagent_dispatch must be confirmed before using build_mode=subagent-driven-development\nNext: confirm the current platform has a real background subagent/Task/multi-agent dispatcher, then run:\n comet state set ${change} subagent_dispatch confirmed\nIf dispatch is unavailable, return to /comet-build Step 2 with subagent-driven-development removed. When executing-plans is the only valid mode, run:\n comet state set ${change} build_mode executing-plans`);
446
473
  }
447
474
  async function tddModeSelected(changeDir, change) {
448
475
  const workflow = await readField(changeDir, 'workflow');
@@ -451,7 +478,7 @@ async function tddModeSelected(changeDir, change) {
451
478
  const tddMode = await readField(changeDir, 'tdd_mode');
452
479
  if (tddMode === 'tdd' || tddMode === 'direct')
453
480
  return pass();
454
- return fail(`tdd_mode must be tdd or direct for full workflow, got '${tddMode || 'null'}'\nNext: ask the user to choose TDD enforcement level, then run:\n node "$COMET_STATE" set ${change} tdd_mode <tdd|direct>`);
481
+ return fail(`tdd_mode must be tdd or direct for full workflow, got '${tddMode || 'null'}'\nNext: ask the user to choose TDD enforcement level, then run:\n comet state set ${change} tdd_mode <tdd|direct>`);
455
482
  }
456
483
  async function reviewModeSelected(changeDir, change) {
457
484
  const workflow = await readField(changeDir, 'workflow');
@@ -461,7 +488,7 @@ async function reviewModeSelected(changeDir, change) {
461
488
  if (reviewMode === 'off' || reviewMode === 'standard' || reviewMode === 'thorough') {
462
489
  return pass();
463
490
  }
464
- return fail(`review_mode must be off, standard, or thorough before leaving build, got '${reviewMode || 'null'}'\nNext: ask the user to choose review strength, then run:\n node "$COMET_STATE" set ${change} review_mode <off|standard|thorough>`);
491
+ return fail(`review_mode must be off, standard, or thorough before leaving build, got '${reviewMode || 'null'}'\nNext: ask the user to choose review strength, then run:\n comet state set ${change} review_mode <off|standard|thorough>`);
465
492
  }
466
493
  async function verificationReportExists(changeDir) {
467
494
  const report = await readField(changeDir, 'verification_report');
@@ -493,7 +520,7 @@ async function designDocRecorded(changeDir, change) {
493
520
  const designDoc = await readField(changeDir, 'design_doc');
494
521
  if (designDoc && designDoc !== 'null' && existsSync(designDoc))
495
522
  return pass();
496
- return fail(`design_doc must point to an existing Superpowers Design Doc for full workflow before leaving design.\nNext: create the Design Doc and run: node "$COMET_STATE" set ${change} design_doc <path>`);
523
+ return fail(`design_doc must point to an existing Superpowers Design Doc for full workflow before leaving design.\nNext: create the Design Doc and run: comet state set ${change} design_doc <path>`);
497
524
  }
498
525
  async function designHandoffContextValid(changeDir, change) {
499
526
  const context = await readField(changeDir, 'handoff_context');
@@ -502,18 +529,18 @@ async function designHandoffContextValid(changeDir, change) {
502
529
  return fail(`handoff_context is missing from .comet.yaml\nNext: run node "$COMET_HANDOFF" ${change} design --write before invoking Superpowers.`);
503
530
  }
504
531
  if (!(await nonempty(context))) {
505
- return fail(`handoff_context does not point to a non-empty file: ${context}\nNext: regenerate the design handoff with comet-handoff.mjs.`);
532
+ return fail(`handoff_context does not point to a non-empty file: ${context}\nNext: regenerate the design handoff with comet handoff ${change} design --write.`);
506
533
  }
507
534
  if (!/^[a-f0-9]{64}$/u.test(recordedHash)) {
508
- return fail(`handoff_hash is missing or invalid: ${recordedHash || 'null'}\nNext: regenerate the design handoff with comet-handoff.mjs.`);
535
+ return fail(`handoff_hash is missing or invalid: ${recordedHash || 'null'}\nNext: regenerate the design handoff with comet handoff ${change} design --write.`);
509
536
  }
510
537
  const actualHash = await computeHandoffHash(changeDir);
511
538
  if (actualHash !== recordedHash) {
512
- return fail(`OpenSpec artifacts changed after handoff was generated.\nExpected handoff_hash: ${recordedHash}\nActual handoff_hash: ${actualHash}\nNext: rerun comet-handoff.mjs so Superpowers receives the current OpenSpec context.`);
539
+ return fail(`OpenSpec artifacts changed after handoff was generated.\nExpected handoff_hash: ${recordedHash}\nActual handoff_hash: ${actualHash}\nNext: run comet handoff ${change} design --write so Superpowers receives the current OpenSpec context.`);
513
540
  }
514
541
  const markdown = `${context.replace(/\.json$/u, '')}.md`;
515
542
  if (!(await nonempty(markdown))) {
516
- return fail(`design handoff markdown is missing or empty: ${markdown}\nNext: regenerate the design handoff with comet-handoff.mjs.`);
543
+ return fail(`design handoff markdown is missing or empty: ${markdown}\nNext: regenerate the design handoff with comet handoff ${change} design --write.`);
517
544
  }
518
545
  return pass();
519
546
  }
@@ -660,6 +687,7 @@ async function guardDesignChecks(output, changeDir, change) {
660
687
  }
661
688
  async function guardBuildChecks(output, changeDir, change, run) {
662
689
  return runChecks(output, [
690
+ check('bound branch matches workspace mode', () => boundBranchMatches(changeDir, change)),
663
691
  check('isolation selected', () => isolationSelected(changeDir, change)),
664
692
  check('build_mode selected', () => buildModeSelected(changeDir, change)),
665
693
  check('build_mode allowed for workflow', () => buildModeAllowedForWorkflow(changeDir)),
@@ -686,6 +714,7 @@ async function guardBuildChecks(output, changeDir, change, run) {
686
714
  }
687
715
  async function guardVerifyChecks(output, changeDir, change, run) {
688
716
  return runChecks(output, [
717
+ check('bound branch matches workspace mode', () => boundBranchMatches(changeDir, change)),
689
718
  check('tasks.md all tasks checked', () => tasksAllDone(changeDir)),
690
719
  // Verification command runs after tasks check — no point running tests
691
720
  // if tasks.md is incomplete.
@@ -700,21 +729,26 @@ async function guardVerifyChecks(output, changeDir, change, run) {
700
729
  return pass();
701
730
  return documentLanguageMatchesConfigured(changeDir, report);
702
731
  }),
703
- check('branch_status=handled', async () => (await branchStatusHandled(changeDir)) ? pass() : fail('')),
704
732
  ]);
705
733
  }
706
- async function guardArchiveChecks(output, changeDir) {
734
+ async function guardArchiveChecks(output, changeDir, change) {
707
735
  return runChecks(output, [
736
+ check('bound branch matches workspace mode', () => boundBranchMatches(changeDir, change)),
708
737
  check('archived is true', async () => ((await archivedIsTrue(changeDir)) ? pass() : fail(''))),
709
738
  check('proposal.md exists', async () => (await nonempty(path.join(changeDir, 'proposal.md'))) ? pass() : fail('')),
710
739
  check('design.md exists', async () => (await nonempty(path.join(changeDir, 'design.md'))) ? pass() : fail('')),
711
740
  check('tasks.md all tasks checked', () => tasksAllDone(changeDir)),
741
+ check('branch_status=handled', async () => (await branchStatusHandled(changeDir)) ? pass() : fail('')),
712
742
  ]);
713
743
  }
714
- async function applyStateUpdate(output, change, changeDir, phase, context) {
744
+ async function applyStateUpdate(output, change, changeDir, phase) {
715
745
  const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
716
746
  if (!event)
717
747
  return;
748
+ // Re-read instead of reusing the run context captured before the checks:
749
+ // boundBranchMatches may have lazily healed bound_branch on disk, and a
750
+ // stale projection would write the pre-heal null back over it.
751
+ const context = await ensureClassicRuntimeRun(changeDir);
718
752
  const result = applyClassicTransition(context.classic, event);
719
753
  await transitionClassicRuntimeRun(changeDir, result.classic, context.run, {
720
754
  event,
@@ -768,7 +802,7 @@ export const classicGuardCommand = async (args, options) => {
768
802
  else if (phase === 'verify')
769
803
  blocked = await guardVerifyChecks(output, changeDir, change, runContext.run);
770
804
  else
771
- blocked = await guardArchiveChecks(output, changeDir);
805
+ blocked = await guardArchiveChecks(output, changeDir, change);
772
806
  if (blocked) {
773
807
  output.stderr.push('');
774
808
  output.stderr.push(red('BLOCKED — fix failing checks before proceeding to next phase'));
@@ -777,7 +811,7 @@ export const classicGuardCommand = async (args, options) => {
777
811
  output.stderr.push('');
778
812
  output.stderr.push(green('ALL CHECKS PASSED — ready for next phase'));
779
813
  if (flag === '--apply') {
780
- await applyStateUpdate(output, change, changeDir, phase, runContext);
814
+ await applyStateUpdate(output, change, changeDir, phase);
781
815
  }
782
816
  return output.toResult(0);
783
817
  }