safeword 0.76.0 → 0.78.0

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 (219) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  2. package/codex-plugin/hooks.json +5 -5
  3. package/codex-plugin/skills/bdd/SKILL.md +6 -0
  4. package/codex-plugin/skills/bdd/references/SCENARIOS.md +2 -2
  5. package/codex-plugin/skills/bdd/references/TDD.md +19 -1
  6. package/codex-plugin/skills/closeout/SKILL.md +11 -3
  7. package/codex-plugin/skills/quality-review/SKILL.md +8 -0
  8. package/codex-plugin/skills/review-spec/SKILL.md +9 -0
  9. package/codex-plugin/skills/tdd-review/SKILL.md +6 -1
  10. package/codex-plugin/skills/verify/SKILL.md +41 -6
  11. package/dist/{architecture-MLLLP2QN.js → architecture-4EIJWLZ2.js} +6 -6
  12. package/dist/{architecture-document-OFBN5ZAO.js → architecture-document-Z3GLUOE3.js} +6 -6
  13. package/dist/{architecture-monorepo-MYENEFSR.js → architecture-monorepo-EIF3E3L3.js} +4 -4
  14. package/dist/{boundary-OWTJP7DH.js → boundary-U5U2PSX2.js} +9 -7
  15. package/dist/boundary-U5U2PSX2.js.map +1 -0
  16. package/dist/{chunk-JVZIT7JQ.js → chunk-34NCOTP5.js} +5 -12
  17. package/dist/chunk-34NCOTP5.js.map +1 -0
  18. package/dist/{chunk-YP2V3YUV.js → chunk-3EJNDIAO.js} +2 -2
  19. package/dist/{chunk-MQIOSUHE.js → chunk-3ETWYWGQ.js} +12 -7
  20. package/dist/{chunk-MQIOSUHE.js.map → chunk-3ETWYWGQ.js.map} +1 -1
  21. package/dist/{chunk-U5BIKVUL.js → chunk-4AIRSIOH.js} +213 -405
  22. package/dist/chunk-4AIRSIOH.js.map +1 -0
  23. package/dist/{chunk-NYTHQ2B5.js → chunk-4CZS4YJV.js} +3 -3
  24. package/dist/{chunk-VN2SBPDX.js → chunk-664YNAMR.js} +42 -47
  25. package/dist/chunk-664YNAMR.js.map +1 -0
  26. package/dist/{chunk-XD26W4MT.js → chunk-7KMQ3QPX.js} +60 -12
  27. package/dist/chunk-7KMQ3QPX.js.map +1 -0
  28. package/dist/{chunk-O2TTGWG6.js → chunk-A7Z27COA.js} +3 -3
  29. package/dist/{chunk-2MBSWVT2.js → chunk-AQU52TJM.js} +5 -5
  30. package/dist/{chunk-XHNWNZRM.js → chunk-AVAUUIDB.js} +2 -1
  31. package/dist/{chunk-XHNWNZRM.js.map → chunk-AVAUUIDB.js.map} +1 -1
  32. package/dist/{chunk-GMHP27HQ.js → chunk-AXAGJLMP.js} +19 -6
  33. package/dist/chunk-AXAGJLMP.js.map +1 -0
  34. package/dist/{chunk-6DKB2H5U.js → chunk-BAYWBZ32.js} +2 -2
  35. package/dist/{chunk-6DKB2H5U.js.map → chunk-BAYWBZ32.js.map} +1 -1
  36. package/dist/chunk-C377VEWH.js +836 -0
  37. package/dist/chunk-C377VEWH.js.map +1 -0
  38. package/dist/{chunk-6SJQRN2A.js → chunk-E4UMSKEX.js} +379 -14
  39. package/dist/chunk-E4UMSKEX.js.map +1 -0
  40. package/dist/{chunk-7ASEPZRH.js → chunk-FASTWBPY.js} +3 -3
  41. package/dist/chunk-FDHJ63FZ.js +241 -0
  42. package/dist/chunk-FDHJ63FZ.js.map +1 -0
  43. package/dist/{chunk-2MI7M2CO.js → chunk-GKR44BCH.js} +2 -2
  44. package/dist/{chunk-PZTGLQSD.js → chunk-I4KBHRZW.js} +4 -4
  45. package/dist/{chunk-QDT6IAYG.js → chunk-JZFZWY3K.js} +2 -2
  46. package/dist/{chunk-LXUBCIMR.js → chunk-KFVEPQ7X.js} +2 -2
  47. package/dist/chunk-ORBC7DKD.js +19 -0
  48. package/dist/chunk-ORBC7DKD.js.map +1 -0
  49. package/dist/{chunk-URVJZ4NL.js → chunk-Q5KLE6ZF.js} +2 -2
  50. package/dist/{chunk-PWBSTTNZ.js → chunk-QUDIOBHP.js} +2 -2
  51. package/dist/{chunk-BPQESZJI.js → chunk-RXQJH4LM.js} +57 -54
  52. package/dist/chunk-RXQJH4LM.js.map +1 -0
  53. package/dist/{chunk-7G6OSNIV.js → chunk-S2T356DA.js} +12 -1
  54. package/dist/chunk-S2T356DA.js.map +1 -0
  55. package/dist/{chunk-PFVH6RET.js → chunk-SLRSGK2C.js} +23 -11
  56. package/dist/{chunk-PFVH6RET.js.map → chunk-SLRSGK2C.js.map} +1 -1
  57. package/dist/{chunk-K64BTVJ7.js → chunk-USPVNZ63.js} +4 -4
  58. package/dist/{chunk-USGSZ4Y4.js → chunk-VD4QUEWP.js} +2 -2
  59. package/dist/{chunk-37ZTQ3NL.js → chunk-VUWQD4AG.js} +12 -13
  60. package/dist/chunk-VUWQD4AG.js.map +1 -0
  61. package/dist/chunk-W32GLIFH.js +384 -0
  62. package/dist/chunk-W32GLIFH.js.map +1 -0
  63. package/dist/{contract-ARHWUCBL.js → chunk-WVDRXD67.js} +2 -3
  64. package/dist/chunk-WVDRXD67.js.map +1 -0
  65. package/dist/{chunk-PQCE5GIH.js → chunk-XROROJLI.js} +96 -7
  66. package/dist/chunk-XROROJLI.js.map +1 -0
  67. package/dist/{cleanup-YLTC244U.js → cleanup-QV2EBF6M.js} +8 -8
  68. package/dist/{cleanup-command-4WXNQCMP.js → cleanup-command-DTE64KTN.js} +16 -21
  69. package/dist/cleanup-command-DTE64KTN.js.map +1 -0
  70. package/dist/cli.js +213 -81
  71. package/dist/cli.js.map +1 -1
  72. package/dist/{clients-54SNTYLV.js → clients-N4UXDPCN.js} +2 -2
  73. package/dist/{codex-bootstrap-3XVVIOJ6.js → codex-bootstrap-7OUYHP4G.js} +12 -11
  74. package/dist/{codex-bootstrap-3XVVIOJ6.js.map → codex-bootstrap-7OUYHP4G.js.map} +1 -1
  75. package/dist/{codex-hook-6NBOO6BO.js → codex-hook-VHGSPJN3.js} +14 -9
  76. package/dist/codex-hook-VHGSPJN3.js.map +1 -0
  77. package/dist/{codify-DYMOC3I6.js → codify-DJELEKJ6.js} +8 -8
  78. package/dist/{commands-DZ3YICLQ.js → commands-4NIKNAST.js} +385 -80
  79. package/dist/commands-4NIKNAST.js.map +1 -0
  80. package/dist/{config-56DVFORI.js → config-5A4LJ62T.js} +2 -2
  81. package/dist/{configured-paths-FKHX74N4.js → configured-paths-ZY2GRPYU.js} +4 -4
  82. package/dist/contract-ZRRRAQWZ.js +8 -0
  83. package/dist/{coordinator-YNT636VE.js → coordinator-ZPEXUBXT.js} +122 -52
  84. package/dist/coordinator-ZPEXUBXT.js.map +1 -0
  85. package/dist/corpus-ZJQNIU4R.js +15 -0
  86. package/dist/{doctor-JK7TUG5R.js → doctor-HUUE7QWE.js} +20 -20
  87. package/dist/{feature-directories-5YGBVQFK.js → feature-directories-7ACGMPS4.js} +6 -6
  88. package/dist/{finalization-YZ3MACEB.js → finalization-5D5N64R7.js} +4 -4
  89. package/dist/{gh-cli-TCMPLTFK.js → gh-cli-46VSKW2O.js} +2 -2
  90. package/dist/{github-rest-ONSGEF2E.js → github-rest-SXLBFCQP.js} +2 -2
  91. package/dist/index.js +1 -1
  92. package/dist/job-EW5LWBHH.js +631 -0
  93. package/dist/job-EW5LWBHH.js.map +1 -0
  94. package/dist/{learning-sync-7E6WZGQ5.js → learning-sync-DRQM2JF6.js} +4 -4
  95. package/dist/{legacy-global-guidance-SQJFZAKL.js → legacy-global-guidance-DWGAGL44.js} +2 -2
  96. package/dist/{lint-gherkin-JE2I4DB7.js → lint-gherkin-5LJX6BBU.js} +31 -10
  97. package/dist/lint-gherkin-5LJX6BBU.js.map +1 -0
  98. package/dist/{operations-3VCNJMVX.js → operations-5CJI6U6T.js} +12 -11
  99. package/dist/{output-IPI7PJMA.js → output-5TOP54O5.js} +2 -2
  100. package/dist/{packet-3XZYADS3.js → packet-IHZNYM3G.js} +2 -2
  101. package/dist/presets/typescript/index.js +1 -1
  102. package/dist/profile-ZZGSSTSJ.js +23 -0
  103. package/dist/{prompt-LUTZ27E4.js → prompt-C3MNQKAT.js} +2 -2
  104. package/dist/remove-PRBJCO6G.js +22 -0
  105. package/dist/{retro-FZH6ERN2.js → retro-Z2DREW77.js} +8 -8
  106. package/dist/{retro-draft-spool-7ZAURNYG.js → retro-draft-spool-YVNO4VSB.js} +2 -2
  107. package/dist/{retro-extract-OMAIIR5F.js → retro-extract-NBSIY4ZY.js} +2 -2
  108. package/dist/{review-pr-MO66DIXC.js → review-pr-TTJKQC4H.js} +2 -2
  109. package/dist/{review-pr-publication-64TR3FDG.js → review-pr-publication-DR75RPF4.js} +2 -2
  110. package/dist/{run-DTVISMLB.js → run-5JWZAFET.js} +4 -4
  111. package/dist/{self-report-LTYM6EWR.js → self-report-3KBA5TG7.js} +2 -2
  112. package/dist/{status-MYY4XKEA.js → status-K62NWMZ6.js} +20 -20
  113. package/dist/status-OFXGT5ZO.js +21 -0
  114. package/dist/{sync-config-3TDU5CY6.js → sync-config-B62CJ5OS.js} +5 -5
  115. package/dist/{sync-tracker-7DFOGVOT.js → sync-tracker-QMPXXLJ4.js} +6 -6
  116. package/dist/{test-execution-6ZJQQLYS.js → test-execution-MCMXLV5O.js} +5 -5
  117. package/dist/{test-plan-R63XUAFZ.js → test-plan-RJTKHEYA.js} +4 -4
  118. package/dist/{ticket-new-M4UL4XP4.js → ticket-new-RPLN56XK.js} +4 -4
  119. package/dist/{ticket-sync-5R7VRCG5.js → ticket-sync-VHTAPISH.js} +5 -5
  120. package/dist/{tracker-map-KFPGE4HB.js → tracker-map-WFRDFSKN.js} +2 -2
  121. package/dist/{tracker-sync-KAVU4N7T.js → tracker-sync-5D4XNQHS.js} +2 -2
  122. package/dist/tracker-sync-5D4XNQHS.js.map +1 -0
  123. package/package.json +3 -2
  124. package/templates/hooks/dependency-bootstrap.ts +31 -0
  125. package/templates/hooks/lib/closeout-binding.ts +140 -1
  126. package/templates/hooks/lib/dependency-readiness.ts +222 -9
  127. package/templates/hooks/lib/safeword-context.ts +27 -13
  128. package/templates/hooks/resolve-verify-ticket.ts +271 -0
  129. package/templates/hooks/session-codex-start.ts +7 -1
  130. package/templates/hooks/session-compact-context.ts +2 -2
  131. package/templates/hooks/session-dependency-readiness.ts +10 -94
  132. package/templates/scripts/closeout-cleanup.ts +430 -71
  133. package/templates/skills/bdd/SCENARIOS.md +2 -2
  134. package/templates/skills/bdd/SKILL.md +6 -0
  135. package/templates/skills/bdd/TDD.md +19 -1
  136. package/templates/skills/closeout/SKILL.md +11 -3
  137. package/templates/skills/quality-review/SKILL.md +8 -0
  138. package/templates/skills/review-spec/SKILL.md +9 -0
  139. package/templates/skills/tdd-review/SKILL.md +6 -1
  140. package/templates/skills/verify/SKILL.md +41 -6
  141. package/dist/boundary-OWTJP7DH.js.map +0 -1
  142. package/dist/chunk-37ZTQ3NL.js.map +0 -1
  143. package/dist/chunk-6SJQRN2A.js.map +0 -1
  144. package/dist/chunk-7G6OSNIV.js.map +0 -1
  145. package/dist/chunk-AMGSSOTT.js +0 -445
  146. package/dist/chunk-AMGSSOTT.js.map +0 -1
  147. package/dist/chunk-BPQESZJI.js.map +0 -1
  148. package/dist/chunk-GMHP27HQ.js.map +0 -1
  149. package/dist/chunk-JVZIT7JQ.js.map +0 -1
  150. package/dist/chunk-PQCE5GIH.js.map +0 -1
  151. package/dist/chunk-U5BIKVUL.js.map +0 -1
  152. package/dist/chunk-UHRE3WGS.js +0 -94
  153. package/dist/chunk-UHRE3WGS.js.map +0 -1
  154. package/dist/chunk-VN2SBPDX.js.map +0 -1
  155. package/dist/chunk-XD26W4MT.js.map +0 -1
  156. package/dist/chunk-ZYFJJH5K.js +0 -52
  157. package/dist/chunk-ZYFJJH5K.js.map +0 -1
  158. package/dist/cleanup-command-4WXNQCMP.js.map +0 -1
  159. package/dist/codex-hook-6NBOO6BO.js.map +0 -1
  160. package/dist/commands-DZ3YICLQ.js.map +0 -1
  161. package/dist/contract-ARHWUCBL.js.map +0 -1
  162. package/dist/coordinator-YNT636VE.js.map +0 -1
  163. package/dist/corpus-32QZRB5Z.js +0 -15
  164. package/dist/lint-gherkin-JE2I4DB7.js.map +0 -1
  165. package/dist/profile-SZMHCSBZ.js +0 -32
  166. package/dist/remove-QRMVWCJJ.js +0 -21
  167. package/dist/status-ECVVBPEH.js +0 -25
  168. /package/dist/{architecture-MLLLP2QN.js.map → architecture-4EIJWLZ2.js.map} +0 -0
  169. /package/dist/{architecture-document-OFBN5ZAO.js.map → architecture-document-Z3GLUOE3.js.map} +0 -0
  170. /package/dist/{architecture-monorepo-MYENEFSR.js.map → architecture-monorepo-EIF3E3L3.js.map} +0 -0
  171. /package/dist/{chunk-YP2V3YUV.js.map → chunk-3EJNDIAO.js.map} +0 -0
  172. /package/dist/{chunk-NYTHQ2B5.js.map → chunk-4CZS4YJV.js.map} +0 -0
  173. /package/dist/{chunk-O2TTGWG6.js.map → chunk-A7Z27COA.js.map} +0 -0
  174. /package/dist/{chunk-2MBSWVT2.js.map → chunk-AQU52TJM.js.map} +0 -0
  175. /package/dist/{chunk-7ASEPZRH.js.map → chunk-FASTWBPY.js.map} +0 -0
  176. /package/dist/{chunk-2MI7M2CO.js.map → chunk-GKR44BCH.js.map} +0 -0
  177. /package/dist/{chunk-PZTGLQSD.js.map → chunk-I4KBHRZW.js.map} +0 -0
  178. /package/dist/{chunk-QDT6IAYG.js.map → chunk-JZFZWY3K.js.map} +0 -0
  179. /package/dist/{chunk-LXUBCIMR.js.map → chunk-KFVEPQ7X.js.map} +0 -0
  180. /package/dist/{chunk-URVJZ4NL.js.map → chunk-Q5KLE6ZF.js.map} +0 -0
  181. /package/dist/{chunk-PWBSTTNZ.js.map → chunk-QUDIOBHP.js.map} +0 -0
  182. /package/dist/{chunk-K64BTVJ7.js.map → chunk-USPVNZ63.js.map} +0 -0
  183. /package/dist/{chunk-USGSZ4Y4.js.map → chunk-VD4QUEWP.js.map} +0 -0
  184. /package/dist/{cleanup-YLTC244U.js.map → cleanup-QV2EBF6M.js.map} +0 -0
  185. /package/dist/{clients-54SNTYLV.js.map → clients-N4UXDPCN.js.map} +0 -0
  186. /package/dist/{codify-DYMOC3I6.js.map → codify-DJELEKJ6.js.map} +0 -0
  187. /package/dist/{config-56DVFORI.js.map → config-5A4LJ62T.js.map} +0 -0
  188. /package/dist/{configured-paths-FKHX74N4.js.map → configured-paths-ZY2GRPYU.js.map} +0 -0
  189. /package/dist/{corpus-32QZRB5Z.js.map → contract-ZRRRAQWZ.js.map} +0 -0
  190. /package/dist/{finalization-YZ3MACEB.js.map → corpus-ZJQNIU4R.js.map} +0 -0
  191. /package/dist/{doctor-JK7TUG5R.js.map → doctor-HUUE7QWE.js.map} +0 -0
  192. /package/dist/{feature-directories-5YGBVQFK.js.map → feature-directories-7ACGMPS4.js.map} +0 -0
  193. /package/dist/{gh-cli-TCMPLTFK.js.map → finalization-5D5N64R7.js.map} +0 -0
  194. /package/dist/{legacy-global-guidance-SQJFZAKL.js.map → gh-cli-46VSKW2O.js.map} +0 -0
  195. /package/dist/{github-rest-ONSGEF2E.js.map → github-rest-SXLBFCQP.js.map} +0 -0
  196. /package/dist/{learning-sync-7E6WZGQ5.js.map → learning-sync-DRQM2JF6.js.map} +0 -0
  197. /package/dist/{operations-3VCNJMVX.js.map → legacy-global-guidance-DWGAGL44.js.map} +0 -0
  198. /package/dist/{output-IPI7PJMA.js.map → operations-5CJI6U6T.js.map} +0 -0
  199. /package/dist/{packet-3XZYADS3.js.map → output-5TOP54O5.js.map} +0 -0
  200. /package/dist/{profile-SZMHCSBZ.js.map → packet-IHZNYM3G.js.map} +0 -0
  201. /package/dist/{prompt-LUTZ27E4.js.map → profile-ZZGSSTSJ.js.map} +0 -0
  202. /package/dist/{remove-QRMVWCJJ.js.map → prompt-C3MNQKAT.js.map} +0 -0
  203. /package/dist/{retro-draft-spool-7ZAURNYG.js.map → remove-PRBJCO6G.js.map} +0 -0
  204. /package/dist/{retro-FZH6ERN2.js.map → retro-Z2DREW77.js.map} +0 -0
  205. /package/dist/{retro-extract-OMAIIR5F.js.map → retro-draft-spool-YVNO4VSB.js.map} +0 -0
  206. /package/dist/{self-report-LTYM6EWR.js.map → retro-extract-NBSIY4ZY.js.map} +0 -0
  207. /package/dist/{review-pr-MO66DIXC.js.map → review-pr-TTJKQC4H.js.map} +0 -0
  208. /package/dist/{review-pr-publication-64TR3FDG.js.map → review-pr-publication-DR75RPF4.js.map} +0 -0
  209. /package/dist/{run-DTVISMLB.js.map → run-5JWZAFET.js.map} +0 -0
  210. /package/dist/{status-ECVVBPEH.js.map → self-report-3KBA5TG7.js.map} +0 -0
  211. /package/dist/{status-MYY4XKEA.js.map → status-K62NWMZ6.js.map} +0 -0
  212. /package/dist/{sync-config-3TDU5CY6.js.map → status-OFXGT5ZO.js.map} +0 -0
  213. /package/dist/{ticket-sync-5R7VRCG5.js.map → sync-config-B62CJ5OS.js.map} +0 -0
  214. /package/dist/{sync-tracker-7DFOGVOT.js.map → sync-tracker-QMPXXLJ4.js.map} +0 -0
  215. /package/dist/{test-execution-6ZJQQLYS.js.map → test-execution-MCMXLV5O.js.map} +0 -0
  216. /package/dist/{test-plan-R63XUAFZ.js.map → test-plan-RJTKHEYA.js.map} +0 -0
  217. /package/dist/{ticket-new-M4UL4XP4.js.map → ticket-new-RPLN56XK.js.map} +0 -0
  218. /package/dist/{tracker-map-KFPGE4HB.js.map → ticket-sync-VHTAPISH.js.map} +0 -0
  219. /package/dist/{tracker-sync-KAVU4N7T.js.map → tracker-map-WFRDFSKN.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safeword",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "description": "Safeword workflow guidance and gates for Codex.",
5
5
  "author": {
6
6
  "name": "Arcade AI"
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "bunx --bun safeword@0.76.0 hook codex session-start --plugin-hook",
9
+ "command": "bunx --bun safeword@0.78.0 hook codex session-start --plugin-hook",
10
10
  "timeout": 120,
11
11
  "statusMessage": "Loading Safeword standing instructions"
12
12
  }
@@ -19,7 +19,7 @@
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
22
- "command": "bunx --bun safeword@0.76.0 hook codex pre-tool-use --plugin-hook",
22
+ "command": "bunx --bun safeword@0.78.0 hook codex pre-tool-use --plugin-hook",
23
23
  "timeout": 30,
24
24
  "statusMessage": "Checking Safeword edit gates"
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "hooks": [
33
33
  {
34
34
  "type": "command",
35
- "command": "bunx --bun safeword@0.76.0 hook codex post-tool-use --plugin-hook",
35
+ "command": "bunx --bun safeword@0.78.0 hook codex post-tool-use --plugin-hook",
36
36
  "timeout": 30,
37
37
  "statusMessage": "Surfacing Safeword post-tool context"
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "hooks": [
46
46
  {
47
47
  "type": "command",
48
- "command": "bunx --bun safeword@0.76.0 hook codex user-prompt-submit --plugin-hook",
48
+ "command": "bunx --bun safeword@0.78.0 hook codex user-prompt-submit --plugin-hook",
49
49
  "timeout": 30,
50
50
  "statusMessage": "Checking queued Safeword prompt context"
51
51
  }
@@ -58,7 +58,7 @@
58
58
  "hooks": [
59
59
  {
60
60
  "type": "command",
61
- "command": "bunx --bun safeword@0.76.0 hook codex stop --plugin-hook",
61
+ "command": "bunx --bun safeword@0.78.0 hook codex stop --plugin-hook",
62
62
  "timeout": 600,
63
63
  "statusMessage": "Checking Safeword stop continuation"
64
64
  }
@@ -70,6 +70,12 @@ bun .safeword/hooks/write-review-stamp.ts --author-agent "author-agent" --review
70
70
 
71
71
  If the reviewer finds blocking issues, fix them and re-review — don't stamp.
72
72
 
73
+ All BDD review exits share one lifecycle rule: `REVIEW_PENDING` is a live
74
+ review, not a verdict. Keep its `review_id`, collect it through the returned
75
+ typed `nextActions` until terminal, and never start a replacement review or
76
+ advance/stamp while it is pending. `REVIEW_STALE` means rerun against the
77
+ current artifacts. Only a terminal verdict may advance the phase.
78
+
73
79
  The plan-implementation exit applies the same discipline to the implementation plan (see references/PLAN_IMPLEMENTATION.md's exit). Other phase exits don't need an independent review by default — they carry their
74
80
  own guards (intake's user sub-phase gates, implement's tests, the done-gate's
75
81
  evidence checks). When the **review gate** is enabled (`reviewGate` in
@@ -198,7 +198,7 @@ delivery retries on exponential backoff`). IDs are 1-indexed per job and
198
198
 
199
199
  **Entry:** Agent enters `scenario-gate` phase.
200
200
 
201
- Run the **`$safeword:review-spec`** skill — it is the gate procedure (vacuous-pass, AODI, determinism risks, adversarial pass + negative-case, cross-cutting checks, and the findings format). It reads the active ticket's `.feature` source when present, using `test-definitions.md` only as the R/G/R ledger, reports findings, and is re-invokable standalone after scenario edits. Apply its findings, then complete the plain-language completeness check and exit below.
201
+ Run the **`$safeword:review-spec`** skill — it is the gate procedure (vacuous-pass, AODI, determinism risks, adversarial pass + negative-case, cross-cutting checks, and the findings format). It reads the active ticket's `.feature` source when present, using `test-definitions.md` only as the R/G/R ledger, reports findings, and is re-invokable standalone after scenario edits. Its final reconciliation maps material dimensions, affected surfaces, and declared public outcomes to scenarios or explicit deferrals, then challenges whether the planned proof exercises the boundary each load-bearing scenario claims. Apply its findings, then complete the plain-language completeness check and exit below.
202
202
 
203
203
  ### Are the reviewed scenarios complete?
204
204
 
@@ -207,7 +207,7 @@ Ask the user: **Do these scenarios now fully cover the intended behavior and imp
207
207
  ### Scenario Gate Exit
208
208
 
209
209
  1. Each scenario passes the vacuous-pass test and AODI (Atomic, Observable, Deterministic, Independent)
210
- 2. Adversarial pass + cross-cutting checks complete; findings presented in the findings format (or confirmed clean)
210
+ 2. Adversarial pass + cross-cutting checks complete, including coverage reconciliation and the proof-claim challenge; findings presented in the findings format (or confirmed clean)
211
211
  3. **Check for one build-only kill-risk.** Run this checkpoint only here, after
212
212
  items 1–2 pass — never during intake, define-behavior, or while scenario
213
213
  validation is incomplete. While items 1–2 are incomplete, remain in
@@ -41,6 +41,19 @@ same actor-facing entry point and actor-visible result that the `When` and
41
41
  - **Observe the `Then` as the actor-visible result.** Store, editor, or component
42
42
  state is supporting evidence; it does not prove a visible UI result, emitted
43
43
  CLI output, or external API response.
44
+ - **Assert the result's material values.** A proxy property is not enough: when a
45
+ `Then` names an actor or typed result, assert those fields in the emitted
46
+ output. Assert attempt order only when that order is part of the promised
47
+ behavior.
48
+ - **Prove the whole scenario in its primary test.** Material `Given`, `When`,
49
+ and `Then` clauses must hold together at the claimed boundary; evidence
50
+ scattered across separate tests does not prove their interaction.
51
+ - **Preserve timing and cardinality.** If the scenario promises behavior within
52
+ the same invocation, attempt, or session, a retry or second call is not
53
+ equivalent proof.
54
+ - **Prove every material outline row.** A `Scenario Outline` is complete only
55
+ when each behaviorally distinct example traverses the claimed boundary;
56
+ otherwise narrow the scenario or add the missing proof.
44
57
  - **Keep evidence limits explicit.** When the real boundary cannot be automated
45
58
  reliably, use the existing `@manual` or `@live` path and perform and record
46
59
  that check separately. A tag, skip reason, or narrower automated test does
@@ -120,7 +133,12 @@ At the bottom of `test-definitions.md`, add one row for the whole-ticket cross-s
120
133
  - Modify test files in a REFACTOR commit — blocked at commit-time (test changes during cleanup are behavior changes in disguise)
121
134
  - Add extra checkboxes like `- [ ] REVIEW` — only RED/GREEN/REFACTOR
122
135
 
123
- **Evidence before claims:** Show test output, don't just claim "tests pass". Run the targeted suite at GREEN for fast feedback; run the FULL suite once at scenario close (after REFACTOR) to catch cross-module regressions.
136
+ **Evidence before claims:** Show terminal test output, don't just claim "tests
137
+ pass." A launched, queued, lock-waiting, timed-out, or still-running command is
138
+ not GREEN. Mark GREEN only after the relevant command reaches a terminal result
139
+ that reports the intended test passing. Run the targeted suite at GREEN for
140
+ fast feedback; run the FULL suite once at scenario close (after REFACTOR) to
141
+ catch cross-module regressions.
124
142
 
125
143
  ### Red flags — stop and rethink:
126
144
 
@@ -89,6 +89,12 @@ successful result whose `data.agent_filing_needed` is `false` and whose derived
89
89
  current session has an empty filing spool. Zero substantial findings and every
90
90
  finding successfully filed are both complete outcomes.
91
91
 
92
+ Each successful run seals the last complete JSONL record from one immutable
93
+ transcript read. If preview reporting appends more complete records, apply
94
+ validates the sealed byte prefix and runs retro only over the bounded appended
95
+ window before advancing the receipt. A partial trailing record is neither
96
+ sealed nor lost; mutation or truncation of the sealed prefix fails closed.
97
+
92
98
  Failed extraction, failed filing, pending drafts, malformed output, or an
93
99
  identity mismatch means no cleanup. Report every failure and its recovery
94
100
  action. A request to skip retro does not create a bypass: preserve the worktree
@@ -113,9 +119,11 @@ or mints one from terminal green hosted CI. Only when neither proof is available
113
119
  does it run the project's verification, build, typecheck, and BDD plans. It does not rerun
114
120
  dependency audit: that changing intelligence is enforced at the
115
121
  delivery-time, pre-merge boundary and cannot repair an immutable merged head.
116
- It reuses the exact verification and retrospective snapshots through matching
117
- preview and apply invocations. New transcript content or changed repository
118
- state makes the plan stale instead of silently expanding its evidence window.
122
+ It reuses the exact verification snapshot and sealed retrospective evidence
123
+ through matching preview and apply invocations. Append-only transcript progress
124
+ advances through the bounded retrospective window without changing the cleanup
125
+ authorization digest. Changed repository state, cleanup targets, or any mutation
126
+ of the sealed transcript prefix still makes the plan stale.
119
127
  After the topic worktree is gone, preview requires its fresh clean-head receipt.
120
128
  It binds the resulting repository state and exact PR identity to `PLAN_DIGEST`.
121
129
  Report the complete operation list and all blockers. Do not apply a blocked plan.
@@ -178,6 +178,14 @@ Each pass:
178
178
  bun .safeword/hooks/run-review.ts review run quality-review changed-file [more-changed-files...] --agent-handoff --json
179
179
  ```
180
180
 
181
+ A healthy deep review may return `REVIEW_PENDING` after its foreground
182
+ courtesy wait. That is a handoff, not a failed route: keep the returned
183
+ `review_id`, continue other useful work, then run the typed `nextActions`
184
+ status command until it reaches a terminal result. Never start a second
185
+ review for the same sources merely because the first is still running.
186
+ Apply the normal verdict rules only to the collected terminal result; if it
187
+ is `REVIEW_STALE`, rerun against the current sources.
188
+
181
189
  Claude-authored work prefers headless Codex; Codex-authored work prefers
182
190
  headless Claude. The coordinator uses a neutral snapshot, checks reviewer
183
191
  provenance, preserves the exact preferred-route failure, and records any
@@ -111,6 +111,15 @@ Eight lenses across the whole scenario set (not per scenario) — each asks "wha
111
111
  - **Invariant binding** — for each normative clause in `spec.md` (never / must not / always / only), name the scenario whose failure would falsify it **and** the condition under which it fails; a bare scenario reference is not a binding, it's a pointer that survives the invariant being violated. An invariant no scenario would catch is a **must-fix** — cheapest to write now, while no code exists to work around. Worse than a gap is the scenario whose title names the invariant while its `Given` establishes a weaker precondition: it reads as coverage and proves nothing, so report it as a vacuous pass, not a missing scenario. Found live in QRX2DN — the spec forbade an unbound session mutating ticket state, every row named `never_uses_a_fallback_for` bound a session id, and the no-identity case the invariant actually named shipped as a defect (#1425).
112
112
  - **Wiring** — for each behavior that crosses a module/command boundary, is there a scenario exercised end-to-end through the real entry point (real config → real collaborators, mocking only the process boundary), not only via injected internals? A path reachable solely through a `provider: none`-style short circuit has no wiring coverage (see `testing/SKILL.md` → Wiring Tests).
113
113
 
114
+ Finish by reconciling the set instead of adding speculative cases: every
115
+ material partition retained in `dimensions.md`, affected surface, and public
116
+ command or user-visible outcome declared in ticket scope needs a scenario or an
117
+ explicit `skip: <reason>`. For each load-bearing scenario ask: _could the
118
+ proposed test pass while the user-facing claim is still broken?_ Same-process
119
+ proof cannot establish caller-exit survival; an injected fake cannot establish
120
+ real CLI wiring; a unit test cannot establish a runtime or protocol boundary.
121
+ Report a proof-boundary mismatch now so the implementation plan can correct it.
122
+
114
123
  ## Findings format
115
124
 
116
125
  Report findings the way safeword talks to the user — lead with the answer, structure only because a multi-finding review earns it, end with the call:
@@ -27,7 +27,7 @@ Key the review off the **last checked** box in the current scenario — that's t
27
27
 
28
28
  Depth scales with the step: lightweight after RED, moderate after GREEN, full after REFACTOR.
29
29
 
30
- ## After RED — review the test
30
+ ## After RED — adversarially review the test
31
31
 
32
32
  Focused review (~1 minute). Check the test that was just written:
33
33
 
@@ -42,6 +42,11 @@ Focused review (~1 minute). Check the test that was just written:
42
42
  - **Fails for the right reason — confirmed by the run, not the eye?** Execute the test now and read the actual failure: it must report the _missing behavior_, not a syntax, import, or setup error. This is the one bullet you don't judge by reading — the run is the evidence.
43
43
  - **Right test type?** Load the testing skill and consult its scope hierarchy (E2E > Integration > Unit). Was a higher-scope test practical here? Did we drop to unit when integration would catch more?
44
44
  - **Coverage adequacy?** Consult testing guide's bug detection matrix. Ask: "What could still break that this test wouldn't catch?" Flag gaps — missing edge cases, error paths, or boundary values. **Where a gap goes:** a missing scenario is a scope change, not a mid-implement edit — defer it to a follow-up ticket, or loop back to define-behavior, re-run the scenario-gate, and re-enter plan-implementation (update impl-plan.md for the new scenario) before implement. Don't silently append scenarios to a signed-off `test-definitions.md`.
45
+ - **Proof matches the claim?** Could this test pass while the user-facing claim
46
+ remains broken? Challenge the actual test boundary, not its title: a
47
+ same-process test cannot prove caller-exit survival, an injected fake cannot
48
+ prove real CLI wiring, and a unit test cannot prove a runtime or protocol
49
+ boundary.
45
50
 
46
51
  **Vacuity guard (the external check).** A test that would pass without the feature proves nothing. The RED→GREEN transition is the proof it's wired to the behavior: it must be failing _now_, and the minimal implementation is what turns it green. If a test ever passes _before_ its implementation exists, it's vacuous — fix the test, not the code.
47
52
 
@@ -41,16 +41,26 @@ For task, patch, or no-ticket work, this proof isn't required — note it's miss
41
41
 
42
42
  ### 1. Find Current Ticket (if any)
43
43
 
44
+ Use the installed resolver. It reconciles this runtime's session binding with
45
+ the current PR or worktree's Git changes and fails closed when those signals
46
+ conflict. It never scans the global `in_progress` backlog. A session-bound
47
+ ticket remains relevant after its status changes during closeout; a changed
48
+ `done` ticket remains eligible.
49
+
44
50
  ```bash
45
- # Find in_progress tickets, excluding epics
46
51
  PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null || pwd)}"
47
- NS_ROOT="$(bun "$PROJECT_DIR/.safeword/hooks/resolve-namespace-root.ts" "$PROJECT_DIR")"
48
- for f in "$NS_ROOT"/tickets/*/ticket.md; do
49
- [ -f "$f" ] || continue
50
- grep -q "^status: in_progress" "$f" && ! grep -q "^type: epic" "$f" && echo "$f"
51
- done | head -1
52
+ bun "$PROJECT_DIR/.safeword/hooks/resolve-verify-ticket.ts" "$PROJECT_DIR"
52
53
  ```
53
54
 
55
+ If Safeword's injected context names a ticket but the host exposes no runtime
56
+ identity to the helper, rerun with `--ticket <id>`. Multiple changed tickets
57
+ fail closed with the same instruction. No candidate means continue without an
58
+ active ticket.
59
+
60
+ Git cannot infer current work from commits made directly on an up-to-date
61
+ default branch because there is no distinct merge-base range. In that case,
62
+ pass `--ticket <id>` when ticket context is required.
63
+
54
64
  If a ticket is found, read it to get:
55
65
 
56
66
  - `parent:` field (if any)
@@ -147,9 +157,21 @@ else
147
157
  exit 1
148
158
  fi
149
159
 
160
+ # >>> verification_lanes (behavior covered by verify-skill.test.ts)
161
+ # Run every lane for complete evidence, but preserve the first non-zero status
162
+ # so a later successful lane cannot turn the aggregate invocation green.
163
+ verification_status=0
164
+ record_verification_status() {
165
+ if [ "$lane_status" -ne 0 ] && [ "$verification_status" -eq 0 ]; then
166
+ verification_status="$lane_status"
167
+ fi
168
+ }
169
+
150
170
  # --- Test suite (resolved by safeword project test-plan — one source of truth) ---
151
171
  plan_kind=verify
152
172
  run_plan
173
+ lane_status=$?
174
+ record_verification_status
153
175
 
154
176
  # --- Gherkin acceptance lane (resolved by safeword project test-plan --kind bdd:
155
177
  # cucumber-js, behave, … — godog/cucumber-rs fold into the Go/Rust test lanes).
@@ -159,15 +181,21 @@ bdd_plan="$(run_safeword project test-plan --kind bdd --format sh)"
159
181
  rc=$?
160
182
  if [ "$rc" -ne 0 ]; then
161
183
  echo "❌ Evidence generation failed: safeword project test-plan --kind bdd exited $rc (red, not a passed check)" >&2
184
+ lane_status=$rc
185
+ record_verification_status
162
186
  elif [ -z "$bdd_plan" ]; then
163
187
  echo "Gherkin acceptance lane: ⏭️ Skipped — no acceptance lane detected"
164
188
  else
165
189
  bash -c "$bdd_plan"
190
+ lane_status=$?
191
+ record_verification_status
166
192
  fi
167
193
 
168
194
  # --- Build check (resolved by safeword project test-plan) ---
169
195
  plan_kind=build
170
196
  run_plan
197
+ lane_status=$?
198
+ record_verification_status
171
199
 
172
200
  # --- Typecheck: static type-check where the stack has one — `tsc --noEmit` for
173
201
  # TypeScript (the same signal CI's lint job runs, #436), mypy/pyright for
@@ -179,12 +207,19 @@ run_plan
179
207
  # it isn't a gap. ---
180
208
  plan_kind=typecheck
181
209
  run_plan
210
+ lane_status=$?
211
+ record_verification_status
182
212
 
183
213
  # --- Supply-chain: JavaScript's package-manager audit, Python's `uv audit` or
184
214
  # `pip-audit`, Go's pinned `govulncheck`, and Rust's cargo-deny advisories.
185
215
  # A missing scanner prints a visible skip, never a false green. ---
186
216
  plan_kind=deps
187
217
  run_plan
218
+ lane_status=$?
219
+ record_verification_status
220
+
221
+ exit "$verification_status"
222
+ # <<< verification_lanes
188
223
  ```
189
224
 
190
225
  The `$safeword:lint` command handles linting with auto-fix. Report any remaining unfixable errors. Aggregate every attempted stack test into the final `**Test Suite:**` status, and every attempted stack build into the final `**Build:**` status. **Typecheck is part of the gate, not optional:** when the ticket changed TypeScript, a passing targeted-test run is not "ready" until `test-plan --kind typecheck` (or `$safeword:lint`, which runs `tsc --noEmit`) is green — CI's lint job runs it and will go red otherwise. A skipped or empty test-plan is not a failure when the project lacks a matching automated check; it is an explicit evidence gap to mention when the ticket touched that stack.
@@ -9,23 +9,23 @@ import {
9
9
  planSelfHealProject,
10
10
  selfHealProject,
11
11
  selfHealProjectPreservingProse
12
- } from "./chunk-PZTGLQSD.js";
12
+ } from "./chunk-I4KBHRZW.js";
13
13
  import {
14
14
  discoverUnreadableWorkspaces,
15
15
  extractMonorepoArchitectureSnapshot
16
- } from "./chunk-YP2V3YUV.js";
16
+ } from "./chunk-3EJNDIAO.js";
17
17
  import {
18
18
  GENERATED_ARCHITECTURE_FILENAME,
19
19
  isArchitectureDocumentEnforcementEnabled,
20
20
  readConfiguredPath,
21
21
  resolveGeneratedArchitecturePath,
22
22
  toRepoDirectory
23
- } from "./chunk-2MI7M2CO.js";
23
+ } from "./chunk-GKR44BCH.js";
24
24
  import {
25
25
  readJson,
26
26
  writeJson
27
- } from "./chunk-7G6OSNIV.js";
28
- import "./chunk-6DKB2H5U.js";
27
+ } from "./chunk-S2T356DA.js";
28
+ import "./chunk-BAYWBZ32.js";
29
29
 
30
30
  // src/commands/architecture.ts
31
31
  import { execFileSync } from "child_process";
@@ -650,4 +650,4 @@ export {
650
650
  architectureStage,
651
651
  architectureStaged
652
652
  };
653
- //# sourceMappingURL=architecture-MLLLP2QN.js.map
653
+ //# sourceMappingURL=architecture-4EIJWLZ2.js.map
@@ -7,11 +7,11 @@ import {
7
7
  selfHeal,
8
8
  selfHealProject,
9
9
  selfHealProjectPreservingProse
10
- } from "./chunk-PZTGLQSD.js";
11
- import "./chunk-YP2V3YUV.js";
12
- import "./chunk-2MI7M2CO.js";
13
- import "./chunk-7G6OSNIV.js";
14
- import "./chunk-6DKB2H5U.js";
10
+ } from "./chunk-I4KBHRZW.js";
11
+ import "./chunk-3EJNDIAO.js";
12
+ import "./chunk-GKR44BCH.js";
13
+ import "./chunk-S2T356DA.js";
14
+ import "./chunk-BAYWBZ32.js";
15
15
  export {
16
16
  isSafewordOwned,
17
17
  isWouldChangeAction,
@@ -22,4 +22,4 @@ export {
22
22
  selfHealProject,
23
23
  selfHealProjectPreservingProse
24
24
  };
25
- //# sourceMappingURL=architecture-document-OFBN5ZAO.js.map
25
+ //# sourceMappingURL=architecture-document-Z3GLUOE3.js.map
@@ -3,13 +3,13 @@ import {
3
3
  discoverWorkspaces,
4
4
  extractMonorepoArchitectureSnapshot,
5
5
  monorepoFingerprintOf
6
- } from "./chunk-YP2V3YUV.js";
7
- import "./chunk-7G6OSNIV.js";
8
- import "./chunk-6DKB2H5U.js";
6
+ } from "./chunk-3EJNDIAO.js";
7
+ import "./chunk-S2T356DA.js";
8
+ import "./chunk-BAYWBZ32.js";
9
9
  export {
10
10
  discoverUnreadableWorkspaces,
11
11
  discoverWorkspaces,
12
12
  extractMonorepoArchitectureSnapshot,
13
13
  monorepoFingerprintOf
14
14
  };
15
- //# sourceMappingURL=architecture-monorepo-MYENEFSR.js.map
15
+ //# sourceMappingURL=architecture-monorepo-EIF3E3L3.js.map
@@ -7,25 +7,25 @@ import {
7
7
  } from "./chunk-3D25MSWD.js";
8
8
  import {
9
9
  createPhaseAnchorScope
10
- } from "./chunk-K64BTVJ7.js";
10
+ } from "./chunk-USPVNZ63.js";
11
11
  import {
12
12
  classifyAnnotation,
13
13
  isValidSha,
14
14
  isValidSkipReason,
15
15
  parseCheckboxAnnotation
16
16
  } from "./chunk-Z4ESH6KU.js";
17
- import "./chunk-URVJZ4NL.js";
17
+ import "./chunk-Q5KLE6ZF.js";
18
18
  import {
19
19
  warn
20
20
  } from "./chunk-QSLVO4XF.js";
21
21
  import {
22
22
  readConfiguredPathFromContent,
23
23
  ticketDirectoriesForConfiguredRoot
24
- } from "./chunk-2MI7M2CO.js";
24
+ } from "./chunk-GKR44BCH.js";
25
25
  import {
26
26
  readFileSafe
27
- } from "./chunk-7G6OSNIV.js";
28
- import "./chunk-6DKB2H5U.js";
27
+ } from "./chunk-S2T356DA.js";
28
+ import "./chunk-BAYWBZ32.js";
29
29
 
30
30
  // src/commands/boundary.ts
31
31
  import { execFileSync as execFileSync2 } from "child_process";
@@ -112,6 +112,7 @@ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
112
112
  import nodePath5 from "path";
113
113
 
114
114
  // templates/hooks/lib/dependency-readiness.ts
115
+ import { spawnSync } from "child_process";
115
116
  import { createHash } from "crypto";
116
117
  import {
117
118
  existsSync as existsSync2,
@@ -119,6 +120,7 @@ import {
119
120
  readdirSync,
120
121
  readFileSync as readFileSync2,
121
122
  realpathSync,
123
+ rmSync,
122
124
  statSync as statSync2,
123
125
  writeFileSync
124
126
  } from "fs";
@@ -132,7 +134,7 @@ import nodePath from "path";
132
134
  import nodePath2 from "path";
133
135
 
134
136
  // templates/hooks/lib/test-runner.ts
135
- import { execSync, spawnSync } from "child_process";
137
+ import { execSync, spawnSync as spawnSync2 } from "child_process";
136
138
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
137
139
  import nodePath4 from "path";
138
140
  var BDD_TEST_TIMEOUT_MS = 5 * 6e4;
@@ -628,4 +630,4 @@ function boundary(options) {
628
630
  export {
629
631
  boundary
630
632
  };
631
- //# sourceMappingURL=boundary-OWTJP7DH.js.map
633
+ //# sourceMappingURL=boundary-U5U2PSX2.js.map