@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
@@ -126,17 +126,17 @@ var require_visit = __commonJS({
126
126
  visit.BREAK = BREAK;
127
127
  visit.SKIP = SKIP;
128
128
  visit.REMOVE = REMOVE;
129
- function visit_(key, node, visitor, path22) {
130
- const ctrl = callVisitor(key, node, visitor, path22);
129
+ function visit_(key, node, visitor, path23) {
130
+ const ctrl = callVisitor(key, node, visitor, path23);
131
131
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
132
- replaceNode(key, path22, ctrl);
133
- return visit_(key, ctrl, visitor, path22);
132
+ replaceNode(key, path23, ctrl);
133
+ return visit_(key, ctrl, visitor, path23);
134
134
  }
135
135
  if (typeof ctrl !== "symbol") {
136
136
  if (identity.isCollection(node)) {
137
- path22 = Object.freeze(path22.concat(node));
137
+ path23 = Object.freeze(path23.concat(node));
138
138
  for (let i = 0; i < node.items.length; ++i) {
139
- const ci = visit_(i, node.items[i], visitor, path22);
139
+ const ci = visit_(i, node.items[i], visitor, path23);
140
140
  if (typeof ci === "number")
141
141
  i = ci - 1;
142
142
  else if (ci === BREAK)
@@ -147,13 +147,13 @@ var require_visit = __commonJS({
147
147
  }
148
148
  }
149
149
  } else if (identity.isPair(node)) {
150
- path22 = Object.freeze(path22.concat(node));
151
- const ck = visit_("key", node.key, visitor, path22);
150
+ path23 = Object.freeze(path23.concat(node));
151
+ const ck = visit_("key", node.key, visitor, path23);
152
152
  if (ck === BREAK)
153
153
  return BREAK;
154
154
  else if (ck === REMOVE)
155
155
  node.key = null;
156
- const cv = visit_("value", node.value, visitor, path22);
156
+ const cv = visit_("value", node.value, visitor, path23);
157
157
  if (cv === BREAK)
158
158
  return BREAK;
159
159
  else if (cv === REMOVE)
@@ -174,17 +174,17 @@ var require_visit = __commonJS({
174
174
  visitAsync.BREAK = BREAK;
175
175
  visitAsync.SKIP = SKIP;
176
176
  visitAsync.REMOVE = REMOVE;
177
- async function visitAsync_(key, node, visitor, path22) {
178
- const ctrl = await callVisitor(key, node, visitor, path22);
177
+ async function visitAsync_(key, node, visitor, path23) {
178
+ const ctrl = await callVisitor(key, node, visitor, path23);
179
179
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
180
- replaceNode(key, path22, ctrl);
181
- return visitAsync_(key, ctrl, visitor, path22);
180
+ replaceNode(key, path23, ctrl);
181
+ return visitAsync_(key, ctrl, visitor, path23);
182
182
  }
183
183
  if (typeof ctrl !== "symbol") {
184
184
  if (identity.isCollection(node)) {
185
- path22 = Object.freeze(path22.concat(node));
185
+ path23 = Object.freeze(path23.concat(node));
186
186
  for (let i = 0; i < node.items.length; ++i) {
187
- const ci = await visitAsync_(i, node.items[i], visitor, path22);
187
+ const ci = await visitAsync_(i, node.items[i], visitor, path23);
188
188
  if (typeof ci === "number")
189
189
  i = ci - 1;
190
190
  else if (ci === BREAK)
@@ -195,13 +195,13 @@ var require_visit = __commonJS({
195
195
  }
196
196
  }
197
197
  } else if (identity.isPair(node)) {
198
- path22 = Object.freeze(path22.concat(node));
199
- const ck = await visitAsync_("key", node.key, visitor, path22);
198
+ path23 = Object.freeze(path23.concat(node));
199
+ const ck = await visitAsync_("key", node.key, visitor, path23);
200
200
  if (ck === BREAK)
201
201
  return BREAK;
202
202
  else if (ck === REMOVE)
203
203
  node.key = null;
204
- const cv = await visitAsync_("value", node.value, visitor, path22);
204
+ const cv = await visitAsync_("value", node.value, visitor, path23);
205
205
  if (cv === BREAK)
206
206
  return BREAK;
207
207
  else if (cv === REMOVE)
@@ -228,23 +228,23 @@ var require_visit = __commonJS({
228
228
  }
229
229
  return visitor;
230
230
  }
231
- function callVisitor(key, node, visitor, path22) {
231
+ function callVisitor(key, node, visitor, path23) {
232
232
  if (typeof visitor === "function")
233
- return visitor(key, node, path22);
233
+ return visitor(key, node, path23);
234
234
  if (identity.isMap(node))
235
- return visitor.Map?.(key, node, path22);
235
+ return visitor.Map?.(key, node, path23);
236
236
  if (identity.isSeq(node))
237
- return visitor.Seq?.(key, node, path22);
237
+ return visitor.Seq?.(key, node, path23);
238
238
  if (identity.isPair(node))
239
- return visitor.Pair?.(key, node, path22);
239
+ return visitor.Pair?.(key, node, path23);
240
240
  if (identity.isScalar(node))
241
- return visitor.Scalar?.(key, node, path22);
241
+ return visitor.Scalar?.(key, node, path23);
242
242
  if (identity.isAlias(node))
243
- return visitor.Alias?.(key, node, path22);
243
+ return visitor.Alias?.(key, node, path23);
244
244
  return void 0;
245
245
  }
246
- function replaceNode(key, path22, node) {
247
- const parent = path22[path22.length - 1];
246
+ function replaceNode(key, path23, node) {
247
+ const parent = path23[path23.length - 1];
248
248
  if (identity.isCollection(parent)) {
249
249
  parent.items[key] = node;
250
250
  } else if (identity.isPair(parent)) {
@@ -854,10 +854,10 @@ var require_Collection = __commonJS({
854
854
  var createNode = require_createNode();
855
855
  var identity = require_identity();
856
856
  var Node = require_Node();
857
- function collectionFromPath(schema, path22, value) {
857
+ function collectionFromPath(schema, path23, value) {
858
858
  let v = value;
859
- for (let i = path22.length - 1; i >= 0; --i) {
860
- const k = path22[i];
859
+ for (let i = path23.length - 1; i >= 0; --i) {
860
+ const k = path23[i];
861
861
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
862
862
  const a = [];
863
863
  a[k] = v;
@@ -876,7 +876,7 @@ var require_Collection = __commonJS({
876
876
  sourceObjects: /* @__PURE__ */ new Map()
877
877
  });
878
878
  }
879
- var isEmptyPath = (path22) => path22 == null || typeof path22 === "object" && !!path22[Symbol.iterator]().next().done;
879
+ var isEmptyPath = (path23) => path23 == null || typeof path23 === "object" && !!path23[Symbol.iterator]().next().done;
880
880
  var Collection = class extends Node.NodeBase {
881
881
  constructor(type, schema) {
882
882
  super(type);
@@ -906,11 +906,11 @@ var require_Collection = __commonJS({
906
906
  * be a Pair instance or a `{ key, value }` object, which may not have a key
907
907
  * that already exists in the map.
908
908
  */
909
- addIn(path22, value) {
910
- if (isEmptyPath(path22))
909
+ addIn(path23, value) {
910
+ if (isEmptyPath(path23))
911
911
  this.add(value);
912
912
  else {
913
- const [key, ...rest] = path22;
913
+ const [key, ...rest] = path23;
914
914
  const node = this.get(key, true);
915
915
  if (identity.isCollection(node))
916
916
  node.addIn(rest, value);
@@ -924,8 +924,8 @@ var require_Collection = __commonJS({
924
924
  * Removes a value from the collection.
925
925
  * @returns `true` if the item was found and removed.
926
926
  */
927
- deleteIn(path22) {
928
- const [key, ...rest] = path22;
927
+ deleteIn(path23) {
928
+ const [key, ...rest] = path23;
929
929
  if (rest.length === 0)
930
930
  return this.delete(key);
931
931
  const node = this.get(key, true);
@@ -939,8 +939,8 @@ var require_Collection = __commonJS({
939
939
  * scalar values from their surrounding node; to disable set `keepScalar` to
940
940
  * `true` (collections are always returned intact).
941
941
  */
942
- getIn(path22, keepScalar) {
943
- const [key, ...rest] = path22;
942
+ getIn(path23, keepScalar) {
943
+ const [key, ...rest] = path23;
944
944
  const node = this.get(key, true);
945
945
  if (rest.length === 0)
946
946
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -958,8 +958,8 @@ var require_Collection = __commonJS({
958
958
  /**
959
959
  * Checks if the collection includes a value with the key `key`.
960
960
  */
961
- hasIn(path22) {
962
- const [key, ...rest] = path22;
961
+ hasIn(path23) {
962
+ const [key, ...rest] = path23;
963
963
  if (rest.length === 0)
964
964
  return this.has(key);
965
965
  const node = this.get(key, true);
@@ -969,8 +969,8 @@ var require_Collection = __commonJS({
969
969
  * Sets a value in this collection. For `!!set`, `value` needs to be a
970
970
  * boolean to add/remove the item from the set.
971
971
  */
972
- setIn(path22, value) {
973
- const [key, ...rest] = path22;
972
+ setIn(path23, value) {
973
+ const [key, ...rest] = path23;
974
974
  if (rest.length === 0) {
975
975
  this.set(key, value);
976
976
  } else {
@@ -3485,9 +3485,9 @@ var require_Document = __commonJS({
3485
3485
  this.contents.add(value);
3486
3486
  }
3487
3487
  /** Adds a value to the document. */
3488
- addIn(path22, value) {
3488
+ addIn(path23, value) {
3489
3489
  if (assertCollection(this.contents))
3490
- this.contents.addIn(path22, value);
3490
+ this.contents.addIn(path23, value);
3491
3491
  }
3492
3492
  /**
3493
3493
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -3562,14 +3562,14 @@ var require_Document = __commonJS({
3562
3562
  * Removes a value from the document.
3563
3563
  * @returns `true` if the item was found and removed.
3564
3564
  */
3565
- deleteIn(path22) {
3566
- if (Collection.isEmptyPath(path22)) {
3565
+ deleteIn(path23) {
3566
+ if (Collection.isEmptyPath(path23)) {
3567
3567
  if (this.contents == null)
3568
3568
  return false;
3569
3569
  this.contents = null;
3570
3570
  return true;
3571
3571
  }
3572
- return assertCollection(this.contents) ? this.contents.deleteIn(path22) : false;
3572
+ return assertCollection(this.contents) ? this.contents.deleteIn(path23) : false;
3573
3573
  }
3574
3574
  /**
3575
3575
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -3584,10 +3584,10 @@ var require_Document = __commonJS({
3584
3584
  * scalar values from their surrounding node; to disable set `keepScalar` to
3585
3585
  * `true` (collections are always returned intact).
3586
3586
  */
3587
- getIn(path22, keepScalar) {
3588
- if (Collection.isEmptyPath(path22))
3587
+ getIn(path23, keepScalar) {
3588
+ if (Collection.isEmptyPath(path23))
3589
3589
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3590
- return identity.isCollection(this.contents) ? this.contents.getIn(path22, keepScalar) : void 0;
3590
+ return identity.isCollection(this.contents) ? this.contents.getIn(path23, keepScalar) : void 0;
3591
3591
  }
3592
3592
  /**
3593
3593
  * Checks if the document includes a value with the key `key`.
@@ -3598,10 +3598,10 @@ var require_Document = __commonJS({
3598
3598
  /**
3599
3599
  * Checks if the document includes a value at `path`.
3600
3600
  */
3601
- hasIn(path22) {
3602
- if (Collection.isEmptyPath(path22))
3601
+ hasIn(path23) {
3602
+ if (Collection.isEmptyPath(path23))
3603
3603
  return this.contents !== void 0;
3604
- return identity.isCollection(this.contents) ? this.contents.hasIn(path22) : false;
3604
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path23) : false;
3605
3605
  }
3606
3606
  /**
3607
3607
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -3618,13 +3618,13 @@ var require_Document = __commonJS({
3618
3618
  * Sets a value in this document. For `!!set`, `value` needs to be a
3619
3619
  * boolean to add/remove the item from the set.
3620
3620
  */
3621
- setIn(path22, value) {
3622
- if (Collection.isEmptyPath(path22)) {
3621
+ setIn(path23, value) {
3622
+ if (Collection.isEmptyPath(path23)) {
3623
3623
  this.contents = value;
3624
3624
  } else if (this.contents == null) {
3625
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path22), value);
3625
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path23), value);
3626
3626
  } else if (assertCollection(this.contents)) {
3627
- this.contents.setIn(path22, value);
3627
+ this.contents.setIn(path23, value);
3628
3628
  }
3629
3629
  }
3630
3630
  /**
@@ -5584,9 +5584,9 @@ var require_cst_visit = __commonJS({
5584
5584
  visit.BREAK = BREAK;
5585
5585
  visit.SKIP = SKIP;
5586
5586
  visit.REMOVE = REMOVE;
5587
- visit.itemAtPath = (cst, path22) => {
5587
+ visit.itemAtPath = (cst, path23) => {
5588
5588
  let item = cst;
5589
- for (const [field2, index] of path22) {
5589
+ for (const [field2, index] of path23) {
5590
5590
  const tok = item?.[field2];
5591
5591
  if (tok && "items" in tok) {
5592
5592
  item = tok.items[index];
@@ -5595,23 +5595,23 @@ var require_cst_visit = __commonJS({
5595
5595
  }
5596
5596
  return item;
5597
5597
  };
5598
- visit.parentCollection = (cst, path22) => {
5599
- const parent = visit.itemAtPath(cst, path22.slice(0, -1));
5600
- const field2 = path22[path22.length - 1][0];
5598
+ visit.parentCollection = (cst, path23) => {
5599
+ const parent = visit.itemAtPath(cst, path23.slice(0, -1));
5600
+ const field2 = path23[path23.length - 1][0];
5601
5601
  const coll = parent?.[field2];
5602
5602
  if (coll && "items" in coll)
5603
5603
  return coll;
5604
5604
  throw new Error("Parent collection not found");
5605
5605
  };
5606
- function _visit(path22, item, visitor) {
5607
- let ctrl = visitor(item, path22);
5606
+ function _visit(path23, item, visitor) {
5607
+ let ctrl = visitor(item, path23);
5608
5608
  if (typeof ctrl === "symbol")
5609
5609
  return ctrl;
5610
5610
  for (const field2 of ["key", "value"]) {
5611
5611
  const token = item[field2];
5612
5612
  if (token && "items" in token) {
5613
5613
  for (let i = 0; i < token.items.length; ++i) {
5614
- const ci = _visit(Object.freeze(path22.concat([[field2, i]])), token.items[i], visitor);
5614
+ const ci = _visit(Object.freeze(path23.concat([[field2, i]])), token.items[i], visitor);
5615
5615
  if (typeof ci === "number")
5616
5616
  i = ci - 1;
5617
5617
  else if (ci === BREAK)
@@ -5622,10 +5622,10 @@ var require_cst_visit = __commonJS({
5622
5622
  }
5623
5623
  }
5624
5624
  if (typeof ctrl === "function" && field2 === "key")
5625
- ctrl = ctrl(item, path22);
5625
+ ctrl = ctrl(item, path23);
5626
5626
  }
5627
5627
  }
5628
- return typeof ctrl === "function" ? ctrl(item, path22) : ctrl;
5628
+ return typeof ctrl === "function" ? ctrl(item, path23) : ctrl;
5629
5629
  }
5630
5630
  exports.visit = visit;
5631
5631
  }
@@ -6927,14 +6927,14 @@ var require_parser = __commonJS({
6927
6927
  case "scalar":
6928
6928
  case "single-quoted-scalar":
6929
6929
  case "double-quoted-scalar": {
6930
- const fs21 = this.flowScalar(this.type);
6930
+ const fs22 = this.flowScalar(this.type);
6931
6931
  if (atNextItem || it.value) {
6932
- map.items.push({ start, key: fs21, sep: [] });
6932
+ map.items.push({ start, key: fs22, sep: [] });
6933
6933
  this.onKeyLine = true;
6934
6934
  } else if (it.sep) {
6935
- this.stack.push(fs21);
6935
+ this.stack.push(fs22);
6936
6936
  } else {
6937
- Object.assign(it, { key: fs21, sep: [] });
6937
+ Object.assign(it, { key: fs22, sep: [] });
6938
6938
  this.onKeyLine = true;
6939
6939
  }
6940
6940
  return;
@@ -7062,13 +7062,13 @@ var require_parser = __commonJS({
7062
7062
  case "scalar":
7063
7063
  case "single-quoted-scalar":
7064
7064
  case "double-quoted-scalar": {
7065
- const fs21 = this.flowScalar(this.type);
7065
+ const fs22 = this.flowScalar(this.type);
7066
7066
  if (!it || it.value)
7067
- fc.items.push({ start: [], key: fs21, sep: [] });
7067
+ fc.items.push({ start: [], key: fs22, sep: [] });
7068
7068
  else if (it.sep)
7069
- this.stack.push(fs21);
7069
+ this.stack.push(fs22);
7070
7070
  else
7071
- Object.assign(it, { key: fs21, sep: [] });
7071
+ Object.assign(it, { key: fs22, sep: [] });
7072
7072
  return;
7073
7073
  }
7074
7074
  case "flow-map-end":
@@ -7257,7 +7257,7 @@ var require_public_api = __commonJS({
7257
7257
  return docs;
7258
7258
  return Object.assign([], { empty: true }, composer$1.streamInfo());
7259
7259
  }
7260
- function parseDocument7(source, options = {}) {
7260
+ function parseDocument8(source, options = {}) {
7261
7261
  const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
7262
7262
  const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
7263
7263
  const composer$1 = new composer.Composer(options);
@@ -7283,7 +7283,7 @@ var require_public_api = __commonJS({
7283
7283
  } else if (options === void 0 && reviver && typeof reviver === "object") {
7284
7284
  options = reviver;
7285
7285
  }
7286
- const doc = parseDocument7(src, options);
7286
+ const doc = parseDocument8(src, options);
7287
7287
  if (!doc)
7288
7288
  return null;
7289
7289
  doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
@@ -7319,7 +7319,7 @@ var require_public_api = __commonJS({
7319
7319
  }
7320
7320
  exports.parse = parse2;
7321
7321
  exports.parseAllDocuments = parseAllDocuments;
7322
- exports.parseDocument = parseDocument7;
7322
+ exports.parseDocument = parseDocument8;
7323
7323
  exports.stringify = stringify;
7324
7324
  }
7325
7325
  });
@@ -7739,7 +7739,7 @@ function fullBuildConfigured(classic) {
7739
7739
  }
7740
7740
  function presetBuildConfigured(classic) {
7741
7741
  return Boolean(
7742
- classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation === "branch" && classic.verifyMode === "light"
7742
+ classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation !== null && classic.verifyMode === "light"
7743
7743
  );
7744
7744
  }
7745
7745
  function resolveBuild(profile, classic, evidence) {
@@ -7808,7 +7808,7 @@ var BUILD_PAUSES = ["plan-ready"];
7808
7808
  var SUBAGENT_DISPATCH = ["confirmed"];
7809
7809
  var TDD_MODES = ["tdd", "direct"];
7810
7810
  var REVIEW_MODES = ["off", "standard", "thorough"];
7811
- var ISOLATIONS = ["branch", "worktree"];
7811
+ var ISOLATIONS = ["current", "branch", "worktree"];
7812
7812
  var VERIFY_MODES = ["light", "full"];
7813
7813
  var VERIFY_RESULTS = ["pending", "pass", "fail"];
7814
7814
  var BRANCH_STATUSES = ["pending", "handled"];
@@ -7824,12 +7824,14 @@ var CLASSIC_WIRE_KEYS = [
7824
7824
  "tdd_mode",
7825
7825
  "review_mode",
7826
7826
  "isolation",
7827
+ "bound_branch",
7827
7828
  "verify_mode",
7828
7829
  "auto_transition",
7829
7830
  "base_ref",
7830
7831
  "design_doc",
7831
7832
  "plan",
7832
7833
  "verify_result",
7834
+ "verify_failures",
7833
7835
  "verification_report",
7834
7836
  "branch_status",
7835
7837
  "created_at",
@@ -7891,6 +7893,14 @@ function booleanValue(doc, key, nullable = true) {
7891
7893
  }
7892
7894
  return value;
7893
7895
  }
7896
+ function nonNegativeInteger(doc, key, fallback = 0) {
7897
+ const value = doc[key];
7898
+ if (value === null || value === void 0 || value === "") return fallback;
7899
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
7900
+ throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`);
7901
+ }
7902
+ return value;
7903
+ }
7894
7904
  function relativePath(doc, key) {
7895
7905
  const value = nullableString(doc, key);
7896
7906
  if (value === null) return null;
@@ -7933,12 +7943,14 @@ function classicStateFromDocument(doc) {
7933
7943
  tddMode: enumValue(doc, "tdd_mode", TDD_MODES),
7934
7944
  reviewMode: enumValue(doc, "review_mode", REVIEW_MODES),
7935
7945
  isolation: enumValue(doc, "isolation", ISOLATIONS),
7946
+ boundBranch: nullableString(doc, "bound_branch"),
7936
7947
  verifyMode: enumValue(doc, "verify_mode", VERIFY_MODES),
7937
7948
  autoTransition: booleanValue(doc, "auto_transition"),
7938
7949
  baseRef: nullableString(doc, "base_ref"),
7939
7950
  designDoc: relativePath(doc, "design_doc"),
7940
7951
  plan: relativePath(doc, "plan"),
7941
7952
  verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false),
7953
+ verifyFailures: nonNegativeInteger(doc, "verify_failures"),
7942
7954
  verificationReport: relativePath(doc, "verification_report"),
7943
7955
  branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
7944
7956
  createdAt: nullableString(doc, "created_at"),
@@ -7997,12 +8009,14 @@ function classicStateToDocument(state) {
7997
8009
  tdd_mode: state.tddMode,
7998
8010
  review_mode: state.reviewMode,
7999
8011
  isolation: state.isolation,
8012
+ bound_branch: state.boundBranch,
8000
8013
  verify_mode: state.verifyMode,
8001
8014
  auto_transition: state.autoTransition,
8002
8015
  base_ref: state.baseRef,
8003
8016
  design_doc: state.designDoc,
8004
8017
  plan: state.plan,
8005
8018
  verify_result: state.verifyResult,
8019
+ verify_failures: state.verifyFailures,
8006
8020
  verification_report: state.verificationReport,
8007
8021
  branch_status: state.branchStatus,
8008
8022
  created_at: state.createdAt,
@@ -9390,7 +9404,7 @@ var CLASSIC_TRANSITION_TABLE = {
9390
9404
  "verify-pass": {
9391
9405
  event: "verify-pass",
9392
9406
  from: "verify",
9393
- guardRefs: ["verification-report-present", "branch-status-handled"]
9407
+ guardRefs: ["verification-report-present"]
9394
9408
  },
9395
9409
  "verify-fail": {
9396
9410
  event: "verify-fail",
@@ -9450,18 +9464,22 @@ function applyClassicTransition(current, event, options = {}) {
9450
9464
  const preserveEvidence = classic.verifyResult === "fail";
9451
9465
  setField(classic, effects, "phase", "verify");
9452
9466
  setField(classic, effects, "verifyResult", "pending");
9467
+ setField(classic, effects, "branchStatus", "pending");
9453
9468
  if (!preserveEvidence) {
9454
9469
  setField(classic, effects, "verificationReport", null);
9455
- setField(classic, effects, "branchStatus", "pending");
9456
9470
  }
9457
9471
  } else if (event === "verify-pass") {
9458
9472
  setField(classic, effects, "verifyResult", "pass");
9473
+ setField(classic, effects, "verifyFailures", 0);
9459
9474
  setField(classic, effects, "phase", "archive");
9460
9475
  setField(classic, effects, "verifiedAt", dateOnly(now));
9461
9476
  setField(classic, effects, "archiveConfirmation", "pending");
9477
+ setField(classic, effects, "branchStatus", "pending");
9462
9478
  } else if (event === "verify-fail") {
9463
9479
  setField(classic, effects, "verifyResult", "fail");
9480
+ setField(classic, effects, "verifyFailures", classic.verifyFailures + 1);
9464
9481
  setField(classic, effects, "phase", "build");
9482
+ setField(classic, effects, "branchStatus", "pending");
9465
9483
  } else if (event === "preset-escalate") {
9466
9484
  if (classic.workflow !== "hotfix" && classic.workflow !== "tweak") {
9467
9485
  throw new Error(
@@ -9472,6 +9490,15 @@ function applyClassicTransition(current, event, options = {}) {
9472
9490
  setField(classic, effects, "classicProfile", "full");
9473
9491
  setField(classic, effects, "phase", "design");
9474
9492
  setField(classic, effects, "designDoc", null);
9493
+ setField(classic, effects, "buildPause", null);
9494
+ setField(classic, effects, "buildMode", null);
9495
+ setField(classic, effects, "subagentDispatch", null);
9496
+ setField(classic, effects, "tddMode", null);
9497
+ setField(classic, effects, "reviewMode", null);
9498
+ setField(classic, effects, "isolation", null);
9499
+ setField(classic, effects, "boundBranch", null);
9500
+ setField(classic, effects, "verifyMode", null);
9501
+ setField(classic, effects, "directOverride", null);
9475
9502
  } else if (event === "archive-confirm") {
9476
9503
  if (classic.verifyResult !== "pass") {
9477
9504
  throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
@@ -9481,9 +9508,11 @@ function applyClassicTransition(current, event, options = {}) {
9481
9508
  } else if (event === "archive-reopen") {
9482
9509
  if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
9483
9510
  setField(classic, effects, "verifyResult", "pending");
9511
+ setField(classic, effects, "verifyFailures", 0);
9484
9512
  setField(classic, effects, "phase", "verify");
9485
9513
  setField(classic, effects, "verifiedAt", null);
9486
9514
  setField(classic, effects, "archiveConfirmation", null);
9515
+ setField(classic, effects, "branchStatus", "pending");
9487
9516
  } else {
9488
9517
  if (classic.verifyResult !== "pass") {
9489
9518
  throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
@@ -9882,11 +9911,11 @@ var classicArchiveCommand = async (args) => {
9882
9911
  };
9883
9912
 
9884
9913
  // domains/comet-classic/classic-guard.ts
9885
- var import_yaml5 = __toESM(require_dist(), 1);
9914
+ var import_yaml6 = __toESM(require_dist(), 1);
9886
9915
  import { spawnSync as spawnSync2 } from "child_process";
9887
9916
  import { createHash as createHash4 } from "crypto";
9888
- import { existsSync, promises as fs15, readFileSync } from "fs";
9889
- import path16 from "path";
9917
+ import { existsSync, promises as fs16, readFileSync } from "fs";
9918
+ import path17 from "path";
9890
9919
 
9891
9920
  // domains/comet-classic/classic-command-checks.ts
9892
9921
  import path13 from "path";
@@ -10095,7 +10124,7 @@ var ENUMS = {
10095
10124
  subagent_dispatch: ["confirmed"],
10096
10125
  tdd_mode: ["tdd", "direct"],
10097
10126
  review_mode: ["off", "standard", "thorough"],
10098
- isolation: ["branch", "worktree"],
10127
+ isolation: ["current", "branch", "worktree"],
10099
10128
  verify_mode: ["light", "full"],
10100
10129
  auto_transition: ["true", "false"],
10101
10130
  verify_result: ["pending", "pass", "fail"],
@@ -10188,6 +10217,18 @@ var classicValidateCommand = async (args) => {
10188
10217
  fail3(`${field2}='${value}' is not valid. Expected: ${values.join(" ")}`);
10189
10218
  }
10190
10219
  }
10220
+ if (Object.prototype.hasOwnProperty.call(record, "bound_branch")) {
10221
+ const value = record.bound_branch;
10222
+ if (value !== null && typeof value !== "string") {
10223
+ fail3(`bound_branch='${text(value)}' is not a string or null`);
10224
+ }
10225
+ }
10226
+ if (Object.prototype.hasOwnProperty.call(record, "verify_failures")) {
10227
+ const value = record.verify_failures;
10228
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
10229
+ fail3(`verify_failures='${text(value)}' is not a non-negative integer`);
10230
+ }
10231
+ }
10191
10232
  for (const field2 of ["design_doc", "plan", "handoff_context"]) {
10192
10233
  const value = text(record[field2]);
10193
10234
  if (value && !await exists3(path14.resolve(value))) {
@@ -10224,8 +10265,9 @@ async function fileExists3(filePath) {
10224
10265
  try {
10225
10266
  await fs13.access(filePath);
10226
10267
  return true;
10227
- } catch {
10228
- return false;
10268
+ } catch (error) {
10269
+ if (isNotFoundError(error)) return false;
10270
+ throw error;
10229
10271
  }
10230
10272
  }
10231
10273
  async function readDir(dirPath) {
@@ -10239,6 +10281,9 @@ async function readDir(dirPath) {
10239
10281
  throw error;
10240
10282
  }
10241
10283
  }
10284
+ function isNotFoundError(error) {
10285
+ return error?.code === "ENOENT";
10286
+ }
10242
10287
 
10243
10288
  // domains/comet-classic/classic-project-config.ts
10244
10289
  function configCandidates(options = {}) {
@@ -10268,6 +10313,96 @@ async function readClassicConfigValue(field2, options = {}) {
10268
10313
  return null;
10269
10314
  }
10270
10315
 
10316
+ // domains/comet-classic/classic-branch-binding.ts
10317
+ var import_yaml5 = __toESM(require_dist(), 1);
10318
+ import { execFileSync } from "child_process";
10319
+ import { randomUUID as randomUUID6 } from "crypto";
10320
+ import { promises as fs15 } from "fs";
10321
+ import path16 from "path";
10322
+ function liveGitBranch(cwd) {
10323
+ try {
10324
+ const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
10325
+ cwd,
10326
+ encoding: "utf8",
10327
+ stdio: ["ignore", "pipe", "ignore"]
10328
+ }).trim();
10329
+ return branch && branch !== "HEAD" ? branch : null;
10330
+ } catch {
10331
+ return null;
10332
+ }
10333
+ }
10334
+ function isGitWorkTree(cwd) {
10335
+ try {
10336
+ return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
10337
+ cwd,
10338
+ encoding: "utf8",
10339
+ stdio: ["ignore", "pipe", "ignore"]
10340
+ }).trim() === "true";
10341
+ } catch {
10342
+ return false;
10343
+ }
10344
+ }
10345
+ var BOUND_BRANCH_ISOLATIONS = ["current", "branch", "worktree"];
10346
+ function requiresBranchBinding(isolation) {
10347
+ return BOUND_BRANCH_ISOLATIONS.includes(isolation);
10348
+ }
10349
+ function evaluateBranchBinding(input) {
10350
+ if (!requiresBranchBinding(input.isolation)) return { status: "not-applicable" };
10351
+ if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) {
10352
+ return { status: "not-applicable" };
10353
+ }
10354
+ if (input.boundBranch === null) {
10355
+ return input.currentBranch === null ? { status: "unbound-detached" } : { status: "needs-heal", branch: input.currentBranch };
10356
+ }
10357
+ if (input.currentBranch === input.boundBranch) return { status: "ok" };
10358
+ return { status: "drift", boundBranch: input.boundBranch, currentBranch: input.currentBranch };
10359
+ }
10360
+ async function resolveBranchBinding(changeDir, options) {
10361
+ const file = path16.join(changeDir, ".comet.yaml");
10362
+ const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
10363
+ if (document.errors.length > 0) {
10364
+ throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`);
10365
+ }
10366
+ const record = document.toJS() ?? {};
10367
+ const isolation = typeof record.isolation === "string" ? record.isolation : null;
10368
+ const boundBranch = typeof record.bound_branch === "string" && record.bound_branch !== "" ? record.bound_branch : null;
10369
+ const bindingRequired = requiresBranchBinding(isolation);
10370
+ const currentBranch = liveGitBranch(options.cwd);
10371
+ const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null ? isGitWorkTree(options.cwd) : true;
10372
+ const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree });
10373
+ if (verdict.status === "needs-heal" && options.heal) {
10374
+ await healBoundBranch(changeDir, verdict.branch);
10375
+ return { status: "healed", branch: verdict.branch, bindingRequired, currentBranch };
10376
+ }
10377
+ return { ...verdict, bindingRequired, currentBranch };
10378
+ }
10379
+ async function healBoundBranch(changeDir, branch) {
10380
+ const file = path16.join(changeDir, ".comet.yaml");
10381
+ const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
10382
+ document.set("bound_branch", branch);
10383
+ const temporary = `${file}.${randomUUID6()}.tmp`;
10384
+ try {
10385
+ await fs15.writeFile(temporary, document.toString(), "utf8");
10386
+ await fs15.rename(temporary, file);
10387
+ } catch (error) {
10388
+ await fs15.rm(temporary, { force: true });
10389
+ throw error;
10390
+ }
10391
+ }
10392
+ function branchLabel(currentBranch) {
10393
+ return currentBranch ?? "detached HEAD";
10394
+ }
10395
+ function driftBlockedMessage(change, boundBranch, currentBranch) {
10396
+ return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'.
10397
+ Next: ask the user to confirm — switch back to '${boundBranch}', or run \`comet state rebind ${change}\` after explicit confirmation.`;
10398
+ }
10399
+ function driftStaleReason(change, boundBranch, currentBranch) {
10400
+ return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`;
10401
+ }
10402
+ function unboundDetachedMessage(change) {
10403
+ return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`;
10404
+ }
10405
+
10271
10406
  // domains/comet-classic/classic-guard.ts
10272
10407
  var GREEN3 = "\x1B[32m";
10273
10408
  var RED3 = "\x1B[31m";
@@ -10332,7 +10467,7 @@ var GuardOutput = class {
10332
10467
  };
10333
10468
  async function exists4(file) {
10334
10469
  try {
10335
- await fs15.access(file);
10470
+ await fs16.access(file);
10336
10471
  return true;
10337
10472
  } catch (error) {
10338
10473
  if (error.code === "ENOENT") return false;
@@ -10341,7 +10476,7 @@ async function exists4(file) {
10341
10476
  }
10342
10477
  async function nonempty(file) {
10343
10478
  try {
10344
- return (await fs15.stat(file)).size > 0;
10479
+ return (await fs16.stat(file)).size > 0;
10345
10480
  } catch (error) {
10346
10481
  if (error.code === "ENOENT") return false;
10347
10482
  throw error;
@@ -10355,8 +10490,8 @@ async function resolveChangeDir(name) {
10355
10490
  return (await resolveClassicChangeDirectory(name)).label;
10356
10491
  }
10357
10492
  async function readField(changeDir, field2) {
10358
- const file = path16.join(changeDir, ".comet.yaml");
10359
- const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
10493
+ const file = path17.join(changeDir, ".comet.yaml");
10494
+ const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
10360
10495
  if (document.errors.length > 0) {
10361
10496
  throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
10362
10497
  }
@@ -10397,7 +10532,7 @@ function countEnglishWords(source) {
10397
10532
  }
10398
10533
  async function documentLanguageMatchesConfigured(changeDir, file) {
10399
10534
  const language = await configuredLanguage(changeDir);
10400
- const source = stripFencedCodeBlocks(await fs15.readFile(file, "utf8"));
10535
+ const source = stripFencedCodeBlocks(await fs16.readFile(file, "utf8"));
10401
10536
  const cjk = countCjkChars(source);
10402
10537
  const englishWords = countEnglishWords(source);
10403
10538
  if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
@@ -10421,7 +10556,7 @@ async function handoffSourceFiles(changeDir) {
10421
10556
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
10422
10557
  const specs = `${changeDir}/specs`;
10423
10558
  if (await exists4(specs)) {
10424
- for (const entry2 of (await fs15.readdir(specs)).sort()) {
10559
+ for (const entry2 of (await fs16.readdir(specs)).sort()) {
10425
10560
  const spec = `${specs}/${entry2}/spec.md`;
10426
10561
  if (await exists4(spec)) files.push(spec);
10427
10562
  }
@@ -10441,7 +10576,7 @@ async function preflight(changeDir, name) {
10441
10576
  if (!await exists4(changeDir)) {
10442
10577
  throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
10443
10578
  }
10444
- if (!await exists4(path16.join(changeDir, ".comet.yaml"))) {
10579
+ if (!await exists4(path17.join(changeDir, ".comet.yaml"))) {
10445
10580
  throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
10446
10581
  }
10447
10582
  const result5 = await classicValidateCommand([name], { json: false });
@@ -10511,9 +10646,9 @@ var INFERRED_COMMAND_SOURCES = [
10511
10646
  "Cargo.toml"
10512
10647
  ];
10513
10648
  async function removedProjectCommandField(field2) {
10514
- const config = path16.join(".comet", "config.yaml");
10649
+ const config = path17.join(".comet", "config.yaml");
10515
10650
  if (!await exists4(config)) return false;
10516
- const document = (0, import_yaml5.parseDocument)(await fs15.readFile(config, "utf8"));
10651
+ const document = (0, import_yaml6.parseDocument)(await fs16.readFile(config, "utf8"));
10517
10652
  if (document.errors.length > 0) {
10518
10653
  throw new Error(
10519
10654
  `.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
@@ -10594,14 +10729,14 @@ ${recoveryCommand(change, scope, recorded.command)}`
10594
10729
  return { status: 0, output: evidenceDetail(recorded) };
10595
10730
  }
10596
10731
  async function tasksAllDone(changeDir) {
10597
- const tasks = path16.join(changeDir, "tasks.md");
10732
+ const tasks = path17.join(changeDir, "tasks.md");
10598
10733
  if (!await exists4(tasks)) {
10599
10734
  return fail(
10600
10735
  `tasks.md is missing at ${tasks}
10601
10736
  Next: restore or create tasks.md for this change before leaving build.`
10602
10737
  );
10603
10738
  }
10604
- const source = await fs15.readFile(tasks, "utf8");
10739
+ const source = await fs16.readFile(tasks, "utf8");
10605
10740
  if (!/- \[x\]/u.test(source)) {
10606
10741
  return fail(
10607
10742
  "tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
@@ -10618,9 +10753,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
10618
10753
  return pass();
10619
10754
  }
10620
10755
  async function tasksHasAny(changeDir) {
10621
- const tasks = path16.join(changeDir, "tasks.md");
10756
+ const tasks = path17.join(changeDir, "tasks.md");
10622
10757
  if (!await exists4(tasks)) return false;
10623
- return /- \[/u.test(await fs15.readFile(tasks, "utf8"));
10758
+ return /- \[/u.test(await fs16.readFile(tasks, "utf8"));
10624
10759
  }
10625
10760
  async function planTasksAllDone(changeDir) {
10626
10761
  const plan = await readField(changeDir, "plan");
@@ -10631,7 +10766,7 @@ async function planTasksAllDone(changeDir) {
10631
10766
  Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
10632
10767
  );
10633
10768
  }
10634
- const source = await fs15.readFile(plan, "utf8");
10769
+ const source = await fs16.readFile(plan, "utf8");
10635
10770
  const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
10636
10771
  if (unfinished.length > 0) {
10637
10772
  return fail(
@@ -10642,13 +10777,38 @@ Next: check off corresponding completed plan tasks, then commit the plan update.
10642
10777
  }
10643
10778
  return pass();
10644
10779
  }
10780
+ async function boundBranchMatches(changeDir, change) {
10781
+ let outcome;
10782
+ try {
10783
+ outcome = await resolveBranchBinding(changeDir, { heal: true, cwd: process.cwd() });
10784
+ } catch (error) {
10785
+ throw new GuardFailure(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
10786
+ }
10787
+ switch (outcome.status) {
10788
+ case "drift":
10789
+ return fail(driftBlockedMessage(change, outcome.boundBranch, outcome.currentBranch));
10790
+ case "unbound-detached":
10791
+ return fail(unboundDetachedMessage(change));
10792
+ case "healed":
10793
+ return pass(`bound_branch lazily set to ${outcome.branch}`);
10794
+ case "needs-heal":
10795
+ case "ok":
10796
+ case "not-applicable":
10797
+ return pass();
10798
+ default: {
10799
+ const exhaustive = outcome;
10800
+ throw new Error(`unhandled branch binding status: ${JSON.stringify(exhaustive)}`);
10801
+ }
10802
+ }
10803
+ }
10645
10804
  async function isolationSelected(changeDir, change) {
10646
10805
  const isolation = await readField(changeDir, "isolation");
10647
- if (isolation === "branch" || isolation === "worktree") return pass();
10806
+ if (isolation === "current" || isolation === "branch" || isolation === "worktree") return pass();
10807
+ const allowedValues = "<current|branch|worktree>";
10648
10808
  return fail(
10649
- `isolation must be branch or worktree, got '${isolation || "null"}'
10650
- Next: ask the user to choose branch or worktree, create the chosen isolation, then run:
10651
- node "$COMET_STATE" set ${change} isolation <branch|worktree>`
10809
+ `isolation must be current, branch, or worktree, got '${isolation || "null"}'
10810
+ Next: choose a valid workspace mode, prepare it when needed, then run:
10811
+ comet state set ${change} isolation ${allowedValues}`
10652
10812
  );
10653
10813
  }
10654
10814
  async function buildModeSelected(changeDir, change) {
@@ -10658,7 +10818,7 @@ async function buildModeSelected(changeDir, change) {
10658
10818
  return fail(
10659
10819
  `build_mode must be selected before leaving build, got '${buildMode || "null"}'
10660
10820
  Next: ask the user to choose an execution mode, then run:
10661
- node "$COMET_STATE" set ${change} build_mode <subagent-driven-development|executing-plans>`
10821
+ comet state set ${change} build_mode <subagent-driven-development|executing-plans>`
10662
10822
  );
10663
10823
  }
10664
10824
  async function buildModeAllowedForWorkflow(changeDir) {
@@ -10680,9 +10840,9 @@ async function subagentDispatchConfirmed(changeDir, change) {
10680
10840
  return fail(
10681
10841
  `subagent_dispatch must be confirmed before using build_mode=subagent-driven-development
10682
10842
  Next: confirm the current platform has a real background subagent/Task/multi-agent dispatcher, then run:
10683
- node "$COMET_STATE" set ${change} subagent_dispatch confirmed
10684
- Or ask the user to switch to executing-plans and run:
10685
- node "$COMET_STATE" set ${change} build_mode executing-plans`
10843
+ comet state set ${change} subagent_dispatch confirmed
10844
+ If dispatch is unavailable, return to /comet-build Step 2 with subagent-driven-development removed. When executing-plans is the only valid mode, run:
10845
+ comet state set ${change} build_mode executing-plans`
10686
10846
  );
10687
10847
  }
10688
10848
  async function tddModeSelected(changeDir, change) {
@@ -10693,7 +10853,7 @@ async function tddModeSelected(changeDir, change) {
10693
10853
  return fail(
10694
10854
  `tdd_mode must be tdd or direct for full workflow, got '${tddMode || "null"}'
10695
10855
  Next: ask the user to choose TDD enforcement level, then run:
10696
- node "$COMET_STATE" set ${change} tdd_mode <tdd|direct>`
10856
+ comet state set ${change} tdd_mode <tdd|direct>`
10697
10857
  );
10698
10858
  }
10699
10859
  async function reviewModeSelected(changeDir, change) {
@@ -10706,7 +10866,7 @@ async function reviewModeSelected(changeDir, change) {
10706
10866
  return fail(
10707
10867
  `review_mode must be off, standard, or thorough before leaving build, got '${reviewMode || "null"}'
10708
10868
  Next: ask the user to choose review strength, then run:
10709
- node "$COMET_STATE" set ${change} review_mode <off|standard|thorough>`
10869
+ comet state set ${change} review_mode <off|standard|thorough>`
10710
10870
  );
10711
10871
  }
10712
10872
  async function verificationReportExists(changeDir) {
@@ -10720,7 +10880,7 @@ async function archivedIsTrue(changeDir) {
10720
10880
  return await readField(changeDir, "archived") === "true";
10721
10881
  }
10722
10882
  async function designDocFrontmatterHas(designDoc, field2, expected) {
10723
- const source = (await fs15.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
10883
+ const source = (await fs16.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
10724
10884
  let inFrontmatter = false;
10725
10885
  for (const line of source.split(/\r?\n/u)) {
10726
10886
  if (!inFrontmatter) {
@@ -10737,7 +10897,7 @@ async function designDocRecorded(changeDir, change) {
10737
10897
  if (designDoc && designDoc !== "null" && existsSync(designDoc)) return pass();
10738
10898
  return fail(
10739
10899
  `design_doc must point to an existing Superpowers Design Doc for full workflow before leaving design.
10740
- Next: create the Design Doc and run: node "$COMET_STATE" set ${change} design_doc <path>`
10900
+ Next: create the Design Doc and run: comet state set ${change} design_doc <path>`
10741
10901
  );
10742
10902
  }
10743
10903
  async function designHandoffContextValid(changeDir, change) {
@@ -10752,13 +10912,13 @@ Next: run node "$COMET_HANDOFF" ${change} design --write before invoking Superpo
10752
10912
  if (!await nonempty(context)) {
10753
10913
  return fail(
10754
10914
  `handoff_context does not point to a non-empty file: ${context}
10755
- Next: regenerate the design handoff with comet-handoff.mjs.`
10915
+ Next: regenerate the design handoff with comet handoff ${change} design --write.`
10756
10916
  );
10757
10917
  }
10758
10918
  if (!/^[a-f0-9]{64}$/u.test(recordedHash)) {
10759
10919
  return fail(
10760
10920
  `handoff_hash is missing or invalid: ${recordedHash || "null"}
10761
- Next: regenerate the design handoff with comet-handoff.mjs.`
10921
+ Next: regenerate the design handoff with comet handoff ${change} design --write.`
10762
10922
  );
10763
10923
  }
10764
10924
  const actualHash = await computeHandoffHash(changeDir);
@@ -10767,14 +10927,14 @@ Next: regenerate the design handoff with comet-handoff.mjs.`
10767
10927
  `OpenSpec artifacts changed after handoff was generated.
10768
10928
  Expected handoff_hash: ${recordedHash}
10769
10929
  Actual handoff_hash: ${actualHash}
10770
- Next: rerun comet-handoff.mjs so Superpowers receives the current OpenSpec context.`
10930
+ Next: run comet handoff ${change} design --write so Superpowers receives the current OpenSpec context.`
10771
10931
  );
10772
10932
  }
10773
10933
  const markdown = `${context.replace(/\.json$/u, "")}.md`;
10774
10934
  if (!await nonempty(markdown)) {
10775
10935
  return fail(
10776
10936
  `design handoff markdown is missing or empty: ${markdown}
10777
- Next: regenerate the design handoff with comet-handoff.mjs.`
10937
+ Next: regenerate the design handoff with comet handoff ${change} design --write.`
10778
10938
  );
10779
10939
  }
10780
10940
  return pass();
@@ -10785,7 +10945,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
10785
10945
  const markdown = `${context.replace(/\.json$/u, "")}.md`;
10786
10946
  if (!await nonempty(markdown))
10787
10947
  return fail(`design handoff markdown is missing or empty: ${markdown}`);
10788
- const source = await fs15.readFile(markdown, "utf8");
10948
+ const source = await fs16.readFile(markdown, "utf8");
10789
10949
  const problems = [];
10790
10950
  if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
10791
10951
  problems.push("handoff markdown is missing Generated-by marker");
@@ -10812,7 +10972,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
10812
10972
  const context = await readField(changeDir, "handoff_context");
10813
10973
  if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
10814
10974
  if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
10815
- const source = await fs15.readFile(context, "utf8");
10975
+ const source = await fs16.readFile(context, "utf8");
10816
10976
  const problems = [];
10817
10977
  let parsed;
10818
10978
  try {
@@ -10849,19 +11009,19 @@ async function guardOpenChecks(output, changeDir) {
10849
11009
  const checks = [
10850
11010
  check(
10851
11011
  "proposal.md exists and non-empty",
10852
- async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
11012
+ async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
10853
11013
  ),
10854
11014
  check(
10855
11015
  "proposal.md matches configured language",
10856
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
11016
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
10857
11017
  ),
10858
11018
  check(
10859
11019
  "tasks.md exists and non-empty",
10860
- async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
11020
+ async () => await nonempty(path17.join(changeDir, "tasks.md")) ? pass() : fail("")
10861
11021
  ),
10862
11022
  check(
10863
11023
  "tasks.md matches configured language",
10864
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
11024
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "tasks.md"))
10865
11025
  ),
10866
11026
  check(
10867
11027
  "tasks.md has at least one task",
@@ -10874,11 +11034,11 @@ async function guardOpenChecks(output, changeDir) {
10874
11034
  0,
10875
11035
  check(
10876
11036
  "design.md exists and non-empty",
10877
- async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
11037
+ async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
10878
11038
  ),
10879
11039
  check(
10880
11040
  "design.md matches configured language",
10881
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
11041
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "design.md"))
10882
11042
  )
10883
11043
  );
10884
11044
  }
@@ -10890,27 +11050,27 @@ async function guardDesignChecks(output, changeDir, change) {
10890
11050
  const builders = [
10891
11051
  check(
10892
11052
  "proposal.md exists",
10893
- async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
11053
+ async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
10894
11054
  ),
10895
11055
  check(
10896
11056
  "proposal.md matches configured language",
10897
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
11057
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
10898
11058
  ),
10899
11059
  check(
10900
11060
  "design.md exists",
10901
- async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
11061
+ async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
10902
11062
  ),
10903
11063
  check(
10904
11064
  "design.md matches configured language",
10905
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
11065
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "design.md"))
10906
11066
  ),
10907
11067
  check(
10908
11068
  "tasks.md exists",
10909
- async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
11069
+ async () => await nonempty(path17.join(changeDir, "tasks.md")) ? pass() : fail("")
10910
11070
  ),
10911
11071
  check(
10912
11072
  "tasks.md matches configured language",
10913
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
11073
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "tasks.md"))
10914
11074
  ),
10915
11075
  check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
10916
11076
  check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
@@ -10961,6 +11121,7 @@ async function guardDesignChecks(output, changeDir, change) {
10961
11121
  }
10962
11122
  async function guardBuildChecks(output, changeDir, change, run) {
10963
11123
  return runChecks(output, [
11124
+ check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
10964
11125
  check("isolation selected", () => isolationSelected(changeDir, change)),
10965
11126
  check("build_mode selected", () => buildModeSelected(changeDir, change)),
10966
11127
  check("build_mode allowed for workflow", () => buildModeAllowedForWorkflow(changeDir)),
@@ -10971,11 +11132,11 @@ async function guardBuildChecks(output, changeDir, change, run) {
10971
11132
  check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
10972
11133
  check(
10973
11134
  "proposal.md exists",
10974
- async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
11135
+ async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
10975
11136
  ),
10976
11137
  check(
10977
11138
  "proposal.md matches configured language",
10978
- () => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
11139
+ () => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
10979
11140
  ),
10980
11141
  check("Superpowers plan matches configured language", async () => {
10981
11142
  const plan = await readField(changeDir, "plan");
@@ -10992,6 +11153,7 @@ async function guardBuildChecks(output, changeDir, change, run) {
10992
11153
  }
10993
11154
  async function guardVerifyChecks(output, changeDir, change, run) {
10994
11155
  return runChecks(output, [
11156
+ check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
10995
11157
  check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
10996
11158
  // Verification command runs after tasks check — no point running tests
10997
11159
  // if tasks.md is incomplete.
@@ -11007,30 +11169,32 @@ async function guardVerifyChecks(output, changeDir, change, run) {
11007
11169
  const report = await readField(changeDir, "verification_report");
11008
11170
  if (!report || report === "null" || !await exists4(report)) return pass();
11009
11171
  return documentLanguageMatchesConfigured(changeDir, report);
11010
- }),
11011
- check(
11012
- "branch_status=handled",
11013
- async () => await branchStatusHandled(changeDir) ? pass() : fail("")
11014
- )
11172
+ })
11015
11173
  ]);
11016
11174
  }
11017
- async function guardArchiveChecks(output, changeDir) {
11175
+ async function guardArchiveChecks(output, changeDir, change) {
11018
11176
  return runChecks(output, [
11177
+ check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
11019
11178
  check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
11020
11179
  check(
11021
11180
  "proposal.md exists",
11022
- async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
11181
+ async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
11023
11182
  ),
11024
11183
  check(
11025
11184
  "design.md exists",
11026
- async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
11185
+ async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
11027
11186
  ),
11028
- check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
11187
+ check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
11188
+ check(
11189
+ "branch_status=handled",
11190
+ async () => await branchStatusHandled(changeDir) ? pass() : fail("")
11191
+ )
11029
11192
  ]);
11030
11193
  }
11031
- async function applyStateUpdate(output, change, changeDir, phase, context) {
11194
+ async function applyStateUpdate(output, change, changeDir, phase) {
11032
11195
  const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
11033
11196
  if (!event) return;
11197
+ const context = await ensureClassicRuntimeRun(changeDir);
11034
11198
  const result5 = applyClassicTransition(context.classic, event);
11035
11199
  await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
11036
11200
  event,
@@ -11084,7 +11248,7 @@ Valid phases: open, design, build, verify, archive`
11084
11248
  blocked2 = await guardBuildChecks(output, changeDir, change, runContext.run);
11085
11249
  else if (phase === "verify")
11086
11250
  blocked2 = await guardVerifyChecks(output, changeDir, change, runContext.run);
11087
- else blocked2 = await guardArchiveChecks(output, changeDir);
11251
+ else blocked2 = await guardArchiveChecks(output, changeDir, change);
11088
11252
  if (blocked2) {
11089
11253
  output.stderr.push("");
11090
11254
  output.stderr.push(red2("BLOCKED — fix failing checks before proceeding to next phase"));
@@ -11093,7 +11257,7 @@ Valid phases: open, design, build, verify, archive`
11093
11257
  output.stderr.push("");
11094
11258
  output.stderr.push(green2("ALL CHECKS PASSED — ready for next phase"));
11095
11259
  if (flag === "--apply") {
11096
- await applyStateUpdate(output, change, changeDir, phase, runContext);
11260
+ await applyStateUpdate(output, change, changeDir, phase);
11097
11261
  }
11098
11262
  return output.toResult(0);
11099
11263
  } catch (error) {
@@ -11106,10 +11270,10 @@ Valid phases: open, design, build, verify, archive`
11106
11270
  };
11107
11271
 
11108
11272
  // domains/comet-classic/classic-handoff.ts
11109
- var import_yaml6 = __toESM(require_dist(), 1);
11273
+ var import_yaml7 = __toESM(require_dist(), 1);
11110
11274
  import { createHash as createHash5 } from "crypto";
11111
- import { promises as fs16, readFileSync as readFileSync2 } from "fs";
11112
- import path17 from "path";
11275
+ import { promises as fs17, readFileSync as readFileSync2 } from "fs";
11276
+ import path18 from "path";
11113
11277
  var GREEN4 = "\x1B[32m";
11114
11278
  var RED4 = "\x1B[31m";
11115
11279
  var YELLOW4 = "\x1B[33m";
@@ -11143,7 +11307,7 @@ var HandoffOutput = class {
11143
11307
  };
11144
11308
  async function exists5(file) {
11145
11309
  try {
11146
- await fs16.access(file);
11310
+ await fs17.access(file);
11147
11311
  return true;
11148
11312
  } catch (error) {
11149
11313
  if (error.code === "ENOENT") return false;
@@ -11152,7 +11316,7 @@ async function exists5(file) {
11152
11316
  }
11153
11317
  async function nonempty2(file) {
11154
11318
  try {
11155
- return (await fs16.stat(file)).size > 0;
11319
+ return (await fs17.stat(file)).size > 0;
11156
11320
  } catch (error) {
11157
11321
  if (error.code === "ENOENT") return false;
11158
11322
  throw error;
@@ -11181,7 +11345,7 @@ async function handoffSourceFiles2(changeDir) {
11181
11345
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
11182
11346
  const specs = `${changeDir}/specs`;
11183
11347
  if (await exists5(specs)) {
11184
- for (const entry2 of (await fs16.readdir(specs)).sort()) {
11348
+ for (const entry2 of (await fs17.readdir(specs)).sort()) {
11185
11349
  const spec = `${specs}/${entry2}/spec.md`;
11186
11350
  if (await exists5(spec)) files.push(spec);
11187
11351
  }
@@ -11229,7 +11393,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11229
11393
  ];
11230
11394
  for (const file of await handoffSourceFiles2(changeDir)) {
11231
11395
  if (!await exists5(file)) continue;
11232
- const content = await fs16.readFile(file, "utf8");
11396
+ const content = await fs17.readFile(file, "utf8");
11233
11397
  const total = lineCount(content);
11234
11398
  lines.push(
11235
11399
  `## ${file}`,
@@ -11254,7 +11418,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11254
11418
  }
11255
11419
  lines.push("");
11256
11420
  }
11257
- await fs16.writeFile(output, lines.join("\n"));
11421
+ await fs17.writeFile(output, lines.join("\n"));
11258
11422
  }
11259
11423
  async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11260
11424
  const entries = [];
@@ -11277,7 +11441,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11277
11441
  "}",
11278
11442
  ""
11279
11443
  ].join("\n");
11280
- await fs16.writeFile(output, document);
11444
+ await fs17.writeFile(output, document);
11281
11445
  }
11282
11446
  async function writeSpecProjectionForFile(file, content) {
11283
11447
  return [
@@ -11317,11 +11481,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11317
11481
  const specs = `${changeDir}/specs`;
11318
11482
  let projected = false;
11319
11483
  if (await exists5(specs)) {
11320
- for (const entry2 of (await fs16.readdir(specs)).sort()) {
11484
+ for (const entry2 of (await fs17.readdir(specs)).sort()) {
11321
11485
  const spec = `${specs}/${entry2}/spec.md`;
11322
11486
  if (!await exists5(spec)) continue;
11323
11487
  projected = true;
11324
- lines.push(...await writeSpecProjectionForFile(spec, await fs16.readFile(spec, "utf8")));
11488
+ lines.push(...await writeSpecProjectionForFile(spec, await fs17.readFile(spec, "utf8")));
11325
11489
  }
11326
11490
  }
11327
11491
  if (!projected) {
@@ -11330,7 +11494,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11330
11494
  lines.push(
11331
11495
  "Full source files remain canonical. If a required heading or scenario is missing here, regenerate the handoff or read the source spec directly. Supporting files (proposal, design, tasks) are referenced by hash only."
11332
11496
  );
11333
- await fs16.writeFile(output, lines.join("\n"));
11497
+ await fs17.writeFile(output, lines.join("\n"));
11334
11498
  }
11335
11499
  async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11336
11500
  const entries = [];
@@ -11339,7 +11503,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11339
11503
  const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
11340
11504
  entries.push({ path: file, sha256: hashFile2(file), role });
11341
11505
  }
11342
- await fs16.writeFile(
11506
+ await fs17.writeFile(
11343
11507
  output,
11344
11508
  `${JSON.stringify(
11345
11509
  {
@@ -11358,8 +11522,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11358
11522
  );
11359
11523
  }
11360
11524
  async function readField2(changeDir, field2) {
11361
- const file = path17.join(changeDir, ".comet.yaml");
11362
- const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
11525
+ const file = path18.join(changeDir, ".comet.yaml");
11526
+ const document = (0, import_yaml7.parseDocument)(await fs17.readFile(file, "utf8"), { uniqueKeys: false });
11363
11527
  if (document.errors.length > 0) {
11364
11528
  throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
11365
11529
  }
@@ -11393,7 +11557,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
11393
11557
  readCheckpoint(changeDir, run.checkpointRef)
11394
11558
  ]);
11395
11559
  if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
11396
- if (context !== await fs16.readFile(contextMd, "utf8")) return false;
11560
+ if (context !== await fs17.readFile(contextMd, "utf8")) return false;
11397
11561
  if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
11398
11562
  return false;
11399
11563
  }
@@ -11516,7 +11680,7 @@ var classicHandoffCommand = async (args) => {
11516
11680
  run: pendingRun,
11517
11681
  unknownKeys: (await readClassicState(changeDir)).unknownKeys
11518
11682
  });
11519
- await fs16.mkdir(handoffDir, { recursive: true });
11683
+ await fs17.mkdir(handoffDir, { recursive: true });
11520
11684
  if (handoffMode === "beta") {
11521
11685
  await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
11522
11686
  await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
@@ -11524,7 +11688,7 @@ var classicHandoffCommand = async (args) => {
11524
11688
  await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
11525
11689
  await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
11526
11690
  }
11527
- const context = await fs16.readFile(contextMd, "utf8");
11691
+ const context = await fs17.readFile(contextMd, "utf8");
11528
11692
  await writeContext(changeDir, pendingRun.contextRef, context);
11529
11693
  const artifacts = {
11530
11694
  ...await readArtifacts(changeDir, pendingRun.artifactsRef),
@@ -11581,37 +11745,24 @@ var classicHandoffCommand = async (args) => {
11581
11745
  };
11582
11746
 
11583
11747
  // domains/comet-classic/classic-hook-guard.ts
11584
- import { existsSync as existsSync2, promises as fs18, readFileSync as readFileSync3 } from "fs";
11585
- import path19 from "path";
11748
+ import { existsSync as existsSync2, promises as fs19, readFileSync as readFileSync3 } from "fs";
11749
+ import path20 from "path";
11586
11750
 
11587
11751
  // domains/comet-classic/classic-current-change.ts
11588
- import { execFileSync } from "child_process";
11589
- import { randomUUID as randomUUID6 } from "crypto";
11590
- import { promises as fs17 } from "fs";
11591
- import path18 from "path";
11752
+ import { randomUUID as randomUUID7 } from "crypto";
11753
+ import { promises as fs18 } from "fs";
11754
+ import path19 from "path";
11592
11755
  function currentChangeFile(projectRoot2) {
11593
- return path18.join(projectRoot2, ".comet", "current-change.json");
11594
- }
11595
- function currentBranch(projectRoot2) {
11596
- try {
11597
- const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
11598
- cwd: projectRoot2,
11599
- encoding: "utf8",
11600
- stdio: ["ignore", "pipe", "ignore"]
11601
- }).trim();
11602
- return branch && branch !== "HEAD" ? branch : null;
11603
- } catch {
11604
- return null;
11605
- }
11756
+ return path19.join(projectRoot2, ".comet", "current-change.json");
11606
11757
  }
11607
11758
  function changeDirectory(projectRoot2, changeName) {
11608
- return path18.join(projectRoot2, "openspec", "changes", changeName);
11759
+ return path19.join(projectRoot2, "openspec", "changes", changeName);
11609
11760
  }
11610
11761
  async function validateActiveChange(projectRoot2, changeName) {
11611
11762
  assertOpenSpecChangeName(changeName);
11612
11763
  const changeDir = changeDirectory(projectRoot2, changeName);
11613
11764
  try {
11614
- await fs17.access(path18.join(changeDir, ".comet.yaml"));
11765
+ await fs18.access(path19.join(changeDir, ".comet.yaml"));
11615
11766
  } catch (error) {
11616
11767
  if (error.code === "ENOENT") {
11617
11768
  throw new Error(
@@ -11652,30 +11803,40 @@ function parseSelection(source) {
11652
11803
  throw new Error("current change selection change must be a string");
11653
11804
  }
11654
11805
  assertOpenSpecChangeName(record.change);
11655
- if (record.branch !== null && typeof record.branch !== "string") {
11806
+ if (record.branch !== void 0 && record.branch !== null && typeof record.branch !== "string") {
11656
11807
  throw new Error("current change selection branch must be a string or null");
11657
11808
  }
11658
11809
  return {
11659
11810
  version: 1,
11660
11811
  change: record.change,
11661
- branch: record.branch
11812
+ branch: record.branch ?? null
11662
11813
  };
11663
11814
  }
11664
11815
  async function selectCurrentChange(projectRoot2, changeName) {
11665
11816
  await validateActiveChange(projectRoot2, changeName);
11817
+ const outcome = await resolveBranchBinding(changeDirectory(projectRoot2, changeName), {
11818
+ heal: true,
11819
+ cwd: projectRoot2
11820
+ });
11821
+ if (outcome.status === "drift") {
11822
+ throw new Error(driftStaleReason(changeName, outcome.boundBranch, outcome.currentBranch));
11823
+ }
11824
+ if (outcome.status === "unbound-detached") {
11825
+ throw new Error(unboundDetachedMessage(changeName));
11826
+ }
11666
11827
  const selection = {
11667
11828
  version: 1,
11668
11829
  change: changeName,
11669
- branch: currentBranch(projectRoot2)
11830
+ branch: outcome.currentBranch
11670
11831
  };
11671
11832
  const file = currentChangeFile(projectRoot2);
11672
- const temporary = `${file}.${randomUUID6()}.tmp`;
11673
- await fs17.mkdir(path18.dirname(file), { recursive: true });
11833
+ const temporary = `${file}.${randomUUID7()}.tmp`;
11834
+ await fs18.mkdir(path19.dirname(file), { recursive: true });
11674
11835
  try {
11675
- await fs17.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
11676
- await fs17.rename(temporary, file);
11836
+ await fs18.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
11837
+ await fs18.rename(temporary, file);
11677
11838
  } catch (error) {
11678
- await fs17.rm(temporary, { force: true });
11839
+ await fs18.rm(temporary, { force: true });
11679
11840
  throw error;
11680
11841
  }
11681
11842
  return selection;
@@ -11683,7 +11844,7 @@ async function selectCurrentChange(projectRoot2, changeName) {
11683
11844
  async function resolveCurrentChange(projectRoot2) {
11684
11845
  let source;
11685
11846
  try {
11686
- source = await fs17.readFile(currentChangeFile(projectRoot2), "utf8");
11847
+ source = await fs18.readFile(currentChangeFile(projectRoot2), "utf8");
11687
11848
  } catch (error) {
11688
11849
  if (error.code === "ENOENT") return { status: "missing" };
11689
11850
  return {
@@ -11701,17 +11862,32 @@ async function resolveCurrentChange(projectRoot2) {
11701
11862
  reason: error instanceof Error ? error.message : String(error)
11702
11863
  };
11703
11864
  }
11704
- const branch = currentBranch(projectRoot2);
11705
- if (selection.branch !== null && branch !== selection.branch) {
11865
+ const outcome = await resolveBranchBinding(changeDirectory(projectRoot2, selection.change), {
11866
+ heal: false,
11867
+ cwd: projectRoot2
11868
+ });
11869
+ if (outcome.status === "drift") {
11870
+ return {
11871
+ status: "stale",
11872
+ reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch)
11873
+ };
11874
+ }
11875
+ if (outcome.status === "unbound-detached") {
11876
+ return { status: "stale", reason: unboundDetachedMessage(selection.change) };
11877
+ }
11878
+ if (outcome.status === "ok") {
11879
+ return { status: "selected", selection };
11880
+ }
11881
+ if (selection.branch !== null && outcome.currentBranch !== selection.branch) {
11706
11882
  return {
11707
11883
  status: "stale",
11708
- reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${branch ?? "detached HEAD"}'`
11884
+ reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? "detached HEAD"}'`
11709
11885
  };
11710
11886
  }
11711
11887
  return { status: "selected", selection };
11712
11888
  }
11713
11889
  async function clearCurrentChange(projectRoot2) {
11714
- await fs17.rm(currentChangeFile(projectRoot2), { force: true });
11890
+ await fs18.rm(currentChangeFile(projectRoot2), { force: true });
11715
11891
  }
11716
11892
 
11717
11893
  // domains/comet-classic/classic-hook-guard.ts
@@ -11736,66 +11912,75 @@ function inputTarget() {
11736
11912
  function normalized(value) {
11737
11913
  return value.replaceAll("\\", "/").replace(/\/+/gu, "/");
11738
11914
  }
11915
+ function comparisonKey(value) {
11916
+ const normalizedValue = normalized(value);
11917
+ return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue;
11918
+ }
11739
11919
  function parseProjectRoot(args) {
11740
11920
  const index = args.indexOf("--project-root");
11741
11921
  const value = index >= 0 ? args[index + 1] : void 0;
11742
- return path19.resolve(value && !value.startsWith("--") ? value : process.cwd());
11922
+ return path20.resolve(value && !value.startsWith("--") ? value : process.cwd());
11743
11923
  }
11744
11924
  function relativeToProjectRoot(target, projectRoot2) {
11745
- const relative = normalized(path19.relative(projectRoot2, target));
11925
+ const relative = normalized(path20.relative(projectRoot2, target));
11746
11926
  if (relative === "") return "";
11747
- if (relative.startsWith("../") || relative === ".." || path19.isAbsolute(relative)) return null;
11927
+ if (relative.startsWith("../") || relative === ".." || path20.isAbsolute(relative)) return null;
11748
11928
  return relative;
11749
11929
  }
11750
11930
  async function physicalPathForPossiblyMissingTarget(target) {
11751
- const resolved = path19.resolve(target);
11752
- const root = path19.parse(resolved).root;
11931
+ const resolved = path20.resolve(target);
11932
+ const root = path20.parse(resolved).root;
11753
11933
  const missingSegments = [];
11754
11934
  let cursor = resolved;
11755
11935
  while (cursor && cursor !== root) {
11756
11936
  try {
11757
- const physicalBase = await fs18.realpath(cursor);
11758
- return path19.join(physicalBase, ...missingSegments.reverse());
11937
+ const physicalBase = await fs19.realpath(cursor);
11938
+ return path20.join(physicalBase, ...missingSegments.reverse());
11759
11939
  } catch (error) {
11760
11940
  const code = error.code;
11761
11941
  if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
11762
- missingSegments.push(path19.basename(cursor));
11763
- cursor = path19.dirname(cursor);
11942
+ missingSegments.push(path20.basename(cursor));
11943
+ cursor = path20.dirname(cursor);
11764
11944
  }
11765
11945
  }
11766
11946
  try {
11767
- const physicalRoot = await fs18.realpath(root);
11768
- return path19.join(physicalRoot, ...missingSegments.reverse());
11947
+ const physicalRoot = await fs19.realpath(root);
11948
+ return path20.join(physicalRoot, ...missingSegments.reverse());
11769
11949
  } catch {
11770
11950
  return null;
11771
11951
  }
11772
11952
  }
11773
11953
  async function projectRelative(target, projectRoot2) {
11774
- const rawCandidate = path19.isAbsolute(target) ? target : path19.resolve(process.cwd(), target);
11954
+ const rawCandidate = path20.isAbsolute(target) ? target : path20.resolve(process.cwd(), target);
11775
11955
  let candidate = normalized(rawCandidate);
11776
11956
  const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
11777
11957
  if (rootRelative !== null) return rootRelative;
11778
11958
  try {
11779
11959
  const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
11780
- const physicalRoot = await fs18.realpath(projectRoot2);
11960
+ const physicalRoot = await fs19.realpath(projectRoot2);
11781
11961
  if (physicalCandidate) {
11782
11962
  const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
11783
11963
  if (physicalRootRelative !== null) return physicalRootRelative;
11784
11964
  candidate = normalized(physicalCandidate);
11785
11965
  }
11786
11966
  } catch {
11787
- if (!path19.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
11967
+ if (!path20.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
11788
11968
  }
11789
11969
  return candidate.replace(/^\.\//u, "");
11790
11970
  }
11791
11971
  async function loadGoverningChange(changeDir) {
11792
11972
  try {
11793
- const runtime = await ensureStrictClassicRuntimeRun(changeDir);
11973
+ const projection = await readClassicState(changeDir, { migrate: false });
11974
+ const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
11975
+ if (unknownKeys.length > 0) {
11976
+ throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
11977
+ }
11978
+ if (!projection.classic) throw new Error("Classic state projection is unavailable");
11794
11979
  return {
11795
11980
  changeDir,
11796
- phase: runtime.classic.phase,
11797
- classic: runtime.classic,
11798
- archived: runtime.classic.archived
11981
+ phase: projection.classic.phase,
11982
+ classic: projection.classic,
11983
+ archived: projection.classic.archived
11799
11984
  };
11800
11985
  } catch {
11801
11986
  const legacy = await readLegacyState(changeDir);
@@ -11809,15 +11994,15 @@ async function loadGoverningChange(changeDir) {
11809
11994
  }
11810
11995
  }
11811
11996
  async function activeChanges(projectRoot2) {
11812
- const changesDir = path19.join(projectRoot2, "openspec", "changes");
11997
+ const changesDir = path20.join(projectRoot2, "openspec", "changes");
11813
11998
  const governingChanges = [];
11814
11999
  if (!existsSync2(changesDir)) return governingChanges;
11815
- for (const entry2 of (await fs18.readdir(changesDir, { withFileTypes: true })).sort(
12000
+ for (const entry2 of (await fs19.readdir(changesDir, { withFileTypes: true })).sort(
11816
12001
  (left, right) => left.name.localeCompare(right.name)
11817
12002
  )) {
11818
12003
  if (!entry2.isDirectory() || entry2.name === "archive") continue;
11819
- const changeDir = path19.join(changesDir, entry2.name);
11820
- if (!existsSync2(path19.join(changeDir, ".comet.yaml"))) continue;
12004
+ const changeDir = path20.join(changesDir, entry2.name);
12005
+ if (!existsSync2(path20.join(changeDir, ".comet.yaml"))) continue;
11821
12006
  const governing = await loadGoverningChange(changeDir);
11822
12007
  if (!governing || governing.archived) continue;
11823
12008
  governingChanges.push(governing);
@@ -11825,13 +12010,47 @@ async function activeChanges(projectRoot2) {
11825
12010
  return governingChanges;
11826
12011
  }
11827
12012
  function isSuperpowersArtifactPath(relativePath2) {
11828
- return relativePath2.startsWith("docs/superpowers/");
12013
+ return comparisonKey(relativePath2).startsWith("docs/superpowers/");
12014
+ }
12015
+ var SUPERPOWERS_ARTIFACT_SLOTS = [
12016
+ {
12017
+ prefix: "docs/superpowers/specs/",
12018
+ field: "designDoc",
12019
+ wireField: "design_doc",
12020
+ phase: "design"
12021
+ },
12022
+ {
12023
+ prefix: "docs/superpowers/plans/",
12024
+ field: "plan",
12025
+ wireField: "plan",
12026
+ phase: "build"
12027
+ },
12028
+ {
12029
+ prefix: "docs/superpowers/reports/",
12030
+ field: "verificationReport",
12031
+ wireField: "verification_report",
12032
+ phase: "verify"
12033
+ }
12034
+ ];
12035
+ function standardSuperpowersArtifactSlot(relativePath2) {
12036
+ const key = comparisonKey(relativePath2);
12037
+ const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix));
12038
+ if (!slot) return null;
12039
+ const fileName = key.slice(slot.prefix.length);
12040
+ if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null;
12041
+ return slot;
12042
+ }
12043
+ function superpowersArtifactValue(governing, slot) {
12044
+ return governing.classic?.[slot.field] ?? null;
12045
+ }
12046
+ function allowsFirstSuperpowersArtifactWrite(governing, slot) {
12047
+ return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot);
11829
12048
  }
11830
12049
  function allowsSuperpowersArtifacts(governing) {
11831
12050
  return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
11832
12051
  }
11833
12052
  function governingChangeName(governing) {
11834
- return governing.changeDir ? path19.basename(governing.changeDir) : null;
12053
+ return governing.changeDir ? path20.basename(governing.changeDir) : null;
11835
12054
  }
11836
12055
  var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
11837
12056
  "design",
@@ -11851,7 +12070,7 @@ function matchesRecordedSuperpowersArtifact(relativePath2, governing) {
11851
12070
  governing.classic?.verificationReport
11852
12071
  ];
11853
12072
  return artifactPaths.some(
11854
- (artifactPath) => artifactPath && normalized(artifactPath) === relativePath2
12073
+ (artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2)
11855
12074
  );
11856
12075
  }
11857
12076
  function matchesSuperpowersArtifactName(relativePath2, changeName) {
@@ -11867,7 +12086,7 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
11867
12086
  const recorded = active.find(
11868
12087
  (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
11869
12088
  );
11870
- if (recorded) return recorded;
12089
+ if (recorded) return { governing: recorded, match: "recorded" };
11871
12090
  const eligible = active.filter(allowsSuperpowersArtifacts);
11872
12091
  const named = eligible.filter((governing) => {
11873
12092
  const name = governingChangeName(governing);
@@ -11875,7 +12094,7 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
11875
12094
  }).sort(
11876
12095
  (a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0)
11877
12096
  )[0];
11878
- if (named) return named;
12097
+ if (named) return { governing: named, match: "named" };
11879
12098
  return null;
11880
12099
  }
11881
12100
  async function repoSourceGoverningChange(projectRoot2, relativePath2) {
@@ -11897,7 +12116,30 @@ async function repoSourceGoverningChange(projectRoot2, relativePath2) {
11897
12116
  )
11898
12117
  };
11899
12118
  }
11900
- if (active.length === 1) return active[0];
12119
+ if (active.length === 1) {
12120
+ const sole = active[0];
12121
+ if (sole.changeDir !== null) {
12122
+ const outcome = await resolveBranchBinding(sole.changeDir, {
12123
+ heal: false,
12124
+ cwd: projectRoot2
12125
+ });
12126
+ const name = governingChangeName(sole) ?? "unknown";
12127
+ if (outcome.status === "drift") {
12128
+ return {
12129
+ blockedResult: blockedStaleSelection(
12130
+ relativePath2,
12131
+ driftStaleReason(name, outcome.boundBranch, outcome.currentBranch)
12132
+ )
12133
+ };
12134
+ }
12135
+ if (outcome.status === "unbound-detached") {
12136
+ return {
12137
+ blockedResult: blockedStaleSelection(relativePath2, unboundDetachedMessage(name))
12138
+ };
12139
+ }
12140
+ }
12141
+ return sole;
12142
+ }
11901
12143
  return {
11902
12144
  blockedResult: blockedMultipleChanges(
11903
12145
  relativePath2,
@@ -11911,8 +12153,8 @@ async function governingChange(relativePath2, projectRoot2) {
11911
12153
  const rest = relativePath2.slice(prefix.length);
11912
12154
  const [name] = rest.split("/");
11913
12155
  if (name && name !== "archive") {
11914
- const changeDir = path19.join(projectRoot2, "openspec", "changes", name);
11915
- const stateFile2 = path19.join(changeDir, ".comet.yaml");
12156
+ const changeDir = path20.join(projectRoot2, "openspec", "changes", name);
12157
+ const stateFile2 = path20.join(changeDir, ".comet.yaml");
11916
12158
  if (existsSync2(stateFile2)) {
11917
12159
  const governing = await loadGoverningChange(changeDir);
11918
12160
  if (governing) return governing;
@@ -11923,7 +12165,26 @@ async function governingChange(relativePath2, projectRoot2) {
11923
12165
  }
11924
12166
  if (isSuperpowersArtifactPath(relativePath2)) {
11925
12167
  const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
11926
- if (superpowers) return { ...superpowers, superpowersArtifact: "matched" };
12168
+ if (superpowers?.match === "recorded") {
12169
+ return { ...superpowers.governing, superpowersArtifact: "matched" };
12170
+ }
12171
+ const slot = standardSuperpowersArtifactSlot(relativePath2);
12172
+ if (superpowers) {
12173
+ return slot ? {
12174
+ ...superpowers.governing,
12175
+ superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched",
12176
+ superpowersSlot: slot
12177
+ } : { ...superpowers.governing, superpowersArtifact: "matched" };
12178
+ }
12179
+ if (slot) {
12180
+ const candidate = await repoSourceGoverningChange(projectRoot2, relativePath2);
12181
+ if (!candidate || "blockedResult" in candidate) return candidate;
12182
+ return {
12183
+ ...candidate,
12184
+ superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched",
12185
+ superpowersSlot: slot
12186
+ };
12187
+ }
11927
12188
  const fallback = (await activeChanges(projectRoot2))[0] ?? null;
11928
12189
  return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
11929
12190
  }
@@ -11971,7 +12232,7 @@ function blocked(relativePath2, phase) {
11971
12232
  " BLOCKED: source writes are not allowed during design",
11972
12233
  " This phase does not allow source writes",
11973
12234
  " ALLOWED: run brainstorming, create the Design Doc, and run guard",
11974
- " NEXT: finish the Design Doc, then run comet-guard design --apply to enter build"
12235
+ " NEXT: finish the Design Doc, then run comet guard <change-name> design --apply to enter build"
11975
12236
  ] : [
11976
12237
  " BLOCKED: source writes are not allowed during archive",
11977
12238
  " This phase does not allow source writes",
@@ -12012,7 +12273,25 @@ function blockedMissingDesignDoc(relativePath2) {
12012
12273
  ].join("\n")
12013
12274
  );
12014
12275
  }
12015
- function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
12276
+ function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) {
12277
+ const slot = governing.superpowersSlot;
12278
+ const recorded = slot ? superpowersArtifactValue(governing, slot) : null;
12279
+ const details = slot ? governing.phase !== slot.phase ? [
12280
+ ` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`,
12281
+ ` Expected phase: ${slot.phase}`,
12282
+ " NEXT: resume the matching Comet phase or use an already recorded artifact path"
12283
+ ] : recorded ? [
12284
+ ` BLOCKED: ${slot.wireField} is already recorded for this change`,
12285
+ ` Recorded path: ${recorded}`,
12286
+ " NEXT: write the recorded artifact or explicitly correct the state path"
12287
+ ] : [
12288
+ " BLOCKED: standard Superpowers artifact state is incomplete",
12289
+ " NEXT: validate the active change state, then retry the matching phase"
12290
+ ] : [
12291
+ " BLOCKED: unmatched Superpowers artifact",
12292
+ " This docs/superpowers/ path does not match any active change artifact",
12293
+ " NEXT: use a recorded artifact path or a standard phase artifact directory"
12294
+ ];
12016
12295
  return result(
12017
12296
  2,
12018
12297
  [
@@ -12021,12 +12300,10 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
12021
12300
  "║ COMET PHASE GUARD — WRITE BLOCKED ║",
12022
12301
  "╚══════════════════════════════════════════╝",
12023
12302
  "",
12024
- ` Current phase: ${phase}`,
12303
+ ` Current phase: ${governing.phase}`,
12025
12304
  ` Target file: ${relativePath2}`,
12026
12305
  "",
12027
- " BLOCKED: unmatched Superpowers artifact",
12028
- " This docs/superpowers/ path does not match any active change artifact",
12029
- " NEXT: record the artifact path in .comet.yaml or include the change name in the artifact filename",
12306
+ ...details,
12030
12307
  ""
12031
12308
  ].join("\n")
12032
12309
  );
@@ -12104,7 +12381,7 @@ var classicHookGuardCommand = async (args) => {
12104
12381
  return allowed(`${relativePath2} (phase: ${phase}, superpowers)`);
12105
12382
  }
12106
12383
  if (governing.superpowersArtifact === "unmatched") {
12107
- return blockedUnmatchedSuperpowersArtifact(relativePath2, phase);
12384
+ return blockedUnmatchedSuperpowersArtifact(relativePath2, governing);
12108
12385
  }
12109
12386
  }
12110
12387
  if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) {
@@ -12485,8 +12762,8 @@ var classicIntentCommand = async (args, _options) => {
12485
12762
  };
12486
12763
 
12487
12764
  // domains/comet-classic/classic-resume-probe.ts
12488
- import path20 from "path";
12489
- import { promises as fs19 } from "fs";
12765
+ import path21 from "path";
12766
+ import { promises as fs20 } from "fs";
12490
12767
  import { spawn } from "child_process";
12491
12768
  var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
12492
12769
  function isRecord2(value) {
@@ -12529,13 +12806,13 @@ function result3(action, change, confidence, reason, evidence = []) {
12529
12806
  }
12530
12807
  async function readIfExists(filePath) {
12531
12808
  if (!await fileExists3(filePath)) return "";
12532
- return fs19.readFile(filePath, "utf8");
12809
+ return fs20.readFile(filePath, "utf8");
12533
12810
  }
12534
12811
  async function changeSearchText(changeDir, classic) {
12535
12812
  const files = ["proposal.md", "design.md", "tasks.md"];
12536
12813
  const parts = [classic.name, classic.workflow, classic.phase];
12537
12814
  for (const file of files) {
12538
- parts.push(await readIfExists(path20.join(changeDir, file)));
12815
+ parts.push(await readIfExists(path21.join(changeDir, file)));
12539
12816
  }
12540
12817
  return parts.join("\n").toLowerCase();
12541
12818
  }
@@ -12599,19 +12876,19 @@ function diagnosticFromProjection(changeDir, name, projection) {
12599
12876
  };
12600
12877
  }
12601
12878
  async function hasOpenSpecChangeFiles(changeDir) {
12602
- return await fileExists3(path20.join(changeDir, "proposal.md")) || await fileExists3(path20.join(changeDir, "design.md")) || await fileExists3(path20.join(changeDir, "tasks.md"));
12879
+ return await fileExists3(path21.join(changeDir, "proposal.md")) || await fileExists3(path21.join(changeDir, "design.md")) || await fileExists3(path21.join(changeDir, "tasks.md"));
12603
12880
  }
12604
12881
  async function discoverActiveChanges(projectRoot2) {
12605
- const changesDir = path20.join(projectRoot2, "openspec", "changes");
12882
+ const changesDir = path21.join(projectRoot2, "openspec", "changes");
12606
12883
  if (!await fileExists3(changesDir)) return [];
12607
12884
  const entries = await readDir(changesDir);
12608
12885
  const changes = [];
12609
12886
  for (const entry2 of entries) {
12610
12887
  if (entry2 === "archive") continue;
12611
- const changeDir = path20.join(changesDir, entry2);
12612
- const stat = await fs19.stat(changeDir).catch(() => null);
12888
+ const changeDir = path21.join(changesDir, entry2);
12889
+ const stat = await fs20.stat(changeDir).catch(() => null);
12613
12890
  if (!stat?.isDirectory()) continue;
12614
- const hasCometState = await fileExists3(path20.join(changeDir, ".comet.yaml"));
12891
+ const hasCometState = await fileExists3(path21.join(changeDir, ".comet.yaml"));
12615
12892
  if (!hasCometState) {
12616
12893
  if (!await hasOpenSpecChangeFiles(changeDir)) continue;
12617
12894
  const missingStateChange = {
@@ -12911,11 +13188,11 @@ var classicResumeProbeCommand = async (args) => {
12911
13188
  };
12912
13189
 
12913
13190
  // domains/comet-classic/classic-state-command.ts
12914
- var import_yaml7 = __toESM(require_dist(), 1);
13191
+ var import_yaml8 = __toESM(require_dist(), 1);
12915
13192
  import { spawnSync as spawnSync3 } from "child_process";
12916
- import { randomUUID as randomUUID7 } from "crypto";
12917
- import { existsSync as existsSync3, promises as fs20 } from "fs";
12918
- import path21 from "path";
13193
+ import { randomUUID as randomUUID8 } from "crypto";
13194
+ import { existsSync as existsSync3, promises as fs21 } from "fs";
13195
+ import path22 from "path";
12919
13196
  init_state();
12920
13197
  var GREEN5 = "\x1B[32m";
12921
13198
  var RED5 = "\x1B[31m";
@@ -12928,8 +13205,10 @@ var EVENTS = CLASSIC_TRANSITION_EVENTS;
12928
13205
  var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
12929
13206
  ...RUN_WIRE_KEYS,
12930
13207
  "archive_confirmation",
13208
+ "verify_failures",
12931
13209
  "classic_profile",
12932
- "classic_migration"
13210
+ "classic_migration",
13211
+ "bound_branch"
12933
13212
  ]);
12934
13213
  var SETTABLE_FIELDS = new Set(
12935
13214
  CLASSIC_WIRE_KEYS.filter((field2) => !MACHINE_OWNED_FIELDS.has(field2))
@@ -12943,7 +13222,7 @@ var FIELD_ENUMS = {
12943
13222
  subagent_dispatch: ["null", "confirmed"],
12944
13223
  tdd_mode: ["tdd", "direct"],
12945
13224
  review_mode: ["off", "standard", "thorough"],
12946
- isolation: ["branch", "worktree"],
13225
+ isolation: ["current", "branch", "worktree"],
12947
13226
  verify_mode: ["light", "full"],
12948
13227
  auto_transition: ["true", "false"],
12949
13228
  verify_result: ["pending", "pass", "fail"],
@@ -12966,6 +13245,7 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
12966
13245
  verifiedAt: "verified_at",
12967
13246
  archiveConfirmation: "archive_confirmation",
12968
13247
  verifyResult: "verify_result",
13248
+ verifyFailures: "verify_failures",
12969
13249
  workflow: "workflow"
12970
13250
  };
12971
13251
  var CommandFailure = class extends Error {
@@ -13026,7 +13306,7 @@ function validateRelativePath(value, field2) {
13026
13306
  }
13027
13307
  async function exists6(file) {
13028
13308
  try {
13029
- await fs20.access(file);
13309
+ await fs21.access(file);
13030
13310
  return true;
13031
13311
  } catch (error) {
13032
13312
  if (error.code === "ENOENT") return false;
@@ -13035,7 +13315,7 @@ async function exists6(file) {
13035
13315
  }
13036
13316
  async function nonempty3(file) {
13037
13317
  try {
13038
- return (await fs20.stat(file)).size > 0;
13318
+ return (await fs21.stat(file)).size > 0;
13039
13319
  } catch (error) {
13040
13320
  if (error.code === "ENOENT") return false;
13041
13321
  throw error;
@@ -13047,27 +13327,27 @@ async function changeDirectory2(name) {
13047
13327
  async function readDocument2(file) {
13048
13328
  let source;
13049
13329
  try {
13050
- source = await fs20.readFile(file, "utf8");
13330
+ source = await fs21.readFile(file, "utf8");
13051
13331
  } catch (error) {
13052
13332
  if (error.code === "ENOENT") {
13053
13333
  fail2(
13054
- `ERROR: .comet.yaml not found at ${path21.relative(process.cwd(), file).replaceAll("\\", "/")}`
13334
+ `ERROR: .comet.yaml not found at ${path22.relative(process.cwd(), file).replaceAll("\\", "/")}`
13055
13335
  );
13056
13336
  }
13057
13337
  throw error;
13058
13338
  }
13059
- const document = (0, import_yaml7.parseDocument)(source, { uniqueKeys: false });
13339
+ const document = (0, import_yaml8.parseDocument)(source, { uniqueKeys: false });
13060
13340
  if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
13061
13341
  return document;
13062
13342
  }
13063
13343
  async function atomicWrite2(file, content) {
13064
- await fs20.mkdir(path21.dirname(file), { recursive: true });
13065
- const temporary = `${file}.${randomUUID7()}.tmp`;
13344
+ await fs21.mkdir(path22.dirname(file), { recursive: true });
13345
+ const temporary = `${file}.${randomUUID8()}.tmp`;
13066
13346
  try {
13067
- await fs20.writeFile(temporary, content, "utf8");
13068
- await fs20.rename(temporary, file);
13347
+ await fs21.writeFile(temporary, content, "utf8");
13348
+ await fs21.rename(temporary, file);
13069
13349
  } catch (error) {
13070
- await fs20.rm(temporary, { force: true });
13350
+ await fs21.rm(temporary, { force: true });
13071
13351
  throw error;
13072
13352
  }
13073
13353
  }
@@ -13100,6 +13380,10 @@ function nullableRecordBoolean(record, field2) {
13100
13380
  if (value === "false") return false;
13101
13381
  return null;
13102
13382
  }
13383
+ function nonNegativeRecordInteger(record, field2, fallback = 0) {
13384
+ const value = record[field2];
13385
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : fallback;
13386
+ }
13103
13387
  function sparseClassicState(record) {
13104
13388
  const workflow = enumRecordValue(record, "workflow", PROFILES, "full");
13105
13389
  return {
@@ -13127,7 +13411,13 @@ function sparseClassicState(record) {
13127
13411
  ["off", "standard", "thorough"],
13128
13412
  null
13129
13413
  ),
13130
- isolation: enumRecordValue(record, "isolation", ["branch", "worktree"], null),
13414
+ isolation: enumRecordValue(
13415
+ record,
13416
+ "isolation",
13417
+ ["current", "branch", "worktree"],
13418
+ null
13419
+ ),
13420
+ boundBranch: nullableRecordString(record, "bound_branch"),
13131
13421
  verifyMode: enumRecordValue(record, "verify_mode", ["light", "full"], null),
13132
13422
  autoTransition: nullableRecordBoolean(record, "auto_transition"),
13133
13423
  baseRef: nullableRecordString(record, "base_ref"),
@@ -13139,6 +13429,7 @@ function sparseClassicState(record) {
13139
13429
  ["pending", "pass", "fail"],
13140
13430
  "pending"
13141
13431
  ),
13432
+ verifyFailures: nonNegativeRecordInteger(record, "verify_failures"),
13142
13433
  verificationReport: nullableRecordString(record, "verification_report"),
13143
13434
  branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
13144
13435
  createdAt: nullableRecordString(record, "created_at"),
@@ -13199,7 +13490,7 @@ async function stateFile(name) {
13199
13490
  const change = await changeDirectory2(name);
13200
13491
  return {
13201
13492
  ...change,
13202
- file: path21.join(change.directory, ".comet.yaml")
13493
+ file: path22.join(change.directory, ".comet.yaml")
13203
13494
  };
13204
13495
  }
13205
13496
  async function readField3(name, field2) {
@@ -13217,7 +13508,7 @@ async function readField3(name, field2) {
13217
13508
  return scalar(value);
13218
13509
  }
13219
13510
  function parsedValue(field2, value) {
13220
- const document = (0, import_yaml7.parseDocument)(`${field2}: ${value}
13511
+ const document = (0, import_yaml8.parseDocument)(`${field2}: ${value}
13221
13512
  `);
13222
13513
  if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
13223
13514
  return document.get(field2);
@@ -13252,7 +13543,35 @@ async function setField2(output, name, field2, value, options = {}) {
13252
13543
  validateSetValue(field2, value);
13253
13544
  const { file, directory } = await stateFile(name);
13254
13545
  const document = await readDocument2(file);
13546
+ const previousRecord = document.toJS() ?? {};
13255
13547
  document.set(field2, parsedValue(field2, value));
13548
+ if (field2 === "isolation") {
13549
+ if (requiresBranchBinding(value)) {
13550
+ const previousIsolation = typeof previousRecord.isolation === "string" ? previousRecord.isolation : null;
13551
+ const existing = previousRecord.bound_branch;
13552
+ const alreadyBound = typeof existing === "string" && existing !== "";
13553
+ if (!alreadyBound || previousIsolation !== value) {
13554
+ const currentBranch = liveGitBranch(process.cwd());
13555
+ const verdict = evaluateBranchBinding({
13556
+ isolation: value,
13557
+ boundBranch: null,
13558
+ currentBranch,
13559
+ gitWorkTree: currentBranch === null ? isGitWorkTree(process.cwd()) : true
13560
+ });
13561
+ if (verdict.status === "needs-heal") {
13562
+ document.set("bound_branch", verdict.branch);
13563
+ } else if (verdict.status === "unbound-detached") {
13564
+ fail2(
13565
+ `ERROR: cannot bind isolation=${value} while HEAD is detached; checkout a branch first`
13566
+ );
13567
+ } else {
13568
+ document.set("bound_branch", null);
13569
+ }
13570
+ }
13571
+ } else {
13572
+ document.set("bound_branch", null);
13573
+ }
13574
+ }
13256
13575
  const run = await readRunState(directory);
13257
13576
  const projection = parseClassicStateDocument(document.toJS(), run);
13258
13577
  if (projection.run) {
@@ -13302,10 +13621,10 @@ async function init(output, name, workflow) {
13302
13621
  validateEnum(workflow, PROFILES);
13303
13622
  const { file, label, directory } = await stateFile(name);
13304
13623
  if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
13305
- await fs20.mkdir(directory, { recursive: true });
13624
+ await fs21.mkdir(directory, { recursive: true });
13306
13625
  const preset = workflow !== "full";
13307
13626
  const reviewMode = preset ? "off" : await reviewModeDefault();
13308
- const document = new import_yaml7.Document({
13627
+ const document = new import_yaml8.Document({
13309
13628
  workflow,
13310
13629
  language: await projectLanguageDefault(),
13311
13630
  phase: "open",
@@ -13315,13 +13634,14 @@ async function init(output, name, workflow) {
13315
13634
  subagent_dispatch: null,
13316
13635
  tdd_mode: preset ? "direct" : null,
13317
13636
  review_mode: reviewMode,
13318
- isolation: preset ? "branch" : null,
13637
+ isolation: null,
13319
13638
  verify_mode: preset ? "light" : null,
13320
13639
  auto_transition: await autoTransition() === "true",
13321
13640
  base_ref: gitOutput(["rev-parse", "--verify", "HEAD"]),
13322
13641
  design_doc: null,
13323
13642
  plan: null,
13324
13643
  verify_result: "pending",
13644
+ verify_failures: 0,
13325
13645
  verification_report: null,
13326
13646
  branch_status: "pending",
13327
13647
  created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
@@ -13346,9 +13666,10 @@ async function requireBuildDecisions(name) {
13346
13666
  const subagentDispatch = await readField3(name, "subagent_dispatch");
13347
13667
  const tddMode = await readField3(name, "tdd_mode");
13348
13668
  const reviewMode = await readField3(name, "review_mode");
13349
- if (!["branch", "worktree"].includes(isolation)) {
13669
+ const allowedIsolation = ["current", "branch", "worktree"];
13670
+ if (!allowedIsolation.includes(isolation)) {
13350
13671
  fail2(
13351
- `ERROR: Cannot transition '${name}': isolation must be branch or worktree, got '${isolation || "null"}'`
13672
+ `ERROR: Cannot transition '${name}': isolation must be current, branch, or worktree, got '${isolation || "null"}'`
13352
13673
  );
13353
13674
  }
13354
13675
  if (!["subagent-driven-development", "executing-plans", "direct"].includes(buildMode)) {
@@ -13381,13 +13702,13 @@ async function requireOpenArtifacts(name) {
13381
13702
  const { directory } = await stateFile(name);
13382
13703
  const workflow = await readField3(name, "workflow");
13383
13704
  for (const artifact of ["proposal.md", "tasks.md"]) {
13384
- if (!await nonempty3(path21.join(directory, artifact))) {
13705
+ if (!await nonempty3(path22.join(directory, artifact))) {
13385
13706
  fail2(
13386
13707
  `ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
13387
13708
  );
13388
13709
  }
13389
13710
  }
13390
- if (workflow === "full" && !await nonempty3(path21.join(directory, "design.md"))) {
13711
+ if (workflow === "full" && !await nonempty3(path22.join(directory, "design.md"))) {
13391
13712
  fail2(
13392
13713
  `ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
13393
13714
  );
@@ -13395,14 +13716,14 @@ async function requireOpenArtifacts(name) {
13395
13716
  }
13396
13717
  async function requireDesignEvidence(name) {
13397
13718
  const designDoc = await readField3(name, "design_doc");
13398
- if (!designDoc || designDoc === "null" || !await nonempty3(path21.resolve(designDoc))) {
13719
+ if (!designDoc || designDoc === "null" || !await nonempty3(path22.resolve(designDoc))) {
13399
13720
  fail2(
13400
13721
  `ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
13401
13722
  );
13402
13723
  }
13403
13724
  }
13404
13725
  async function writeSparseTransitionEffects(directory, effects) {
13405
- const file = path21.join(directory, ".comet.yaml");
13726
+ const file = path22.join(directory, ".comet.yaml");
13406
13727
  const document = await readDocument2(file);
13407
13728
  for (const effect of effects) {
13408
13729
  const field2 = wireField2(effect.field);
@@ -13417,7 +13738,7 @@ async function applyTransitionEvent(output, name, event) {
13417
13738
  let sparse = false;
13418
13739
  if (!classic) {
13419
13740
  if (projection.run) fail2("ERROR: Classic state projection is missing");
13420
- const document = await readDocument2(path21.join(directory, ".comet.yaml"));
13741
+ const document = await readDocument2(path22.join(directory, ".comet.yaml"));
13421
13742
  classic = sparseClassicState(document.toJS());
13422
13743
  sparse = true;
13423
13744
  }
@@ -13464,14 +13785,11 @@ async function transition(output, name, event) {
13464
13785
  } else if (event === "verify-pass") {
13465
13786
  await requirePhase(name, "verify");
13466
13787
  const report = await readField3(name, "verification_report");
13467
- if (!report || !await exists6(path21.resolve(report))) {
13788
+ if (!report || !await exists6(path22.resolve(report))) {
13468
13789
  fail2(
13469
13790
  `ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
13470
13791
  );
13471
13792
  }
13472
- if (await readField3(name, "branch_status") !== "handled") {
13473
- fail2(`ERROR: Cannot transition '${name}': branch_status must be handled`);
13474
- }
13475
13793
  } else if (event === "verify-fail") {
13476
13794
  await requirePhase(name, "verify");
13477
13795
  } else if (event === "archive-confirm") {
@@ -13531,9 +13849,9 @@ async function next(output, name) {
13531
13849
  async function taskCheckoff(output, taskFile, taskText) {
13532
13850
  validateRelativePath(taskFile, "task file");
13533
13851
  if (!taskText) fail2("ERROR: Task text cannot be empty");
13534
- const file = path21.resolve(taskFile);
13852
+ const file = path22.resolve(taskFile);
13535
13853
  if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
13536
- const lines = (await fs20.readFile(file, "utf8")).split(/\r?\n/u);
13854
+ const lines = (await fs21.readFile(file, "utf8")).split(/\r?\n/u);
13537
13855
  const matches = lines.filter(
13538
13856
  (line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
13539
13857
  );
@@ -13571,21 +13889,21 @@ async function check2(output, name, phase) {
13571
13889
  designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
13572
13890
  );
13573
13891
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
13574
- (await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
13575
- `${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13892
+ (await nonempty3(path22.join(directory, artifact)) ? pass2 : reject)(
13893
+ `${artifact} ${await nonempty3(path22.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13576
13894
  );
13577
13895
  }
13578
13896
  } else if (phase === "build") {
13579
13897
  const workflow = await readField3(name, "workflow");
13580
13898
  const designDoc = await readField3(name, "design_doc");
13581
13899
  if (workflow === "full") {
13582
- (designDoc && designDoc !== "null" && await exists6(path21.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
13900
+ (designDoc && designDoc !== "null" && await exists6(path22.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
13583
13901
  } else {
13584
13902
  pass2(`workflow=${workflow} (design_doc not required)`);
13585
13903
  }
13586
13904
  for (const artifact of ["proposal.md", "tasks.md"]) {
13587
- (await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
13588
- `${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13905
+ (await nonempty3(path22.join(directory, artifact)) ? pass2 : reject)(
13906
+ `${artifact} ${await nonempty3(path22.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
13589
13907
  );
13590
13908
  }
13591
13909
  } else if (phase === "verify") {
@@ -13598,6 +13916,29 @@ async function check2(output, name, phase) {
13598
13916
  const archived = await readField3(name, "archived");
13599
13917
  (archived !== "true" ? pass2 : reject)(`archived=${archived} (expected: not true)`);
13600
13918
  }
13919
+ const binding = await resolveBranchBinding(directory, { heal: true, cwd: process.cwd() });
13920
+ if (binding.bindingRequired) {
13921
+ switch (binding.status) {
13922
+ case "drift":
13923
+ reject(driftBlockedMessage(name, binding.boundBranch, binding.currentBranch));
13924
+ break;
13925
+ case "unbound-detached":
13926
+ reject(unboundDetachedMessage(name));
13927
+ break;
13928
+ case "healed":
13929
+ pass2(`bound_branch lazily set to ${binding.branch}`);
13930
+ break;
13931
+ case "needs-heal":
13932
+ case "ok":
13933
+ case "not-applicable":
13934
+ pass2("bound_branch matches current branch");
13935
+ break;
13936
+ default: {
13937
+ const exhaustive = binding;
13938
+ throw new Error(`unhandled branch binding status: ${JSON.stringify(exhaustive)}`);
13939
+ }
13940
+ }
13941
+ }
13601
13942
  output.stdout.push("");
13602
13943
  if (blocked2) {
13603
13944
  output.stderr.push(red4("BLOCKED — fix failing checks before proceeding"));
@@ -13607,7 +13948,7 @@ async function check2(output, name, phase) {
13607
13948
  }
13608
13949
  function fieldStatus(field2, value, file) {
13609
13950
  if (!value || value === "null") return ` - ${field2}: PENDING`;
13610
- if (file && !existsSync3(path21.resolve(file))) {
13951
+ if (file && !existsSync3(path22.resolve(file))) {
13611
13952
  return ` - ${field2}: BROKEN (path ${value} does not exist)`;
13612
13953
  }
13613
13954
  return ` - ${field2}: DONE (${value})`;
@@ -13616,7 +13957,7 @@ async function recoverOpen(output, directory) {
13616
13957
  output.stdout.push(" Artifacts:");
13617
13958
  let complete = 0;
13618
13959
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
13619
- const done = await nonempty3(path21.join(directory, artifact));
13960
+ const done = await nonempty3(path22.join(directory, artifact));
13620
13961
  if (done) complete += 1;
13621
13962
  output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
13622
13963
  }
@@ -13629,7 +13970,7 @@ async function recoverDesign(output, name, directory) {
13629
13970
  output.stdout.push(" Artifacts:");
13630
13971
  for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
13631
13972
  output.stdout.push(
13632
- ` - ${artifact}: ${await nonempty3(path21.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
13973
+ ` - ${artifact}: ${await nonempty3(path22.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
13633
13974
  );
13634
13975
  }
13635
13976
  const handoff = await readField3(name, "handoff_context");
@@ -13643,11 +13984,11 @@ async function recoverDesign(output, name, directory) {
13643
13984
  fieldStatus("design_doc", design, design),
13644
13985
  ""
13645
13986
  );
13646
- if (design && design !== "null" && await exists6(path21.resolve(design))) {
13987
+ if (design && design !== "null" && await exists6(path22.resolve(design))) {
13647
13988
  output.stdout.push(
13648
13989
  "Recovery action: Design Doc already created and linked. Run guard to transition to build."
13649
13990
  );
13650
- } else if (handoff && handoff !== "null" && await exists6(path21.resolve(handoff))) {
13991
+ } else if (handoff && handoff !== "null" && await exists6(path22.resolve(handoff))) {
13651
13992
  output.stdout.push(
13652
13993
  "Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
13653
13994
  );
@@ -13677,7 +14018,7 @@ async function recoverBuild(output, name, directory, workflow) {
13677
14018
  decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
13678
14019
  }
13679
14020
  output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
13680
- const tasks = path21.join(directory, "tasks.md");
14021
+ const tasks = path22.join(directory, "tasks.md");
13681
14022
  if (!await exists6(tasks)) {
13682
14023
  output.stdout.push(
13683
14024
  " Tasks: tasks.md MISSING",
@@ -13686,14 +14027,14 @@ async function recoverBuild(output, name, directory, workflow) {
13686
14027
  );
13687
14028
  return;
13688
14029
  }
13689
- const lines = (await fs20.readFile(tasks, "utf8")).split(/\r?\n/u);
14030
+ const lines = (await fs21.readFile(tasks, "utf8")).split(/\r?\n/u);
13690
14031
  const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
13691
14032
  const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
13692
14033
  const pending = total - done;
13693
14034
  let planTotal = 0;
13694
14035
  let planDone = 0;
13695
- if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
13696
- const planLines = (await fs20.readFile(path21.resolve(plan), "utf8")).split(/\r?\n/u);
14036
+ if (plan && plan !== "null" && await exists6(path22.resolve(plan))) {
14037
+ const planLines = (await fs21.readFile(path22.resolve(plan), "utf8")).split(/\r?\n/u);
13697
14038
  planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
13698
14039
  planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
13699
14040
  }
@@ -13731,7 +14072,7 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
13731
14072
  }
13732
14073
  if (pause === "plan-ready") {
13733
14074
  if (buildMode === "subagent-driven-development" && (pending > 0 || planPending > 0)) {
13734
- return subagentDispatch === "confirmed" ? "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then inspect the first unchecked task (OpenSpec or plan additions) against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Plan-ready pause is stale and subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
14075
+ return subagentDispatch === "confirmed" ? "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then inspect the first unchecked task (OpenSpec or plan additions) against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Plan-ready pause is stale and subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
13735
14076
  }
13736
14077
  if (pending > 0 || planPending > 0) {
13737
14078
  return "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then continue from the first unchecked task.";
@@ -13752,13 +14093,13 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
13752
14093
  }
13753
14094
  if (pending > 0) {
13754
14095
  if (buildMode === "subagent-driven-development") {
13755
- return subagentDispatch === "confirmed" ? "Recovery action: Read tasks.md and the Superpowers plan (which may include additions beyond OpenSpec), then inspect the first unchecked task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
14096
+ return subagentDispatch === "confirmed" ? "Recovery action: Read tasks.md and the Superpowers plan (which may include additions beyond OpenSpec), then inspect the first unchecked task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
13756
14097
  }
13757
14098
  return "Recovery action: Read tasks.md and continue from first unchecked task.";
13758
14099
  }
13759
14100
  if (planPending > 0) {
13760
14101
  if (buildMode === "subagent-driven-development") {
13761
- return subagentDispatch === "confirmed" ? "Recovery action: Read the Superpowers plan, then inspect the first unchecked Superpowers plan task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
14102
+ return subagentDispatch === "confirmed" ? "Recovery action: Read the Superpowers plan, then inspect the first unchecked Superpowers plan task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
13762
14103
  }
13763
14104
  return "Recovery action: Read the Superpowers plan and continue from the first unchecked plan task.";
13764
14105
  }
@@ -13766,17 +14107,19 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
13766
14107
  }
13767
14108
  async function recoverVerify(output, name) {
13768
14109
  const result5 = await readField3(name, "verify_result");
14110
+ const failures = await readField3(name, "verify_failures");
13769
14111
  const mode = await readField3(name, "verify_mode");
13770
14112
  const report = await readField3(name, "verification_report");
13771
14113
  const branch = await readField3(name, "branch_status");
13772
14114
  output.stdout.push(
13773
14115
  " Verification:",
13774
14116
  fieldStatus("verify_result", result5),
14117
+ ` - verify_failures: ${failures || "0"}`,
13775
14118
  fieldStatus("verify_mode", mode),
13776
14119
  fieldStatus("verification_report", report, report),
13777
- fieldStatus("branch_status", branch),
14120
+ branch === "handled" ? " - branch_status: LEGACY (handled before archive; archive still owns final closure)" : " - branch_status: DEFERRED (handled after the archive commit)",
13778
14121
  "",
13779
- result5 === "pass" && branch === "handled" ? "Recovery action: Verification complete. Run guard to transition to archive." : result5 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
14122
+ result5 === "pass" ? "Recovery action: Verification complete. Continue to archive; branch handling happens after archive changes are committed." : result5 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
13780
14123
  );
13781
14124
  }
13782
14125
  async function recoverArchive(output, name) {
@@ -13822,19 +14165,19 @@ async function scale(output, name) {
13822
14165
  validateChangeName4(name);
13823
14166
  const { file, directory, label } = await stateFile(name);
13824
14167
  if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
13825
- const tasksFile = path21.join(directory, "tasks.md");
13826
- const taskCount = await exists6(tasksFile) ? (await fs20.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
13827
- const specs = path21.join(directory, "specs");
14168
+ const tasksFile = path22.join(directory, "tasks.md");
14169
+ const taskCount = await exists6(tasksFile) ? (await fs21.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
14170
+ const specs = path22.join(directory, "specs");
13828
14171
  let deltaSpecs = 0;
13829
14172
  if (await exists6(specs)) {
13830
- for (const entry2 of await fs20.readdir(specs)) {
13831
- if (await exists6(path21.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
14173
+ for (const entry2 of await fs21.readdir(specs)) {
14174
+ if (await exists6(path22.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
13832
14175
  }
13833
14176
  }
13834
14177
  const plan = await readField3(name, "plan");
13835
14178
  let baseRef = "";
13836
- if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
13837
- const match = (await fs20.readFile(path21.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
14179
+ if (plan && plan !== "null" && await exists6(path22.resolve(plan))) {
14180
+ const match = (await fs21.readFile(path22.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
13838
14181
  baseRef = match?.[1].trim() ?? "";
13839
14182
  }
13840
14183
  if (!baseRef) baseRef = await readField3(name, "base_ref");
@@ -13914,15 +14257,42 @@ async function selectChange(output, name) {
13914
14257
  validateChangeName4(name);
13915
14258
  try {
13916
14259
  const selection = await selectCurrentChange(process.cwd(), name);
14260
+ const boundBranch = await readField3(name, "bound_branch");
14261
+ const bound = boundBranch && boundBranch !== "null" ? boundBranch : null;
13917
14262
  output.stderr.push(
13918
- green4(
13919
- `[SELECTED] current change: ${selection.change}${selection.branch ? ` (branch: ${selection.branch})` : ""}`
13920
- )
14263
+ green4(`[SELECTED] current change: ${selection.change}${bound ? ` (branch: ${bound})` : ""}`)
13921
14264
  );
13922
14265
  } catch (error) {
13923
14266
  fail2(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
13924
14267
  }
13925
14268
  }
14269
+ async function rebind(output, name) {
14270
+ validateChangeName4(name);
14271
+ const { directory } = await stateFile(name);
14272
+ const boundBranch = await readField3(name, "bound_branch");
14273
+ if (!boundBranch || boundBranch === "null") {
14274
+ fail2(
14275
+ `ERROR: '${name}' is not yet bound; use 'comet state set ${name} isolation <current|branch|worktree>' to establish the first binding`
14276
+ );
14277
+ }
14278
+ const branch = liveGitBranch(process.cwd());
14279
+ if (branch === null) {
14280
+ fail2("ERROR: cannot rebind while HEAD is detached; checkout a branch first");
14281
+ }
14282
+ const before = await readClassicState(directory);
14283
+ if (!before.classic) fail2("ERROR: Classic state projection is missing");
14284
+ await healBoundBranch(directory, branch);
14285
+ const after = { ...before.classic, boundBranch: branch };
14286
+ await appendClassicStateEvent(directory, {
14287
+ change: name,
14288
+ event: "rebind",
14289
+ source: "comet-state",
14290
+ from: before.classic,
14291
+ to: after,
14292
+ effects: [{ field: "boundBranch", from: boundBranch, to: branch }]
14293
+ });
14294
+ output.stderr.push(green4(`[REBIND] bound_branch: ${boundBranch} → ${branch}`));
14295
+ }
13926
14296
  async function currentChange(output) {
13927
14297
  const resolution = await resolveCurrentChange(process.cwd());
13928
14298
  if (resolution.status === "selected") {
@@ -13976,6 +14346,9 @@ var classicStateCommand = async (args) => {
13976
14346
  } else if (subcommand === "task-checkoff") {
13977
14347
  required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
13978
14348
  await taskCheckoff(output, rest[0], rest[1]);
14349
+ } else if (subcommand === "rebind") {
14350
+ requiredExact(rest, 1, "Usage: comet-state.mjs rebind <change-name>");
14351
+ await rebind(output, rest[0]);
13979
14352
  } else if (subcommand === "select") {
13980
14353
  requiredExact(rest, 1, "Usage: comet-state.mjs select <change-name>");
13981
14354
  await selectChange(output, rest[0]);