safeword 0.71.0 → 0.72.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 (187) 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/audit/SKILL.md +55 -13
  4. package/codex-plugin/skills/bdd/SKILL.md +13 -10
  5. package/codex-plugin/skills/bdd/references/DISCOVERY.md +22 -7
  6. package/codex-plugin/skills/bdd/references/PLAN_IMPLEMENTATION.md +12 -2
  7. package/codex-plugin/skills/bdd/references/TDD.md +6 -6
  8. package/codex-plugin/skills/closeout/SKILL.md +124 -0
  9. package/codex-plugin/skills/quality-review/SKILL.md +65 -11
  10. package/codex-plugin/skills/retro-filer/SKILL.md +9 -4
  11. package/codex-plugin/skills/review-spec/SKILL.md +28 -3
  12. package/codex-plugin/skills/self-review/SKILL.md +9 -3
  13. package/codex-plugin/skills/verify/SKILL.md +8 -0
  14. package/dist/{architecture-XECMMTJO.js → architecture-GOUJM5NE.js} +3 -3
  15. package/dist/{architecture-document-WW2KDC7G.js → architecture-document-KFR4JDUT.js} +3 -3
  16. package/dist/{boundary-2VG6JWGW.js → boundary-J2RW4N3E.js} +10 -8
  17. package/dist/{boundary-2VG6JWGW.js.map → boundary-J2RW4N3E.js.map} +1 -1
  18. package/dist/chunk-2MQTOSGG.js +312 -0
  19. package/dist/chunk-2MQTOSGG.js.map +1 -0
  20. package/dist/{chunk-ABCSHC5I.js → chunk-5KUTV6L5.js} +16 -2
  21. package/dist/chunk-5KUTV6L5.js.map +1 -0
  22. package/dist/{chunk-2EHR2PVO.js → chunk-7NZ26QJP.js} +4 -4
  23. package/dist/{chunk-VXFLYFZ6.js → chunk-AF6L253C.js} +2 -2
  24. package/dist/{chunk-Z6LFUJ6B.js → chunk-F4CA33GP.js} +229 -157
  25. package/dist/chunk-F4CA33GP.js.map +1 -0
  26. package/dist/{chunk-YEMSUOB2.js → chunk-I5QEPYVK.js} +107 -73
  27. package/dist/chunk-I5QEPYVK.js.map +1 -0
  28. package/dist/{chunk-DTDUW7QI.js → chunk-KWBGQ2YZ.js} +6 -6
  29. package/dist/chunk-KWBGQ2YZ.js.map +1 -0
  30. package/dist/{chunk-7MZST3KV.js → chunk-OSBX5BZS.js} +40 -16
  31. package/dist/chunk-OSBX5BZS.js.map +1 -0
  32. package/dist/{chunk-HU7KMEIQ.js → chunk-OWGNCT45.js} +2 -2
  33. package/dist/{chunk-QQI3PT2V.js → chunk-PAC2VLLK.js} +21 -35
  34. package/dist/chunk-PAC2VLLK.js.map +1 -0
  35. package/dist/chunk-PSKZASFR.js +36 -0
  36. package/dist/chunk-PSKZASFR.js.map +1 -0
  37. package/dist/{chunk-QVI4TWIU.js → chunk-QPWX5JXI.js} +2 -2
  38. package/dist/{chunk-PZHHGMVB.js → chunk-RFP2MSVO.js} +1 -1
  39. package/dist/chunk-RFP2MSVO.js.map +1 -0
  40. package/dist/{chunk-WHNYCYU6.js → chunk-RLH5PLSD.js} +2 -2
  41. package/dist/{chunk-PKWJP43G.js → chunk-UM7PJKBV.js} +2 -2
  42. package/dist/chunk-Y6LUZKTN.js +144 -0
  43. package/dist/chunk-Y6LUZKTN.js.map +1 -0
  44. package/dist/{chunk-IRKICK2K.js → chunk-ZFM3XTN3.js} +3 -3
  45. package/dist/{chunk-WZDLZULJ.js → chunk-ZQCKTQKJ.js} +2 -2
  46. package/dist/cleanup-STJVSD7S.js +294 -0
  47. package/dist/cleanup-STJVSD7S.js.map +1 -0
  48. package/dist/cli.js +99 -29
  49. package/dist/cli.js.map +1 -1
  50. package/dist/{codex-hook-EOWMR47H.js → codex-hook-HBEPMPEK.js} +5 -5
  51. package/dist/{codify-ZAEKSB2M.js → codify-L2V7J2GJ.js} +4 -4
  52. package/dist/{configured-paths-CSRPNDRS.js → configured-paths-TXRFWRTV.js} +2 -2
  53. package/dist/contract-BJD62H2W.js +13 -0
  54. package/dist/contract-BJD62H2W.js.map +1 -0
  55. package/dist/{converge-setup-UMMCYSH2.js → converge-setup-6WS2RMSG.js} +37 -26
  56. package/dist/converge-setup-6WS2RMSG.js.map +1 -0
  57. package/dist/coordinator-P7SET5EM.js +1024 -0
  58. package/dist/coordinator-P7SET5EM.js.map +1 -0
  59. package/dist/{corpus-AFBBTLYZ.js → corpus-XW3XX3F5.js} +4 -4
  60. package/dist/{feature-directories-FUAV6QSQ.js → feature-directories-36DL4FAU.js} +3 -3
  61. package/dist/{learning-sync-OPK7A6IA.js → learning-sync-6ESNEZTX.js} +2 -2
  62. package/dist/{legacy-global-guidance-S6YM7CLF.js → legacy-global-guidance-JPJBHFMW.js} +4 -4
  63. package/dist/{lint-gherkin-MK3ZICLF.js → lint-gherkin-UHMNF5SD.js} +4 -4
  64. package/dist/{migrate-codex-plugin-QAPK7USM.js → migrate-codex-plugin-CPLYYCGP.js} +8 -8
  65. package/dist/{plan-DNSAGZEC.js → plan-RRN2ESJ7.js} +9 -9
  66. package/dist/profile-DM4UNHMN.js +17 -0
  67. package/dist/{remove-NFOKH6VW.js → remove-GNAT5APA.js} +9 -9
  68. package/dist/{retro-T5NS6SAH.js → retro-XO7CALV4.js} +17 -7
  69. package/dist/retro-XO7CALV4.js.map +1 -0
  70. package/dist/{retro-draft-spool-XAD5F76M.js → retro-draft-spool-OU4YMZQO.js} +6 -2
  71. package/dist/{run-IUKI4FQ3.js → run-HQCZ67WE.js} +2 -2
  72. package/dist/status-5DDTQIJU.js +16 -0
  73. package/dist/status-5DDTQIJU.js.map +1 -0
  74. package/dist/{status-7E3ZIJJL.js → status-ILKQIXSD.js} +14 -13
  75. package/dist/{status-7E3ZIJJL.js.map → status-ILKQIXSD.js.map} +1 -1
  76. package/dist/{sync-tracker-G37ZNGZU.js → sync-tracker-EMEVBKQS.js} +4 -4
  77. package/dist/{test-plan-VGGQW5VP.js → test-plan-QAQT6OXT.js} +2 -2
  78. package/dist/{ticket-new-GDGYKYDR.js → ticket-new-Y2QIVBHL.js} +3 -3
  79. package/dist/{ticket-sync-YCOPASYY.js → ticket-sync-5XRY6Q2Z.js} +3 -3
  80. package/dist/ticket-sync-5XRY6Q2Z.js.map +1 -0
  81. package/package.json +3 -1
  82. package/templates/SAFEWORD.md +11 -1
  83. package/templates/agents/safeword-retro-filer.md +11 -6
  84. package/templates/commands/audit.md +1 -1
  85. package/templates/commands/bdd.md +1 -1
  86. package/templates/commands/closeout.md +5 -0
  87. package/templates/commands/debug.md +1 -1
  88. package/templates/commands/quality-review.md +1 -1
  89. package/templates/commands/refactor.md +1 -1
  90. package/templates/commands/retro.md +1 -1
  91. package/templates/commands/review-spec.md +1 -1
  92. package/templates/commands/self-review.md +4 -0
  93. package/templates/commands/spike.md +1 -1
  94. package/templates/commands/testing.md +1 -1
  95. package/templates/commands/verify.md +1 -1
  96. package/templates/cursor/rules/bdd-core.mdc +1 -1
  97. package/templates/cursor/rules/bdd-discovery.mdc +1 -1
  98. package/templates/cursor/rules/bdd-done.mdc +1 -1
  99. package/templates/cursor/rules/bdd-plan-implementation.mdc +1 -1
  100. package/templates/cursor/rules/bdd-scenarios.mdc +1 -1
  101. package/templates/cursor/rules/bdd-splitting.mdc +1 -1
  102. package/templates/cursor/rules/bdd-tdd.mdc +1 -1
  103. package/templates/cursor/rules/bdd-verify.mdc +1 -1
  104. package/templates/cursor/rules/safeword-brainstorming.mdc +1 -1
  105. package/templates/cursor/rules/safeword-debugging.mdc +1 -1
  106. package/templates/cursor/rules/safeword-elicitation.mdc +1 -1
  107. package/templates/cursor/rules/safeword-figure-it-out.mdc +1 -1
  108. package/templates/cursor/rules/safeword-quality-reviewing.mdc +1 -1
  109. package/templates/cursor/rules/safeword-refactoring.mdc +1 -1
  110. package/templates/cursor/rules/safeword-retro-filer.mdc +1 -1
  111. package/templates/cursor/rules/safeword-tdd-review.mdc +1 -1
  112. package/templates/cursor/rules/safeword-testing.mdc +1 -1
  113. package/templates/cursor/rules/safeword-ticket-system.mdc +1 -1
  114. package/templates/doc-templates/impl-plan-template.md +14 -4
  115. package/templates/guides/self-report-filing.md +15 -5
  116. package/templates/hooks/audit-principle-trace.ts +49 -0
  117. package/templates/hooks/codex/pre-tool-quality.ts +9 -0
  118. package/templates/hooks/cursor/before-shell-execution.ts +19 -10
  119. package/templates/hooks/lib/closeout-binding.ts +145 -0
  120. package/templates/hooks/lib/drain-retro-spool.ts +28 -0
  121. package/templates/hooks/lib/impl-plan.ts +22 -2
  122. package/templates/hooks/lib/lint.ts +2 -0
  123. package/templates/hooks/lib/namespace-root.ts +13 -0
  124. package/templates/hooks/lib/principle-trace.ts +173 -0
  125. package/templates/hooks/lib/project-knowledge.ts +39 -0
  126. package/templates/hooks/lib/retro-draft-spool.ts +57 -17
  127. package/templates/hooks/lib/retro-nudge.ts +3 -2
  128. package/templates/hooks/lib/review-ledger.ts +70 -11
  129. package/templates/hooks/post-tool-sync-learnings.ts +7 -3
  130. package/templates/hooks/pre-tool-architecture-stage.ts +7 -3
  131. package/templates/hooks/pre-tool-quality.ts +29 -7
  132. package/templates/hooks/resolve-namespace-root.ts +9 -3
  133. package/templates/hooks/resolve-project-knowledge.ts +10 -0
  134. package/templates/hooks/session-architecture-heal.ts +7 -3
  135. package/templates/hooks/session-auto-upgrade.ts +4 -0
  136. package/templates/hooks/stop-quality.ts +5 -4
  137. package/templates/hooks/stop-retro.ts +2 -0
  138. package/templates/hooks/write-review-stamp.ts +43 -5
  139. package/templates/principles-template.md +49 -0
  140. package/templates/scripts/closeout-cleanup.ts +843 -0
  141. package/templates/skills/audit/SKILL.md +55 -13
  142. package/templates/skills/bdd/DISCOVERY.md +22 -7
  143. package/templates/skills/bdd/PLAN_IMPLEMENTATION.md +12 -2
  144. package/templates/skills/bdd/SKILL.md +13 -10
  145. package/templates/skills/bdd/TDD.md +6 -6
  146. package/templates/skills/closeout/SKILL.md +125 -0
  147. package/templates/skills/quality-review/SKILL.md +65 -11
  148. package/templates/skills/retro-filer/SKILL.md +9 -4
  149. package/templates/skills/review-spec/SKILL.md +28 -3
  150. package/templates/skills/self-review/SKILL.md +9 -3
  151. package/templates/skills/verify/SKILL.md +8 -0
  152. package/dist/chunk-7MZST3KV.js.map +0 -1
  153. package/dist/chunk-ABCSHC5I.js.map +0 -1
  154. package/dist/chunk-DTDUW7QI.js.map +0 -1
  155. package/dist/chunk-PZHHGMVB.js.map +0 -1
  156. package/dist/chunk-QQI3PT2V.js.map +0 -1
  157. package/dist/chunk-YEMSUOB2.js.map +0 -1
  158. package/dist/chunk-Z6LFUJ6B.js.map +0 -1
  159. package/dist/converge-setup-UMMCYSH2.js.map +0 -1
  160. package/dist/retro-T5NS6SAH.js.map +0 -1
  161. /package/dist/{architecture-XECMMTJO.js.map → architecture-GOUJM5NE.js.map} +0 -0
  162. /package/dist/{architecture-document-WW2KDC7G.js.map → architecture-document-KFR4JDUT.js.map} +0 -0
  163. /package/dist/{chunk-2EHR2PVO.js.map → chunk-7NZ26QJP.js.map} +0 -0
  164. /package/dist/{chunk-VXFLYFZ6.js.map → chunk-AF6L253C.js.map} +0 -0
  165. /package/dist/{chunk-HU7KMEIQ.js.map → chunk-OWGNCT45.js.map} +0 -0
  166. /package/dist/{chunk-QVI4TWIU.js.map → chunk-QPWX5JXI.js.map} +0 -0
  167. /package/dist/{chunk-WHNYCYU6.js.map → chunk-RLH5PLSD.js.map} +0 -0
  168. /package/dist/{chunk-PKWJP43G.js.map → chunk-UM7PJKBV.js.map} +0 -0
  169. /package/dist/{chunk-IRKICK2K.js.map → chunk-ZFM3XTN3.js.map} +0 -0
  170. /package/dist/{chunk-WZDLZULJ.js.map → chunk-ZQCKTQKJ.js.map} +0 -0
  171. /package/dist/{codex-hook-EOWMR47H.js.map → codex-hook-HBEPMPEK.js.map} +0 -0
  172. /package/dist/{codify-ZAEKSB2M.js.map → codify-L2V7J2GJ.js.map} +0 -0
  173. /package/dist/{configured-paths-CSRPNDRS.js.map → configured-paths-TXRFWRTV.js.map} +0 -0
  174. /package/dist/{corpus-AFBBTLYZ.js.map → corpus-XW3XX3F5.js.map} +0 -0
  175. /package/dist/{feature-directories-FUAV6QSQ.js.map → feature-directories-36DL4FAU.js.map} +0 -0
  176. /package/dist/{learning-sync-OPK7A6IA.js.map → learning-sync-6ESNEZTX.js.map} +0 -0
  177. /package/dist/{legacy-global-guidance-S6YM7CLF.js.map → legacy-global-guidance-JPJBHFMW.js.map} +0 -0
  178. /package/dist/{lint-gherkin-MK3ZICLF.js.map → lint-gherkin-UHMNF5SD.js.map} +0 -0
  179. /package/dist/{migrate-codex-plugin-QAPK7USM.js.map → migrate-codex-plugin-CPLYYCGP.js.map} +0 -0
  180. /package/dist/{plan-DNSAGZEC.js.map → plan-RRN2ESJ7.js.map} +0 -0
  181. /package/dist/{retro-draft-spool-XAD5F76M.js.map → profile-DM4UNHMN.js.map} +0 -0
  182. /package/dist/{remove-NFOKH6VW.js.map → remove-GNAT5APA.js.map} +0 -0
  183. /package/dist/{ticket-sync-YCOPASYY.js.map → retro-draft-spool-OU4YMZQO.js.map} +0 -0
  184. /package/dist/{run-IUKI4FQ3.js.map → run-HQCZ67WE.js.map} +0 -0
  185. /package/dist/{sync-tracker-G37ZNGZU.js.map → sync-tracker-EMEVBKQS.js.map} +0 -0
  186. /package/dist/{test-plan-VGGQW5VP.js.map → test-plan-QAQT6OXT.js.map} +0 -0
  187. /package/dist/{ticket-new-GDGYKYDR.js.map → ticket-new-Y2QIVBHL.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safeword",
3
- "version": "0.71.0",
3
+ "version": "0.72.0",
4
4
  "description": "Safe Word 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.71.0 hook codex session-start --plugin-hook",
9
+ "command": "bunx --bun safeword@0.72.0 hook codex session-start --plugin-hook",
10
10
  "timeout": 120,
11
11
  "statusMessage": "Loading Safe Word standing instructions"
12
12
  }
@@ -19,7 +19,7 @@
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
22
- "command": "bunx --bun safeword@0.71.0 hook codex pre-tool-use --plugin-hook",
22
+ "command": "bunx --bun safeword@0.72.0 hook codex pre-tool-use --plugin-hook",
23
23
  "timeout": 30,
24
24
  "statusMessage": "Checking Safe Word edit gates"
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "hooks": [
33
33
  {
34
34
  "type": "command",
35
- "command": "bunx --bun safeword@0.71.0 hook codex post-tool-use --plugin-hook",
35
+ "command": "bunx --bun safeword@0.72.0 hook codex post-tool-use --plugin-hook",
36
36
  "timeout": 30,
37
37
  "statusMessage": "Surfacing Safe Word post-tool context"
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "hooks": [
46
46
  {
47
47
  "type": "command",
48
- "command": "bunx --bun safeword@0.71.0 hook codex user-prompt-submit --plugin-hook",
48
+ "command": "bunx --bun safeword@0.72.0 hook codex user-prompt-submit --plugin-hook",
49
49
  "timeout": 30,
50
50
  "statusMessage": "Checking queued Safe Word prompt context"
51
51
  }
@@ -58,7 +58,7 @@
58
58
  "hooks": [
59
59
  {
60
60
  "type": "command",
61
- "command": "bunx --bun safeword@0.71.0 hook codex stop --plugin-hook",
61
+ "command": "bunx --bun safeword@0.72.0 hook codex stop --plugin-hook",
62
62
  "timeout": 600,
63
63
  "statusMessage": "Checking Safe Word stop continuation"
64
64
  }
@@ -670,7 +670,35 @@ Test Quality:
670
670
 
671
671
  Review the changed area from the printed scope. For each significantly changed area, check if related docs, readmes, or guides need updating. Flag stale, missing, or contradictory impacted documentation as errors. Documentation drift is never a warning; date-only staleness with no changed-code contradiction is repository-audit context, not a diff finding.
672
672
 
673
- ### 6. Namespace Domain Docs
673
+ ### 6. Principle Trace Integrity
674
+
675
+ For the active ticket, when `impl-plan.md` declares project-principle alignment,
676
+ resolve the source using `paths.principles` (default
677
+ `<namespace-root>/principles.md`) and check the principle trace as **observable
678
+ facts only**:
679
+
680
+ - The named principle exists in the configured source.
681
+ - The trace contains a non-empty concrete consequence and proof.
682
+ - The proof reference resolves to recorded test, verification, or manual
683
+ evidence; an intentional conflict is named in Known deviations.
684
+
685
+ Report a missing source entry, incomplete mapping, dead evidence reference, or
686
+ unrecorded conflict as `[E010] Broken principle trace`. Do not judge whether a
687
+ principle was applicable, whether the consequence was a wise interpretation,
688
+ or whether an experience was genuinely delightful—those are adversarial
689
+ `quality-review` judgments. A plan with no declared applicable principle is not
690
+ an audit finding.
691
+
692
+ Run the factual checker below verbatim. Its sentinel keeps the executable audit
693
+ contract testable without turning semantic review into shell heuristics.
694
+
695
+ ```bash
696
+ # principle-trace-check — E010 objective trace integrity only.
697
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null || pwd)}"
698
+ bun "$PROJECT_DIR/.safeword/hooks/audit-principle-trace.ts" "$PROJECT_DIR"
699
+ ```
700
+
701
+ ### 7. Namespace Domain Docs
674
702
 
675
703
  When a changed feature/spec or changed domain doc references them, reconcile the
676
704
  three namespace domain docs — `personas.md`, `surfaces.md`, `glossary.md` —
@@ -687,20 +715,29 @@ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null
687
715
  source "$PROJECT_DIR/.safeword/hooks/lib/audit-scope.sh"
688
716
  audit_scope_initialize "$PROJECT_DIR"
689
717
 
690
- # A branch audit skips unrelated domain-doc corpus drift. Deleted and
691
- # type-changed feature/spec/domain files remain in reference review scope, so
692
- # they still trigger this check for broken-reference review. A repository audit
693
- # deliberately checks every domain doc and every discovered feature/spec.
694
- if [ "$AUDIT_SCOPE_MODE" = "diff" ] && ! audit_scope_has_review_path_matching '(^|/)(personas|surfaces|glossary)\.md$|\.feature$|(^|/)spec\.md$'; then
695
- exit 0
696
- fi
697
-
698
718
  # Resolve the namespace root (honors config paths.projectRoot in real runs).
699
719
  # Fall back on directory existence — robust when the resolver hook is absent.
700
720
  NS_ROOT="$(bun "$PROJECT_DIR/.safeword/hooks/resolve-namespace-root.ts" "$PROJECT_DIR" 2> /dev/null)"
701
721
  [ -d "$NS_ROOT" ] || {
702
722
  if [ -d "$PROJECT_DIR/.project" ]; then NS_ROOT="$PROJECT_DIR/.project"; else NS_ROOT="$PROJECT_DIR/.safeword-project"; fi
703
723
  }
724
+ PERSONAS_FILE="$(bun "$PROJECT_DIR/.safeword/hooks/resolve-namespace-root.ts" "$PROJECT_DIR" personas personas.md 2> /dev/null)"
725
+ SURFACES_FILE="$(bun "$PROJECT_DIR/.safeword/hooks/resolve-namespace-root.ts" "$PROJECT_DIR" surfaces surfaces.md 2> /dev/null)"
726
+ GLOSSARY_FILE="$(bun "$PROJECT_DIR/.safeword/hooks/resolve-namespace-root.ts" "$PROJECT_DIR" glossary glossary.md 2> /dev/null)"
727
+ [ -n "$PERSONAS_FILE" ] || PERSONAS_FILE="$NS_ROOT/personas.md"
728
+ [ -n "$SURFACES_FILE" ] || SURFACES_FILE="$NS_ROOT/surfaces.md"
729
+ [ -n "$GLOSSARY_FILE" ] || GLOSSARY_FILE="$NS_ROOT/glossary.md"
730
+
731
+ # A branch audit skips unrelated domain-doc corpus drift. Include configured
732
+ # domain paths, whose basenames need not be personas.md/surfaces.md/glossary.md.
733
+ if [ "$AUDIT_SCOPE_MODE" = "diff" ] && ! audit_scope_has_review_path_matching '(^|/)(personas|surfaces|glossary)\.md$|\.feature$|(^|/)spec\.md$'; then
734
+ domain_path_changed=false
735
+ for configured_domain_file in "$PERSONAS_FILE" "$SURFACES_FILE" "$GLOSSARY_FILE"; do
736
+ configured_domain_path="${configured_domain_file#"$PROJECT_DIR"/}"
737
+ audit_scope_path_changed "$configured_domain_path" && domain_path_changed=true
738
+ done
739
+ [ "$domain_path_changed" = true ] || exit 0
740
+ fi
704
741
 
705
742
  # Single-source the HTML-comment strip used by every check below. Strips
706
743
  # same-line comments FIRST (`s/<!--.*-->//g`) then deletes multi-line comment
@@ -724,7 +761,11 @@ domain_docs_entry_count() {
724
761
  # In a diff audit, report only a changed domain doc. An unchanged empty scaffold
725
762
  # is existing debt, not a finding caused by an unrelated feature or spec change.
726
763
  for doc in personas surfaces glossary; do
727
- dd_file="$NS_ROOT/$doc.md"
764
+ case "$doc" in
765
+ personas) dd_file="$PERSONAS_FILE" ;;
766
+ surfaces) dd_file="$SURFACES_FILE" ;;
767
+ glossary) dd_file="$GLOSSARY_FILE" ;;
768
+ esac
728
769
  [ -f "$dd_file" ] || continue
729
770
  domain_doc_path="${dd_file#"$PROJECT_DIR"/}"
730
771
  [ "$AUDIT_SCOPE_MODE" = "repository" ] || audit_scope_path_changed "$domain_doc_path" || continue
@@ -749,7 +790,7 @@ feature_directories() {
749
790
  return 127
750
791
  fi
751
792
  }
752
- surfaces_file="$NS_ROOT/surfaces.md"
793
+ surfaces_file="$SURFACES_FILE"
753
794
  dd_file="$surfaces_file"
754
795
  surfaces_path="${surfaces_file#"$PROJECT_DIR"/}"
755
796
  if [ "$AUDIT_SCOPE_MODE" = "repository" ] || audit_scope_path_changed "$surfaces_path"; then
@@ -785,7 +826,7 @@ fi
785
826
  # --- Persona drift (E009): spec **Persona:** code referenced but undefined ---
786
827
  # Spec lines only, comment-stripped (feature lineage tags carry ticket-ids, not
787
828
  # personas). Suppressed when personas.md is empty/absent.
788
- personas_file="$NS_ROOT/personas.md"
829
+ personas_file="$PERSONAS_FILE"
789
830
  tickets_dir="$NS_ROOT/tickets"
790
831
  dd_file="$personas_file"
791
832
  personas_path="${personas_file#"$PROJECT_DIR"/}"
@@ -923,7 +964,7 @@ fi
923
964
 
924
965
  **Empty-doc offer (W008):** report the empty doc and point the user to its template — do **not** draft entries or write the file during the audit pass (read-only). Filling it is a follow-up the user approves.
925
966
 
926
- **Coverage limitation:** the block reads the default namespace-root locations; per-file `paths.personas` / `paths.surfaces` / `paths.glossary` overrides are validated by `safeword doctor` (structure), not here. If the safeword feature-directory resolver is unavailable, W009 says E008 fell back to root `features/` only. Persona drift reads spec `**Persona:**` lines only — feature lineage tags are not a reliable persona source.
967
+ **Coverage limitation:** configured `paths.personas`, `paths.surfaces`, and `paths.glossary` are resolved before reconciliation. `safeword doctor` separately reports missing configured files and orphaned defaults. If the safeword feature-directory resolver is unavailable, W009 says E008 fell back to root `features/` only. Persona drift reads spec `**Persona:**` lines only — feature lineage tags are not a reliable persona source.
927
968
 
928
969
  ---
929
970
 
@@ -941,6 +982,7 @@ Report findings by severity with codes:
941
982
  - [E007] Drifted layer→dir: `ARCHITECTURE.md` maps `domain` → `src/core/` but no such module path is in `architecture.generated.md`
942
983
  - [E008] Surface drift: `@surface.safeword-cli` is referenced in `features/` but has no matching entry in `surfaces.md`
943
984
  - [E009] Persona drift: persona code `DEV` is named in a spec `**Persona:**` line but has no matching entry in `personas.md`
985
+ - [E010] Broken principle trace: `Delight the user` points to `verify.md#persona-walkthrough`, but that evidence record does not exist
944
986
 
945
987
  ### Warnings (should review)
946
988
 
@@ -48,18 +48,21 @@ phase: implement # intake | define-behavior | scenario-gate | plan-implementatio
48
48
 
49
49
  The **scenario-gate exit requires** an independent review of the scenarios — not
50
50
  your own pass. (Your own inline pass is Tier 1: `$safeword:self-review`, per asset, as you
51
- author.) Run it as a _fresh reviewer with no conversation history_ so the author
52
- can't grade their own work: a forked subagent — a skill with `context: fork`, or
53
- an explicit subagent — handed only the phase's artifacts and the ticket's scope,
54
- applying the `$safeword:review-spec` procedure; **its** verdict decides. When
55
- `crossModelReview` is on, that reviewer must be a **different model than the
56
- author** — a same-model reviewer shares the author's blind spots. Prefer one of
57
- comparable-or-better capability; never weaker. If you can't run a different
58
- model, log a deliberate skip (`--skip "<reason>"`) rather than stamping a
59
- same-model review. On a pass, record the stamp:
51
+ author.) Invoke the shared host-owned coordinator with only the phase artifacts
52
+ and ticket scope; its typed verdict decides:
60
53
 
61
54
  ```bash
62
- bun .safeword/hooks/write-review-stamp.ts --phase <phase you are leaving>
55
+ safeword review run scenario-gate feature-file ticket-spec [legacy-test-definitions]
56
+ ```
57
+
58
+ The coordinator prefers the opposite headless agent, labels a permitted
59
+ same-agent fallback as degraded, and blocks with one recovery action when no
60
+ safe route remains. Do not bypass it with a private subagent. On a result that
61
+ satisfies the configured policy, record the returned provenance in the stamp
62
+ (substitute the four values from `data` in the coordinator result):
63
+
64
+ ```bash
65
+ bun .safeword/hooks/write-review-stamp.ts --author-agent "author-agent" --reviewer-agent "actual-reviewer" --independence "independence" --phase phase-name
63
66
  ```
64
67
 
65
68
  If the reviewer finds blocking issues, fix them and re-review — don't stamp.
@@ -4,18 +4,18 @@
4
4
 
5
5
  ## Sub-phase gates
6
6
 
7
- Intake advances through sub-phases (load personas/glossary/surfaces → JTBD → Rules → engineering scope). Each one ends with a **gate** — don't advance on your own momentum; present what you captured and get the user's signoff first. Three moves:
7
+ Intake advances through sub-phases (load principles/personas/glossary/surfaces → JTBD → Rules → engineering scope). Each one ends with a **gate** — don't advance on your own momentum; present what you captured and get the user's signoff first. Three moves:
8
8
 
9
9
  1. **Present** the captured artifact verbatim — the JTBD list, the Rules list grouped by JTBD, or the Scope / Out of Scope / Done When block.
10
10
  2. **Ask** the sub-phase's closing question (below).
11
11
  3. **Wait** for confirmation. Any forward-moving reply advances — an explicit "looks good" / "proceed", or an amendment you fold in and re-present. A new concern loops back; you don't advance until it's resolved.
12
12
 
13
- | Sub-phase | Closing question |
14
- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | Personas / glossary / surfaces | _"`<file>` is empty — add entries now, or proceed without?"_ (only when missing/empty) |
16
- | Jobs To Be Done | _"Here's who asked, the cost of not doing it, and how reversible it is — plus the jobs it serves. Given that, is this a feature, or a task? And do the jobs cover who this serves and why?"_ |
17
- | Rules | _"Does each job's criteria capture what 'done' means for the persona? Any to split, add, or drop?"_ |
18
- | Engineering scope | _"Here's the scope / out-of-scope / done-when — ready to proceed?"_ |
13
+ | Sub-phase | Closing question |
14
+ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | Principles / personas / glossary / surfaces | _"`<file>` is empty — add entries now, or proceed without?"_ (only when missing/empty) |
16
+ | Jobs To Be Done | _"Here's who asked, the cost of not doing it, and how reversible it is — plus the jobs it serves. Given that, is this a feature, or a task? And do the jobs cover who this serves and why?"_ |
17
+ | Rules | _"Does each job's criteria capture what 'done' means for the persona? Any to split, add, or drop?"_ |
18
+ | Engineering scope | _"Here's the scope / out-of-scope / done-when — ready to proceed?"_ |
19
19
 
20
20
  **On resume** (picked up mid-sub-phase across sessions): re-present the captured artifact for re-confirmation rather than assuming the prior signoff still stands — context may have shifted.
21
21
 
@@ -23,6 +23,21 @@ Intake advances through sub-phases (load personas/glossary/surfaces → JTBD →
23
23
 
24
24
  These gates are conversational discipline the agent runs — not a hook block. (Hook-enforced sub-phase tracking is future work, coordinated with phase-step-enforcement epic 172.)
25
25
 
26
+ ## Load project principles
27
+
28
+ At intake start, read the configured principles file (`paths.principles`,
29
+ default `<namespace-root>/principles.md`). This is the project's source of truth
30
+ for durable decision policy. Do not copy the catalogue into the ticket and do
31
+ not force every principle to apply.
32
+
33
+ - **Missing or empty:** ask whether to add principles now or proceed without.
34
+ - **Applicable:** carry the principle forward only when it changes a Rule,
35
+ design choice, proof, or deliberate deviation.
36
+ - **Experiential:** translate it into observable Rules and, when appropriate, a
37
+ Rave Moment; tests prove the mechanics, not the emotion itself.
38
+ - **Technical:** keep product Rules technology-neutral and carry the principle
39
+ into plan-implementation, where alternatives and evidence belong.
40
+
26
41
  ## Load project personas
27
42
 
28
43
  At intake start, read the configured personas file (`paths.personas`, default `<namespace-root>/personas.md`). This file is the project's source of truth for who features serve; later phases (JTBD authoring, criteria validation, scenario numbering) reference its entries.
@@ -21,6 +21,16 @@ worktree. Never reuse the spike's experimental code or commits.
21
21
  2. **Then survey what exists** — after sketching the ideal, read the generated architecture state doc (`architecture.generated.md` — the machine-owned _what-is_) and the decision record (resolved from `paths.architecture`) for **reuse** candidates: components that already do the job, or do it better. Order matters: surveying first anchors the design to the status quo.
22
22
  3. **Reconcile without sunk-cost conformance.** Existing architecture is changeable with a recorded decision, not a constraint to conform to. Reuse what's better; change what's worse — deliberately, with the change recorded (ADR lifecycle below).
23
23
 
24
+ ## Apply project principles
25
+
26
+ Re-read the configured principles file (`paths.principles`, default
27
+ `<namespace-root>/principles.md`) so planning does not depend on intake context
28
+ surviving. Identify only the **applicable project principles**—do not enumerate
29
+ the catalogue as a checklist. For each applicable principle, record in Design
30
+ alignment: **principle → concrete consequence → proof**. Put an intentional
31
+ conflict in Known deviations with its reason. No applicable principle is a
32
+ valid `skip:`; vague “complies with principles” prose is not.
33
+
24
34
  ## Environment fluency
25
35
 
26
36
  - **Map installed language skills and component skills to the scenarios** — for the languages the feature touches, check the installed skill packs (`.claude/skills/<lang>-*`) and note per-scenario which apply. Scope to the feature's touched code and surfaces: in a polyglot monorepo, surface only what's relevant, never the full inventory.
@@ -36,7 +46,7 @@ Scaffold from `.safeword/templates/impl-plan-template.md` (sibling to `ticket.md
36
46
 
37
47
  - **Approach** — open with the riskiest assumption and the cheapest scenario that proves it; then the proof plan: for each scenario the primary proof (`unit`, `integration`, `E2E`, or `eval` per `testing/SKILL.md`'s highest practical scope rule), supporting proofs, at least one wiring test per new entry point, and the build order with the load-bearing slice first. Cover each **affected surface** the spec lists — name the proof that covers it or a per-surface `skip: <reason>`.
38
48
  - **Decisions** — one row per significant technical choice: choice, alternatives, rejected-because, with the `$safeword:figure-it-out` evidence cited.
39
- - **Arch alignment** — consult the architecture record (resolve `paths.architecture` in `.safeword/config.json`; default `.project/architecture.md`; a directory holds one ADR per `.md`, README excluded) **before** filling this in. Records exist: list the decisions this design honors. None recorded yet: write `skip: no ADRs in this project yet` and offer to draft the first ADR (technology choices spanning features, data ownership, cross-service contracts).
49
+ - **Design alignment** — record applicable project principles with their concrete consequence and proof, then consult the architecture record (resolve `paths.architecture` in `.safeword/config.json`; default `.project/architecture.md`; a directory holds one ADR per `.md`, README excluded). Records exist: list the decisions this design honors. With applicable principles but no records, write `None recorded yet` for the architecture sub-entry and offer to draft the first ADR for a significant decision. With neither applicable principles nor architecture records, write `skip: no applicable principles or ADRs` and offer to draft the first ADR for a significant decision (technology choices spanning features, data ownership, cross-service contracts).
40
50
  - **Known deviations** — where this deviates from guidance and why that's acceptable.
41
51
  - **Doc impact** — which configured `docs.sources` surfaces the customer-visible changes touch, folded into the build order as tasks; internal-only: `skip: <reason>`.
42
52
  - **Assessment triggers** — what would prompt revisiting these choices.
@@ -57,7 +67,7 @@ Scaffold from `.safeword/templates/impl-plan-template.md` (sibling to `ticket.md
57
67
 
58
68
  ## Exit: review, then (optionally) the user
59
69
 
60
- 1. **Independent review first.** Spawn a fresh reviewer with no conversation history handed only `impl-plan.md`, the ticket scope, and the `.feature` source to refute the plan (wrong-direction design, missed scenarios, editorial padding via the deletion test). Fix findings, re-review, then stamp the exit (`write-review-stamp.ts --phase plan-implementation`, where the review gate is enabled). Human handoff happens **only after** this review passes — raw planning output is never presented for approval. Exception, any time: information only the user has (intent, priorities, constraints not in code or docs) routes to the user the moment the gap appears — `$safeword:elicit`.
70
+ 1. **Independent review first.** At review time, run `bun .safeword/hooks/resolve-project-knowledge.ts`, then run `safeword review run plan-implementation impl-plan.md spec.md ticket.md feature-file principles-file personas-file surfaces-file` with the current files identified by the resolver. The shared coordinator sends that bounded packet to the opposite headless agent when available; its typed verdict, failure classification, and independence level are authoritative, so do not substitute a private subagent. Give the reviewer the current `spec.md`, configured principles file, configured personas file, and configured surfaces file in that packet. The reviewer refutes the plan: challenge whether it selected the actually applicable principles, whether each concrete consequence follows from its principle, whether the proposed proof can prove that consequence, whether conflicts belong in Known deviations, whether the design fulfills each persona's JTBD, and whether any affected surface was omitted or lacks credible proof. Also check wrong-direction design, missed scenarios, and editorial padding via the deletion test. Fix findings, re-resolve the sources, re-review, then stamp the exit with the returned agent provenance (`write-review-stamp.ts --author-agent "author-agent" --reviewer-agent "actual-reviewer" --independence "independence" --phase plan-implementation`, where the review gate is enabled). Add `--model` only when the executed reviewer reports a verifiable model identifier; the coordinator never invents one. Human handoff happens **only after** this review passes — raw planning output is never presented for approval. Exception, any time: information only the user has (intent, priorities, constraints not in code or docs) routes to the user the moment the gap appears — `$safeword:elicit`.
61
71
  2. **`designApprovalGate`** (in `.safeword/config.json`): **absent or off** — the reviewed plan advances autonomously; do not ask. **Enabled** — present the reviewed plan (riskiest assumption, build order, decisions) and wait for user approval before `implement`.
62
72
  3. **Sessions without an interactive user** (cloud/headless — Claude Code on the Web, Codex Cloud, Cursor Cloud Agents): an enabled approval gate must not stall the container. Record the auto-decision as pending approval in the ticket work log and surface the reviewed plan in the session's reviewable output (PR description / session summary) — approval lands at PR review. Note: Cursor Cloud Agents run `preToolUse` hooks but not stop hooks, so enforcement rides the transition gate there, not stop-time nudges.
63
73
  4. **Update frontmatter:** `phase: implement`. The pre-tool transition gate verifies `impl-plan.md` parses valid with status `planned` — a missing or invalid plan blocks the move with the fix named. A `phase_skips` justification satisfies phase provenance only — a new-flow feature (spec.md present) still needs the valid plan to enter implement.
@@ -153,7 +153,7 @@ Then reconcile the plan.
153
153
  All scenarios complete → reconcile `impl-plan.md` against what actually shipped, **before** advancing to verify (the stop hook blocks `verify`/`done` while the plan still says `planned`):
154
154
 
155
155
  1. **Walk the Decisions table** — for each row ask "did we actually do this, or did we change our mind?" Update changed rows: new choice, new rationale, the abandoned choice moves into Alternatives considered.
156
- 2. **Walk Arch alignment** — for each claim ask "did the implementation honor this?" Move anything that deviated into **Known deviations** with the reason.
156
+ 2. **Walk Design alignment** — for applicable project principles and architecture claims, ask "did the implementation honor each stated consequence, and does its proof pass?" Move anything that deviated into **Known deviations** with the reason.
157
157
  3. **Refresh Assessment triggers** — add triggers the implementation surfaced (e.g., "works at current scale, degrades past 10x").
158
158
  4. **Flip the status line** to `**Status:** implemented`. The phase hook stamps the transition with real time (Claude Code — on other harnesses add a short transition entry yourself); log the reconciliation outcome ({N} decisions updated, {M} deviations recorded) as a narrative work-log entry.
159
159
 
@@ -169,20 +169,20 @@ surfaces a real spec, scope, value, or risk decision.
169
169
  Off by default. When `.safeword/config.json` sets `architectureReviewGate: true`, the stop hook blocks `verify`/`done` for a new-flow feature until its `impl-plan.md` design has been **independently reviewed** — the same propose-then-challenge discipline the scenario-gate applies to scenarios, now applied to the design. Two requirements:
170
170
 
171
171
  1. **Cited evidence.** The Decisions section must carry a citation — a URL or a `[n]` source-reference marker — proving the choice was weighed against real evidence (the `$safeword:figure-it-out` trace), or an auditable `skip: <reason>`.
172
- 2. **A fresh-context review.** Spawn a reviewer with **no conversation history**, handed only `impl-plan.md` and the ticket scope, to try to refute the design against its cited sources. On a pass, stamp it:
172
+ 2. **A fresh-context review.** Run `safeword review run plan-implementation impl-plan.md ticket-spec feature-file` so the shared coordinator gives only the bounded design evidence to the preferred opposite headless agent. Its typed result must satisfy the configured policy; a private subagent result cannot satisfy this gate. On a pass, stamp it:
173
173
 
174
174
  ```bash
175
- bun .safeword/hooks/write-review-stamp.ts impl-plan
175
+ bun .safeword/hooks/write-review-stamp.ts --author-agent "author-agent" --reviewer-agent "actual-reviewer" --independence "independence" impl-plan
176
176
  ```
177
177
 
178
178
  The stamp binds to the plan's current content, so editing the design after review invalidates it — re-review and re-stamp.
179
179
 
180
- **Cross-model (`crossModelReview: true`).** The reviewer must run on a **different model than the author** — a same-model reviewer shares the author's blind spots (correlated errors). Prefer one of comparable-or-better capability; never weaker. This means an explicit different-model subagent **not** a `context: fork`, which inherits the author's model. Record the model you assigned:
180
+ **Cross-model (`crossModelReview: true`).** The reviewer must run on a **different model than the author** — a same-model reviewer shares the author's blind spots (correlated errors). Prefer one of comparable-or-better capability; never weaker. Record a model only when the executed reviewer reports a verifiable identifier; the cross-agent coordinator does not guess a default model:
181
181
 
182
182
  ```bash
183
- bun .safeword/hooks/write-review-stamp.ts --model "<reviewer-model-id>" impl-plan
183
+ bun .safeword/hooks/write-review-stamp.ts --author-agent "author-agent" --reviewer-agent "actual-reviewer" --model "verified-model" --independence "independence" impl-plan
184
184
  ```
185
185
 
186
- The gate compares that tag against the author model (captured at SessionStart) and enforces **different only** — "comparable-or-better" is your judgment, not gate-checked. An absent tag fails closed. If you can't run a different model, log a deliberate skip (`--skip "<reason>"`) rather than stamping a same-model review. (This gate is stricter than quality-review's advisory loop, which accepts a fresh-context pass on your own model — here a genuinely different model, or an explicit `--skip`, is required.)
186
+ The gate compares that tag against the author model (captured at SessionStart) and enforces **different only** — "comparable-or-better" is your judgment, not gate-checked. An absent tag fails closed. When `crossAgentReview` is `require`, degraded evidence and skips also fail closed; restore the opposite reviewer and rerun the coordinator. (This gate is stricter than quality-review's advisory loop, which may accept a labeled same-agent result under the default `prefer` policy.)
187
187
 
188
188
  **Avoid bloat.**
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: closeout
3
+ description: Close a completed local delivery safely. Use when wrapping up a
4
+ finished coding session by verifying it, merging only with explicit authority,
5
+ running the mandatory retrospective, and cleaning the exact merged branch and
6
+ worktree. Do NOT use for cloud-agent tasks, unmerged work, or cleanup without
7
+ a pull request.
8
+ ---
9
+
10
+ # Closeout
11
+
12
+ Close a completed local GitHub delivery from observed state. Never compress the
13
+ workflow into “merge succeeded, so we are done.”
14
+
15
+ ## 1. Prove delivery readiness
16
+
17
+ Run `$safeword:verify` for the current pull request head. Then observe the pull request
18
+ directly with structured `gh pr view --json` output. Require all of these before
19
+ any merge:
20
+
21
+ - local verification covers the current pull request head;
22
+ - all required checks pass;
23
+ - review requirements are satisfied; and
24
+ - the pull request is not a draft.
25
+
26
+ Collect and report every blocker. Missing, stale, failing, pending, unknown, or
27
+ ambiguous evidence means **no merge or cleanup**. A merge command's exit status
28
+ never proves that the pull request is merged.
29
+
30
+ ## 2. Respect merge authority
31
+
32
+ Invocation alone grants no merge authority. Read authority only from the current user request;
33
+ historical, implied, or previously consumed authority is not available to a
34
+ resumed closeout.
35
+
36
+ - **No authority:** report that the delivery is ready and stop before merging.
37
+ - **Normal merge:** only an explicit current request for a normal merge permits a
38
+ policy-compliant `gh pr merge`. Never escalate a blocked normal merge.
39
+ - **Administrative merge:** only an explicit current request to perform an
40
+ administrative merge or bypass repository requirements permits `--admin`.
41
+
42
+ Merge authority is consumed when the merge action is attempted. Entering a merge
43
+ queue or enabling auto-merge consumes it too; later runs observe that queued
44
+ action and do not repeat it.
45
+
46
+ ## 3. Re-observe merge truth and resume
47
+
48
+ After every merge command—success or error—re-observe the exact pull request:
49
+
50
+ ```sh
51
+ gh pr view PR_NUMBER --json state,mergedAt,mergeCommit,headRefName,headRefOid
52
+ ```
53
+
54
+ Continue only when `state` is exactly `MERGED` and the observed head still
55
+ matches the recorded pull request head. Queued, automatic, pending, unknown, or
56
+ unobservable results are not merge proof; report the recovery check and stop.
57
+
58
+ If the command reported an error but fresh observation proves the expected head
59
+ was merged, report that the remote merge succeeded, do not retry it, and proceed
60
+ to the mandatory retrospective. On every invocation, re-observe durable state
61
+ and continue only the unfinished suffix. Treat an absent cleanup target as
62
+ complete only after proving it was the exact planned target. If the pull request
63
+ is merged, its retrospective is complete, and its exact branch and worktree are
64
+ already absent, report that the session is already closed.
65
+
66
+ ## 4. Complete the current session's retrospective
67
+
68
+ After merge is independently confirmed, invoke the cleanup guard in preview
69
+ mode. Its host hook supplies a short-lived, single-consumer binding to this exact
70
+ session (and Cursor transcript). A missing or expired binding fails closed;
71
+ there is no newest-session fallback and callers cannot nominate another receipt,
72
+ session, transcript, or spool.
73
+
74
+ The guard runs `safeword retro run --json` itself and accepts only a
75
+ successful result whose `data.agent_filing_needed` is `false` and whose derived
76
+ current session has an empty filing spool. Zero substantial findings and every
77
+ finding successfully filed are both complete outcomes.
78
+
79
+ Failed extraction, failed filing, pending drafts, malformed output, or an
80
+ identity mismatch means no cleanup. Report every failure and its recovery
81
+ action. A request to skip retro does not create a bypass: preserve the worktree
82
+ and branches and explain that the retrospective is required before cleanup.
83
+
84
+ ## 5. Preview, confirm, and apply exact cleanup
85
+
86
+ Run the guard from the delivery worktree; preview is the default:
87
+
88
+ ```sh
89
+ bun .safeword/scripts/closeout-cleanup.ts --pr PR_NUMBER
90
+ ```
91
+
92
+ The preview reruns the project's verification, build, typecheck, BDD, and
93
+ dependency plans and binds the resulting repository state and exact PR identity
94
+ to `PLAN_DIGEST`. Report the complete operation list and all blockers. Do not
95
+ apply a blocked plan.
96
+
97
+ With the user's cleanup intent already established by invoking closeout, apply
98
+ only the unchanged preview:
99
+
100
+ ```sh
101
+ bun .safeword/scripts/closeout-cleanup.ts --pr PR_NUMBER --yes --plan PLAN_DIGEST
102
+ ```
103
+
104
+ The guard re-observes identity and executes only this order: worktree, remote
105
+ branch, local branch. It never passes `--force` to `git worktree remove`; remote
106
+ deletion uses an exact `--force-with-lease`, and squash/rebase-safe local deletion
107
+ uses `git update-ref -d` with the recorded old OID. Never use merge-time branch
108
+ deletion. Changed, dirty, locked, stale, protected, default, main, ambiguous, or
109
+ other-worktree targets are preserved and reported with a recovery action.
110
+
111
+ ## 6. Report the durable result
112
+
113
+ Claim the session complete only after fresh observation proves every state.
114
+ Report:
115
+
116
+ - verification and the exact verified head;
117
+ - merged state and merge commit;
118
+ - retrospective completion and filing result;
119
+ - remote branch, local branch, and worktree state; and
120
+ - unresolved items (explicitly `none` when empty).
121
+
122
+ When blocked or partially complete, report every blocker and its recovery action,
123
+ including simultaneous blockers. Never hide a successful remote merge behind a
124
+ later local cleanup failure, and never describe a planned deletion as completed.
@@ -48,6 +48,56 @@ If in a BDD workflow, read the current ticket from `<namespace-root>/tickets/` a
48
48
  | verify | Flaky-test & regression patterns, coverage gaps |
49
49
  | done | CI/CD patterns, release checklists |
50
50
 
51
+ ### Project-principle challenge
52
+
53
+ For a BDD ticket, run `bun .safeword/hooks/resolve-project-knowledge.ts` at the
54
+ start of each pass and read the current `principles`, `personas`, and `surfaces`
55
+ paths and content it returns (including overrides such as `paths.principles`).
56
+ Do not substitute labels or intake-era content.
57
+ With `impl-plan.md`, read those sources alongside the plan and work-product.
58
+ Treat the plan's
59
+ **principle → concrete consequence → proof** entries as claims to refute, not a
60
+ compliance checklist:
61
+
62
+ - Challenge applicability, including a principle the plan may have omitted;
63
+ report only omissions that would materially change behavior, design, proof,
64
+ or a deliberate deviation.
65
+ - Check that each consequence actually follows from the principle and appears
66
+ in the shipped work; check that the named proof demonstrates that consequence
67
+ rather than adjacent mechanics.
68
+ - An experiential principle is not proven by tests alone. Require the
69
+ user-facing signal the plan named—such as a persona walkthrough, usability
70
+ observation, or Rave Moment check—and state any evidence limitation.
71
+ - For sourcing or architecture principles, independently check current options,
72
+ extension boundaries, and compatibility claims against primary sources; do
73
+ not accept the plan's research summary as its own proof.
74
+ - Treat an intentional conflict as valid only when Known deviations names it
75
+ and explains the trade-off.
76
+
77
+ This is the judgment gate. `$safeword:audit` later checks trace integrity as observable
78
+ facts only; it does not decide whether a principle was applicable or wise.
79
+
80
+ ### Persona and surface challenge
81
+
82
+ For a BDD ticket, read `spec.md` plus the configured persona and surface
83
+ inventories (`paths.personas` and `paths.surfaces`). Challenge whether the
84
+ shipped behavior fulfills each persona's JTBD and Rules, rather than merely
85
+ resolving a persona code. Then reconcile every affected surface against the
86
+ plan, scenarios, and verification output:
87
+
88
+ - Require one concrete proof result per affected surface, or a named `skip:`
89
+ with its limitation; an `@surface.*` tag alone is coverage intent, not surface
90
+ evidence.
91
+ - Check the surface evidence used the real surface boundary or names why that
92
+ boundary could not run. A generic unit test does not prove runtime, client,
93
+ protocol, or deployment parity.
94
+ - Challenge omitted personas or surfaces only when the source artifacts and
95
+ ticket scope make the omission material; do not turn either inventory into a
96
+ universal checklist.
97
+
98
+ Persona fulfillment and proof fidelity are review judgments. `$safeword:audit` owns only
99
+ unknown references, stale tags, and dead evidence links.
100
+
51
101
  ## 2. Research Angles
52
102
 
53
103
  Run each angle that applies — angle _diversity_ is the lever, not search volume: **source-currency** + **risk/security** (this section), **supersession** + **primary-source docs** (§3). If the user gave a focus or scope restriction, apply it to **every** angle — don't use it only for the first search.
@@ -119,17 +169,21 @@ Run the review in passes until **Critical issues** come back None. A couple of p
119
169
 
120
170
  Each pass:
121
171
 
122
- 1. **Review with a fresh, independent reviewer.** A same-model, same-context
123
- reviewer shares your blind spots, and ungrounded self-correction can
124
- _degrade_ the work rather than improve it. Prefer a different model of
125
- comparable-or-better capability; otherwise run a fresh-context pass on your
126
- own model (the usual path, since most setups run one model) — never a
127
- _weaker_ one. Hand the reviewer only the work-product and its scope, have it
128
- apply §1–3, and return the Output Format above.
129
- - Claude Code: Agent/Task tool. Codex: ask in your prompt — subagents never
130
- auto-spawn, and `/agent` only switches existing threads. Cursor: subagents.
131
- No sub-agent? Re-read in a fresh context independence is the point, not
132
- the mechanism.
172
+ 1. **Run the shared independent-review coordinator.** After gathering any
173
+ current-source evidence needed by §1–3, pass only the bounded work-product
174
+ and scope to the host-owned coordinator:
175
+
176
+ ```bash
177
+ safeword review run quality-review changed-file [more-changed-files...]
178
+ ```
179
+
180
+ Claude-authored work prefers headless Codex; Codex-authored work prefers
181
+ headless Claude. The coordinator uses a neutral snapshot, checks reviewer
182
+ provenance, preserves the exact preferred-route failure, and labels any
183
+ permitted same-agent fallback as degraded. Treat its typed result as the
184
+ review verdict. If it blocks, follow its one recovery action; do not invent
185
+ a private subagent route or mint passing evidence yourself.
186
+
133
187
  2. **Triage.** Fix every **Critical issue** this pass. Apply the **Suggested
134
188
  improvements** worth the change; list the rest — don't chase them.
135
189
  3. **Decide.** Stop when **Critical issues = None**; remaining suggestions are
@@ -48,9 +48,14 @@ the host project.
48
48
 
49
49
  4. After every successful comment or create, append exactly one compact JSON ack
50
50
  `{"signature":"<signature>","issue":<number>}` to the sibling `.acks.jsonl`
51
- file before removing that draft from the spool. Leave failed drafts in place.
52
- 5. Create at most five new issues per run. Rewrite the spool with only unfiled
53
- drafts, or delete it when none remain. If tracker write access is unavailable,
54
- leave the spool unchanged and report `retro-filer: cannot file - <reason>`.
51
+ file, then re-read it and exact-match that signature and destination. Remove
52
+ the draft only when the append succeeded and the exact ack is visible. If the
53
+ append or verification fails, leave the draft in place.
54
+ 5. Create at most five new issues per run. Drain only by running
55
+ `bun .safeword/hooks/lib/drain-retro-spool.ts "<spool-path>"`; never rewrite or
56
+ delete the spool directly. The helper removes only drafts whose valid ack is
57
+ reader-visible, so unfiled or unacknowledged drafts remain. If tracker write
58
+ access is unavailable, leave the spool unchanged and report
59
+ `retro-filer: cannot file - <reason>`.
55
60
 
56
61
  Finish with one line of counts: `retro-filer: filed 2, commented 1, remaining 0`.