@pmelab/gtd 24.0.0 → 24.2.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 (203) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +42 -13
  3. package/claude/hooks/access.ts +26 -1
  4. package/claude/hooks/assist.ts +135 -0
  5. package/claude/hooks/drive.ts +15 -7
  6. package/claude/hooks/judge.ts +27 -0
  7. package/claude/hooks/register.tsx +294 -135
  8. package/claude/hooks/tools.ts +121 -0
  9. package/claude/hooks/wording.ts +2 -35
  10. package/claude/types/index.d.ts +9 -0
  11. package/dist/chunks/Schema-Dh-mkwkq.mjs +25353 -0
  12. package/dist/chunks/reserved-ejn-LVYO.mjs +1388 -0
  13. package/dist/chunks/rolldown-runtime-8H4AJuhK.mjs +14 -0
  14. package/dist/chunks/steering-D-PvRC5W.mjs +16296 -0
  15. package/dist/chunks/wire-DA7q2ikx.mjs +473 -0
  16. package/dist/flows.mjs +525 -0
  17. package/dist/gtd.bundle.mjs +43339 -55575
  18. package/dist/steering.mjs +2 -0
  19. package/dist/testkit.mjs +13006 -0
  20. package/dist/wire.mjs +2 -0
  21. package/dist/workflow-kit.mjs +352 -0
  22. package/dist/workflows/feature.mjs +315 -0
  23. package/dist/workflows/fix.mjs +40 -0
  24. package/dist/workflows/review.mjs +25 -0
  25. package/dist/workflows.mjs +8 -6
  26. package/package.json +69 -8
  27. package/src/cli/Cli.test.ts +1329 -0
  28. package/src/cli/Cli.ts +1348 -0
  29. package/src/cli/Program.test.ts +2262 -0
  30. package/src/cli/Program.ts +1331 -0
  31. package/src/cli/ToolCommands.ts +43 -0
  32. package/src/cli/index.ts +14 -0
  33. package/src/edge/Edge.test.ts +1016 -0
  34. package/src/edge/Edge.ts +1225 -0
  35. package/src/edge/index.ts +16 -0
  36. package/src/emit/Emit.test.ts +425 -0
  37. package/src/emit/Emit.ts +139 -0
  38. package/src/emit/GitScript.test.ts +260 -0
  39. package/src/emit/GitScript.ts +131 -0
  40. package/src/emit/ModeContradiction.test.ts +120 -0
  41. package/src/emit/ModeContradiction.ts +66 -0
  42. package/src/emit/OutcomeScript.test.ts +238 -0
  43. package/src/emit/OutcomeScript.ts +73 -0
  44. package/src/emit/SteeringMode.test.ts +274 -0
  45. package/src/emit/SteeringMode.ts +256 -0
  46. package/src/emit/index.ts +40 -0
  47. package/src/install/ConfigScaffold.ts +19 -0
  48. package/src/install/Install.test.ts +426 -0
  49. package/src/install/Install.ts +634 -0
  50. package/src/install/Version.ts +27 -0
  51. package/src/install/index.ts +3 -0
  52. package/src/platform/CommandRunner.ts +74 -0
  53. package/src/platform/Commentary.test.ts +126 -0
  54. package/src/platform/Commentary.ts +92 -0
  55. package/src/platform/Git.test.ts +50 -0
  56. package/src/platform/Git.ts +552 -0
  57. package/src/platform/Host.ts +42 -0
  58. package/src/platform/RetainedHistory.test.ts +65 -0
  59. package/src/platform/RetainedHistory.ts +34 -0
  60. package/src/platform/Workspace.test.ts +669 -0
  61. package/src/platform/Workspace.ts +631 -0
  62. package/src/platform/WorktreeState.test.ts +162 -0
  63. package/src/platform/WorktreeState.ts +50 -0
  64. package/src/platform/index.ts +22 -0
  65. package/src/replay/Access.test.ts +53 -0
  66. package/src/replay/Access.ts +46 -0
  67. package/src/replay/Format.test.ts +20 -0
  68. package/src/replay/Format.ts +14 -0
  69. package/src/replay/Replay.test.ts +887 -0
  70. package/src/replay/Replay.ts +669 -0
  71. package/src/replay/Trailers.test.ts +117 -0
  72. package/src/replay/Trailers.ts +213 -0
  73. package/src/replay/Tree.test.ts +25 -0
  74. package/src/replay/Tree.ts +45 -0
  75. package/src/replay/index.ts +11 -0
  76. package/src/steering/Blocks.fixture.ts +6 -0
  77. package/src/steering/Blocks.test.ts +167 -0
  78. package/src/steering/Blocks.ts +321 -0
  79. package/src/steering/CodeThreads.test.ts +127 -0
  80. package/src/steering/CodeThreads.ts +197 -0
  81. package/src/steering/Edits.test.ts +19 -0
  82. package/src/steering/Edits.ts +20 -0
  83. package/src/steering/Eol.test.ts +16 -0
  84. package/src/steering/Eol.ts +2 -0
  85. package/src/steering/FORMATS.md +58 -0
  86. package/src/steering/Footnotes.test.ts +977 -0
  87. package/src/steering/Footnotes.ts +661 -0
  88. package/src/steering/Inline.fixture.ts +7 -0
  89. package/src/steering/Inline.test.ts +227 -0
  90. package/src/steering/Inline.ts +195 -0
  91. package/src/steering/MarkdownTree.test.ts +247 -0
  92. package/src/steering/MarkdownTree.ts +260 -0
  93. package/src/steering/Outline.ts +19 -0
  94. package/src/steering/SteeringFormat.test.ts +91 -0
  95. package/src/steering/SteeringFormat.ts +267 -0
  96. package/src/steering/SteeringFormats.test.ts +163 -0
  97. package/src/steering/Threads.ts +163 -0
  98. package/src/steering/contract.test.ts +264 -0
  99. package/src/steering/fixtures/code-threads/cache.stripped.ts.txt +6 -0
  100. package/src/steering/fixtures/code-threads/cache.ts.md +15 -0
  101. package/src/steering/fixtures/free-form/notes.md +12 -0
  102. package/src/steering/fixtures/free-form/notes.read.json +42 -0
  103. package/src/steering/fixtures/qa/answered.md +23 -0
  104. package/src/steering/fixtures/qa/answered.read.json +119 -0
  105. package/src/steering/fixtures/qa/requirements.md +160 -0
  106. package/src/steering/fixtures/qa/requirements.read.json +475 -0
  107. package/src/steering/fixtures/review/review.md +76 -0
  108. package/src/steering/fixtures/review/review.read.json +237 -0
  109. package/src/steering/fixtures/review/ticked.md +23 -0
  110. package/src/steering/fixtures/review/ticked.read.json +99 -0
  111. package/src/steering/fixtures/review-notes/baseline.md +14 -0
  112. package/src/steering/fixtures/review-notes/noted.md +19 -0
  113. package/src/steering/freeform.test.ts +311 -0
  114. package/src/steering/freeform.ts +148 -0
  115. package/src/steering/index.test.ts +62 -0
  116. package/src/steering/index.ts +140 -0
  117. package/src/steering/qa.test.ts +2160 -0
  118. package/src/steering/qa.ts +1104 -0
  119. package/src/steering/review.test.ts +1829 -0
  120. package/src/steering/review.ts +1180 -0
  121. package/src/step/LandStep.ts +31 -0
  122. package/src/step/RepoSnapshot.ts +29 -0
  123. package/src/step/accessRefusal.test.ts +58 -0
  124. package/src/step/accessRefusal.ts +26 -0
  125. package/src/step/index.ts +5 -0
  126. package/src/step/planStart.test.ts +202 -0
  127. package/src/step/planStart.ts +167 -0
  128. package/src/step/planStep.test.ts +104 -0
  129. package/src/step/planStep.ts +89 -0
  130. package/src/step/snapshot.fixture.ts +28 -0
  131. package/src/testkit/EmittedScriptRecognizer.test.ts +639 -0
  132. package/src/testkit/EmittedScriptRecognizer.ts +642 -0
  133. package/src/testkit/FakeGitOperations.ts +95 -0
  134. package/src/testkit/GitTiers.test.ts +11 -0
  135. package/src/testkit/GitTiers.ts +1052 -0
  136. package/src/testkit/InMemRepo.ts +467 -0
  137. package/src/testkit/Layers.test.ts +58 -0
  138. package/src/testkit/Layers.ts +241 -0
  139. package/src/testkit/StrictGitOperations.ts +31 -0
  140. package/src/testkit/TestRepo.test.ts +119 -0
  141. package/src/testkit/TestRepo.ts +274 -0
  142. package/src/testkit/cliIo.ts +36 -0
  143. package/src/testkit/index.ts +9 -0
  144. package/src/testkit/steps.ts +177 -0
  145. package/src/testkit/testkit.feature +65 -0
  146. package/src/wire/BeatDocument.test.ts +419 -0
  147. package/src/wire/BeatDocument.ts +114 -0
  148. package/src/wire/BeatPlain.test.ts +113 -0
  149. package/src/wire/BeatPlain.ts +127 -0
  150. package/src/wire/BeatStatus.test.ts +59 -0
  151. package/src/wire/BeatStatus.ts +81 -0
  152. package/src/wire/Demand.test.ts +142 -0
  153. package/src/wire/Demand.ts +108 -0
  154. package/src/wire/ExitCodes.test.ts +21 -0
  155. package/src/wire/ExitCodes.ts +16 -0
  156. package/src/wire/Glob.ts +29 -0
  157. package/src/wire/Judge.ts +22 -0
  158. package/src/wire/Land.test.ts +120 -0
  159. package/src/wire/Land.ts +75 -0
  160. package/src/wire/Select.test.ts +148 -0
  161. package/src/wire/Select.ts +78 -0
  162. package/src/wire/Sessions.test.ts +59 -0
  163. package/src/wire/Sessions.ts +39 -0
  164. package/src/wire/WireSchema.test.ts +162 -0
  165. package/src/wire/constants.ts +22 -0
  166. package/src/wire/golden/1/beat-capture.json +34 -0
  167. package/src/wire/golden/1/beat-message.json +34 -0
  168. package/src/wire/golden/1/beat-prompt.json +44 -0
  169. package/src/wire/golden/1/beat-script.json +34 -0
  170. package/src/wire/golden/1/beat-stalled.json +34 -0
  171. package/src/wire/golden/1/judge.json +14 -0
  172. package/src/wire/golden/1/land-noop.json +10 -0
  173. package/src/wire/golden/1/land.json +10 -0
  174. package/src/wire/index.ts +12 -0
  175. package/src/wire/types.ts +46 -0
  176. package/src/workflow/Diagnostic.test.ts +93 -0
  177. package/src/workflow/Diagnostic.ts +54 -0
  178. package/src/workflow/SteeringFormats.test.ts +27 -0
  179. package/src/workflow/SteeringFormats.ts +6 -0
  180. package/src/workflow/Workflow.ts +77 -0
  181. package/src/workflow/compile.test.ts +53 -0
  182. package/src/workflow/compile.ts +56 -0
  183. package/src/workflow/discovery.ts +122 -0
  184. package/src/workflow/fingerprint.test.ts +52 -0
  185. package/src/workflow/fingerprint.ts +53 -0
  186. package/src/workflow/index.ts +33 -0
  187. package/src/workflow/load.test.ts +1017 -0
  188. package/src/workflow/load.ts +836 -0
  189. package/src/workflow/skills.test.ts +109 -0
  190. package/src/workflow/skills.ts +90 -0
  191. package/src/workflow/vars.ts +33 -0
  192. package/src/workflows/feature/feature.test.ts +27 -33
  193. package/src/workflows/feature/index.ts +10 -0
  194. package/src/workflows/fix/fix.feature +80 -0
  195. package/src/workflows/fix/fix.test.ts +58 -78
  196. package/src/workflows/fix/index.ts +6 -0
  197. package/src/workflows/index.ts +2 -10
  198. package/src/workflows/review/index.ts +6 -0
  199. package/src/workflows/review/review.test.ts +85 -29
  200. package/src/workflows/review/review.ts +3 -1
  201. package/src/workflows/shared/config.fixture.ts +12 -0
  202. package/src/workflows/shared/reserved.ts +11 -0
  203. package/src/workflows/shared/workflows.steps.ts +7 -0
@@ -0,0 +1,475 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "title": "Review note severity tiers (issue #270, T3b)",
5
+ "anchor": {
6
+ "kind": "paragraph",
7
+ "line": 0
8
+ }
9
+ },
10
+ {
11
+ "title": "Sketch: `.gtd/TODO.md` said \"work on issue 270\". Issue #270: at the `review.triage` judge rest (T3, shipped), ask four narrow questions per human note — `security` (yes/no), `reversibility` (one-way/two-way), `blastRadius` (local/module/contract), `newConcept` (yes/no) — in the same commit, as more `Gtd-Judge:` trailers. Flow code, never the model, turns the answers into a tier: security yes or one-way → `critical`; contract or new concept → `should`; else `could`.",
12
+ "anchor": {
13
+ "kind": "paragraph",
14
+ "line": 2
15
+ }
16
+ },
17
+ {
18
+ "title": "Concerns",
19
+ "anchor": {
20
+ "kind": "paragraph",
21
+ "line": 80
22
+ }
23
+ },
24
+ {
25
+ "title": "1. Severity questions at the triage rest — PRODUCT",
26
+ "anchor": {
27
+ "kind": "paragraph",
28
+ "line": 82
29
+ }
30
+ },
31
+ {
32
+ "title": "The `review.triage` rest asks the four severity questions per tiered note (which notes: open question above) alongside the T3 verdict, in one commit. Question wording follows the issue: one-way covers migrations, published API shapes, file formats, anything another system depends on by tomorrow; new concept is judged against the code, never a glossary file, and reusing or extending an existing concept is not new.",
33
+ "anchor": {
34
+ "kind": "paragraph",
35
+ "line": 84
36
+ }
37
+ },
38
+ {
39
+ "title": "2. Tier derivation and guard rails — PRODUCT",
40
+ "anchor": {
41
+ "kind": "paragraph",
42
+ "line": 91
43
+ }
44
+ },
45
+ {
46
+ "title": "Flow code derives the tier: security yes OR one-way → `critical`; contract OR new concept → `should`; else `could`. Deferring is the risky direction: an unanswered question, truncated evidence, or `p` below `reviewSeverityMinP` makes the note `critical`, never `could`. A human note starting with `!` is `critical` and gets no severity questions.",
47
+ "anchor": {
48
+ "kind": "paragraph",
49
+ "line": 93
50
+ }
51
+ },
52
+ {
53
+ "title": "3. Routing by tier — PRODUCT",
54
+ "anchor": {
55
+ "kind": "paragraph",
56
+ "line": 99
57
+ }
58
+ },
59
+ {
60
+ "title": "`critical` + `should` → `.gtd/REQUIREMENTS.md` this round, via the existing collecting turn. `could` → `.gtd/TODO.md` as a deferred package (lifecycle: open question above). Inside REQUIREMENTS.md: one-way doors first, then new concepts, then the rest in note order.",
61
+ "anchor": {
62
+ "kind": "paragraph",
63
+ "line": 101
64
+ }
65
+ },
66
+ {
67
+ "title": "4. Sign-off block on criticals — PRODUCT",
68
+ "anchor": {
69
+ "kind": "paragraph",
70
+ "line": 106
71
+ }
72
+ },
73
+ {
74
+ "title": "A round with an open critical cannot sign off (what counts as resolved: open question above).",
75
+ "anchor": {
76
+ "kind": "paragraph",
77
+ "line": 108
78
+ }
79
+ },
80
+ {
81
+ "title": "5. `!` marker in the review format — TECHNICAL",
82
+ "anchor": {
83
+ "kind": "paragraph",
84
+ "line": 111
85
+ }
86
+ },
87
+ {
88
+ "title": "`src/steering/review.ts` recognises the leading `!` on a human note and strips it from the text the folding turn sees.",
89
+ "anchor": {
90
+ "kind": "paragraph",
91
+ "line": 113
92
+ }
93
+ },
94
+ {
95
+ "title": "6. New process setting `reviewSeverityMinP` — TECHNICAL",
96
+ "anchor": {
97
+ "kind": "paragraph",
98
+ "line": 116
99
+ }
100
+ },
101
+ {
102
+ "title": "Default and fold-time recording per the answered questions below; outcomes stay re-foldable from the recorded trailers (T7, #272), never re-asked.",
103
+ "anchor": {
104
+ "kind": "paragraph",
105
+ "line": 118
106
+ }
107
+ },
108
+ {
109
+ "title": "7. Acceptance — PRODUCT",
110
+ "anchor": {
111
+ "kind": "paragraph",
112
+ "line": 121
113
+ }
114
+ },
115
+ {
116
+ "title": "A review with one one-way-door note, one new-concept note and six style notes produces REQUIREMENTS.md with two entries (one-way door first) and TODO.md with six, and sign-off stays blocked until the one-way door is resolved. Cucumber scenarios cover it; README and `docs/` reflect the `!` marker, the new setting and the deferral.",
117
+ "anchor": {
118
+ "kind": "paragraph",
119
+ "line": 123
120
+ }
121
+ },
122
+ {
123
+ "title": "Which notes get a severity tier?",
124
+ "detail": "T3 already routes each note by verdict: `edit` → planning lap, `question` → answered in place, `nit` → one batched fix, `praise` → dropped. The tier can reroute only edits, or also pull questions and nits into a lap.",
125
+ "status": "open",
126
+ "answered": false,
127
+ "anchor": {
128
+ "kind": "question",
129
+ "index": 0
130
+ },
131
+ "body": [
132
+ {
133
+ "title": "T3 already routes each note by verdict: `edit` → planning lap, `question` → answered in place, `nit` → one batched fix, `praise` → dropped. The tier can reroute only edits, or also pull questions and nits into a lap.",
134
+ "anchor": {
135
+ "kind": "paragraph",
136
+ "line": 14
137
+ }
138
+ }
139
+ ],
140
+ "children": [
141
+ {
142
+ "title": "Only `edit` notes are tiered",
143
+ "checked": false,
144
+ "anchor": {
145
+ "kind": "option",
146
+ "questionIndex": 0,
147
+ "index": 0
148
+ },
149
+ "body": [
150
+ {
151
+ "title": "critical/should edits → REQUIREMENTS.md now; could edits → TODO.md Questions, nits, praise route exactly as today; no severity asked for them A nit that renames a published API (one-way) is still fixed without a plan Done-when holds only if the six style notes are judged `edit`, not `nit`",
152
+ "anchor": {
153
+ "kind": "paragraph",
154
+ "line": 19
155
+ }
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ "title": "Every note but praise is tiered; the tier only escalates",
161
+ "checked": false,
162
+ "anchor": {
163
+ "kind": "option",
164
+ "questionIndex": 0,
165
+ "index": 1
166
+ },
167
+ "body": [
168
+ {
169
+ "title": "A critical/should question or nit goes to REQUIREMENTS.md instead of its T3 route A could question is still answered, a could nit still fixed now; only could edits are deferred More judge questions per rest; a misjudged \"touches auth\" pulls a harmless nit into a full lap",
170
+ "anchor": {
171
+ "kind": "paragraph",
172
+ "line": 24
173
+ }
174
+ }
175
+ ]
176
+ },
177
+ {
178
+ "title": "Every note but praise is tiered; could also defers nits",
179
+ "checked": false,
180
+ "anchor": {
181
+ "kind": "option",
182
+ "questionIndex": 0,
183
+ "index": 2
184
+ },
185
+ "body": [
186
+ {
187
+ "title": "Same escalation as above, plus could nits go to TODO.md instead of being fixed The T3 nit route then only fires for nothing — effectively retired Matches the done-when literally whether style notes are judged edit or nit",
188
+ "anchor": {
189
+ "kind": "paragraph",
190
+ "line": 31
191
+ }
192
+ }
193
+ ]
194
+ },
195
+ {
196
+ "title": "",
197
+ "checked": false,
198
+ "anchor": {
199
+ "kind": "option",
200
+ "questionIndex": 0,
201
+ "index": 3
202
+ }
203
+ }
204
+ ]
205
+ },
206
+ {
207
+ "title": "What does \"a lap cannot sign off with an open critical\" enforce?",
208
+ "detail": "A critical note always goes to REQUIREMENTS.md, so the round it appears in already cannot sign off. The fork is whether the NEXT round checks that the critical was actually dealt with.",
209
+ "status": "open",
210
+ "answered": false,
211
+ "anchor": {
212
+ "kind": "question",
213
+ "index": 1
214
+ },
215
+ "body": [
216
+ {
217
+ "title": "A critical note always goes to REQUIREMENTS.md, so the round it appears in already cannot sign off. The fork is whether the NEXT round checks that the critical was actually dealt with.",
218
+ "anchor": {
219
+ "kind": "paragraph",
220
+ "line": 39
221
+ }
222
+ }
223
+ ],
224
+ "children": [
225
+ {
226
+ "title": "Nothing extra: routing a critical into a lap is the block",
227
+ "checked": false,
228
+ "anchor": {
229
+ "kind": "option",
230
+ "questionIndex": 1,
231
+ "index": 0
232
+ },
233
+ "body": [
234
+ {
235
+ "title": "No new step, no new guard The next round signs off whenever the human signs off, even if the fix missed the point",
236
+ "anchor": {
237
+ "kind": "paragraph",
238
+ "line": 44
239
+ }
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ "title": "Carry each critical into the next REVIEW.md as an item the human must tick",
245
+ "checked": false,
246
+ "anchor": {
247
+ "kind": "option",
248
+ "questionIndex": 1,
249
+ "index": 1
250
+ },
251
+ "body": [
252
+ {
253
+ "title": "The review gate refuses sign-off while any carried critical is unticked New visible ritual in REVIEW.md; the human, not a model, decides \"resolved\"",
254
+ "anchor": {
255
+ "kind": "paragraph",
256
+ "line": 48
257
+ }
258
+ }
259
+ ]
260
+ },
261
+ {
262
+ "title": "A judge asks \"is this critical resolved?\" per carried critical at sign-off",
263
+ "checked": false,
264
+ "anchor": {
265
+ "kind": "option",
266
+ "questionIndex": 1,
267
+ "index": 2
268
+ },
269
+ "body": [
270
+ {
271
+ "title": "One more judge rest per signing round Below-floor or unanswered keeps sign-off blocked, which can loop until the human edits again",
272
+ "anchor": {
273
+ "kind": "paragraph",
274
+ "line": 51
275
+ }
276
+ }
277
+ ]
278
+ },
279
+ {
280
+ "title": "",
281
+ "checked": false,
282
+ "anchor": {
283
+ "kind": "option",
284
+ "questionIndex": 1,
285
+ "index": 3
286
+ }
287
+ }
288
+ ]
289
+ },
290
+ {
291
+ "title": "What happens to deferred `could` notes in `.gtd/TODO.md`?",
292
+ "detail": "TODO.md is the human's sketch file at `idle`. A deferred note written there mid process gets committed onto the branch, and the next process's `unwind` only reverts what the human adds at `idle` — not what is already committed — so deferred notes do not reach the next triage on their own.",
293
+ "status": "open",
294
+ "answered": false,
295
+ "anchor": {
296
+ "kind": "question",
297
+ "index": 2
298
+ },
299
+ "body": [
300
+ {
301
+ "title": "TODO.md is the human's sketch file at `idle`. A deferred note written there mid process gets committed onto the branch, and the next process's `unwind` only reverts what the human adds at `idle` — not what is already committed — so deferred notes do not reach the next triage on their own.",
302
+ "anchor": {
303
+ "kind": "paragraph",
304
+ "line": 58
305
+ }
306
+ }
307
+ ],
308
+ "children": [
309
+ {
310
+ "title": "Append them to TODO.md and leave them committed; the human pulls them into a later process by hand",
311
+ "checked": false,
312
+ "anchor": {
313
+ "kind": "option",
314
+ "questionIndex": 2,
315
+ "index": 0
316
+ },
317
+ "body": [
318
+ {
319
+ "title": "Smallest build `.gtd/TODO.md` ships in the branch/PR unless the human deletes it Deferred items are only a reminder until a human re-types or re-touches them",
320
+ "anchor": {
321
+ "kind": "paragraph",
322
+ "line": 65
323
+ }
324
+ }
325
+ ]
326
+ },
327
+ {
328
+ "title": "Append them to TODO.md, and the next ordinary start treats a committed TODO.md as part of its sketch",
329
+ "checked": false,
330
+ "anchor": {
331
+ "kind": "option",
332
+ "questionIndex": 2,
333
+ "index": 1
334
+ },
335
+ "body": [
336
+ {
337
+ "title": "Deferred items become concerns of the next process automatically Changes what `unwind`/triage read for every ordinary start `.gtd/TODO.md` still sits in the branch until that next process",
338
+ "anchor": {
339
+ "kind": "paragraph",
340
+ "line": 70
341
+ }
342
+ }
343
+ ]
344
+ },
345
+ {
346
+ "title": "Do not commit them: hand them back at sign-off (message or summary), tree stays clean",
347
+ "checked": false,
348
+ "anchor": {
349
+ "kind": "option",
350
+ "questionIndex": 2,
351
+ "index": 2
352
+ },
353
+ "body": [
354
+ {
355
+ "title": "No `.gtd/` file leaks into the PR Deviates from the issue's \"TODO.md as a deferred package\"; nothing persists unless the human copies it",
356
+ "anchor": {
357
+ "kind": "paragraph",
358
+ "line": 75
359
+ }
360
+ }
361
+ ]
362
+ },
363
+ {
364
+ "title": "",
365
+ "checked": false,
366
+ "anchor": {
367
+ "kind": "option",
368
+ "questionIndex": 2,
369
+ "index": 3
370
+ }
371
+ }
372
+ ]
373
+ },
374
+ {
375
+ "title": "Where does the `!` go, and on which notes?",
376
+ "detail": "As the first character of the human's own note text, on any note kind — pointer note, footnote, chunk prose — stripped before folding; prefixing the pointer path itself would break the pointer parse, and one rule for all kinds is easier to remember.",
377
+ "status": "answered",
378
+ "answered": false,
379
+ "anchor": {
380
+ "kind": "question",
381
+ "index": 3
382
+ },
383
+ "body": [
384
+ {
385
+ "title": "As the first character of the human's own note text, on any note kind — pointer note, footnote, chunk prose — stripped before folding; prefixing the pointer path itself would break the pointer parse, and one rule for all kinds is easier to remember.",
386
+ "anchor": {
387
+ "kind": "paragraph",
388
+ "line": 133
389
+ }
390
+ }
391
+ ],
392
+ "children": []
393
+ },
394
+ {
395
+ "title": "Does a REQUIREMENTS.md entry show its tier?",
396
+ "detail": "Yes: each folded note names its tier and the answer that earned it (\"critical: one-way door\", \"critical: forced by `!`\", \"critical: judge unsure\"); the issue rejects a single score for being unauditable, so the reason must be visible.",
397
+ "status": "answered",
398
+ "answered": false,
399
+ "anchor": {
400
+ "kind": "question",
401
+ "index": 4
402
+ },
403
+ "body": [
404
+ {
405
+ "title": "Yes: each folded note names its tier and the answer that earned it (\"critical: one-way door\", \"critical: forced by `!`\", \"critical: judge unsure\"); the issue rejects a single score for being unauditable, so the reason must be visible.",
406
+ "anchor": {
407
+ "kind": "paragraph",
408
+ "line": 140
409
+ }
410
+ }
411
+ ],
412
+ "children": []
413
+ },
414
+ {
415
+ "title": "What order do concerns take inside REQUIREMENTS.md?",
416
+ "detail": "One-way doors, then new concepts, then every other critical/should note in document order — the issue fixes the first two; document order is the existing tie-break.",
417
+ "status": "answered",
418
+ "answered": false,
419
+ "anchor": {
420
+ "kind": "question",
421
+ "index": 5
422
+ },
423
+ "body": [
424
+ {
425
+ "title": "One-way doors, then new concepts, then every other critical/should note in document order — the issue fixes the first two; document order is the existing tie-break.",
426
+ "anchor": {
427
+ "kind": "paragraph",
428
+ "line": 146
429
+ }
430
+ }
431
+ ],
432
+ "children": []
433
+ },
434
+ {
435
+ "title": "What is `reviewSeverityMinP`'s default, and what does a blank value do?",
436
+ "detail": "`0.7`, one floor for all four questions, matching `reviewNoteActionable` and `judgeIdenticalMinP`; a blank value makes every tiered note `critical`, the same fail-safe a blank `reviewNoteActionable` gives (every note an `edit`).",
437
+ "status": "answered",
438
+ "answered": false,
439
+ "anchor": {
440
+ "kind": "question",
441
+ "index": 6
442
+ },
443
+ "body": [
444
+ {
445
+ "title": "`0.7`, one floor for all four questions, matching `reviewNoteActionable` and `judgeIdenticalMinP`; a blank value makes every tiered note `critical`, the same fail-safe a blank `reviewNoteActionable` gives (every note an `edit`).",
446
+ "anchor": {
447
+ "kind": "paragraph",
448
+ "line": 152
449
+ }
450
+ }
451
+ ],
452
+ "children": []
453
+ },
454
+ {
455
+ "title": "What happens without a configured judge?",
456
+ "detail": "Same as T3: the rest surfaces to the driver, which answers the extra questions like the verdict; no separate fallback.",
457
+ "status": "answered",
458
+ "answered": false,
459
+ "anchor": {
460
+ "kind": "question",
461
+ "index": 7
462
+ },
463
+ "body": [
464
+ {
465
+ "title": "Same as T3: the rest surfaces to the driver, which answers the extra questions like the verdict; no separate fallback.",
466
+ "anchor": {
467
+ "kind": "paragraph",
468
+ "line": 158
469
+ }
470
+ }
471
+ ],
472
+ "children": []
473
+ }
474
+ ]
475
+ }
@@ -0,0 +1,76 @@
1
+ # Review: 0ef1c31
2
+
3
+ <!-- base: 8d5defb41ddbf4ec3927018eecf09a893653a028 -->
4
+
5
+ **This round closes the reverse gap from the last review: a commit footer under
6
+ a title with no `!` now fails. It also anchors the title's `!` match and states
7
+ the both-or-neither rule in both agent prompts.** No new risk found.
8
+ `tests/tooling/commits.test.ts` passes locally: **31/31**.
9
+
10
+ ## PR check: both-or-neither for title and footer
11
+
12
+ **The title check now runs both ways.** A `!` title with no footer fails, as
13
+ before. **A footer with no `!` title now fails too.** The `!` match is a regex
14
+ anchored at the type prefix, so **a `!:` later in the title no longer counts as
15
+ breaking**.
16
+
17
+ - [ ] ./.github/workflows/commits.yml#36-46 — one
18
+ `git log --no-merges --format=%B` over the range sets `carries`. The old
19
+ per-commit loop is gone. `bang` is the bash version of
20
+ `commitlint.config.mjs`'s `bangHeader` (`\w` spelled `[A-Za-z0-9_]`). The
21
+ step uses GitHub's default shell, `bash -e` without pipefail, so `grep -q`
22
+ closing the pipe early cannot flip `carries` off. **If anyone adds
23
+ `shell: bash` (which turns on pipefail), a SIGPIPE from `git log` can
24
+ silently drop a found footer.**
25
+ - [ ] ./tests/tooling/commits.test.ts#52-59 — `check` now wraps `checkTitle`
26
+ with the fixed title `"feat: a title"`. `checkTitle` no longer adds a
27
+ `feat: fine` commit when given no messages, so callers pass their own.
28
+ - [ ] ./tests/tooling/commits.test.ts#62-66 — the passing case includes a footer
29
+ commit, so its title gains `!`. Without it the new reverse rule fails the
30
+ case.
31
+ - [ ] ./tests/tooling/commits.test.ts#118-132 — existing cases are adapted: the
32
+ title case now supplies its own commit, and the footer commit moves first
33
+ in the list. Neither changes what the case asserts.
34
+ - [ ] ./tests/tooling/commits.test.ts#140-150 — two new cases: a footer under a
35
+ plain title fails with "title has no", and `` `!:` `` in the middle of a
36
+ title passes.
37
+
38
+ ## Convention doc
39
+
40
+ **`docs/commits.md` states the reverse rule and that the 72-char limit applies
41
+ before GitHub's ` (#N)` suffix.**
42
+
43
+ - [ ] ./docs/commits.md#10-20 — prose only. The pinned string
44
+ `at most 72 characters` is unchanged, so the doc-pin test still matches.
45
+
46
+ ## Agent prompts state the rule
47
+
48
+ **Both closing-message prompts now require `!` and
49
+ `BREAKING CHANGE: <how to migrate>` together, and list the type set.** A new
50
+ test pins this.
51
+
52
+ - [ ] ./src/workflows/text.ts#867-878 — this replaces the old "`!` alone is not
53
+ enough" note with "both or neither". It adds a subject-format bullet with
54
+ the 9 types and the 72-char limit.
55
+ - [ ] ./claude/hooks/ship.ts#64-67 — squash prompt: a `!` subject needs the
56
+ footer.
57
+ - [ ] ./claude/hooks/ship.ts#301-304 — PR prompt: a `!` title needs the footer
58
+ line in the description. Line 304 now goes past the prompt's line width
59
+ (cosmetic only).
60
+ - [ ] ./tests/tooling/commits.test.ts#161-170 — pins both prompt files to the
61
+ config's `type-enum` and the literal footer placeholder. **The type check
62
+ is weak: `toContain("ci")` / `"fix"` / `"test"` matches anywhere in large
63
+ files, so dropping a type from the prompt prose would not fail it.** Only
64
+ the footer string check has real power.
65
+
66
+ ## Release bump cases and cache inputs
67
+
68
+ **More semantic-release cases, including a real GitHub squash body. The tooling
69
+ task cache now also keys on `package-lock.json`.**
70
+
71
+ - [ ] ./tests/tooling/commits.test.ts#238-240 — `fix` → patch, `docs` → no
72
+ release, and a `(#12)` squash with a `* feat!:` body footer → 2.0.0. This
73
+ proves the footer cuts a major from inside the squash body.
74
+ - [ ] ./turbo.json#86-88 — adds `package-lock.json` to the inputs, so a
75
+ commitlint or semantic-release version bump no longer reuses a stale
76
+ cached pass.