@pmelab/gtd 58.2.0 → 59.0.1

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 (150) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +120 -395
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-07Z_ENch.mjs} +13503 -3460
  4. package/dist/chunks/{wire-C12dahrH.mjs → wire-BTUhRGXj.mjs} +1 -1
  5. package/dist/flows.mjs +1 -1
  6. package/dist/gtd.bundle.mjs +484 -497
  7. package/dist/testkit/gherkin.mjs +145 -65
  8. package/dist/testkit.mjs +1 -1
  9. package/dist/wire.mjs +1 -1
  10. package/package.json +3 -42
  11. package/skills/authoring/SKILL.md +34 -27
  12. package/src/cli/Cli.test.ts +148 -71
  13. package/src/cli/Cli.ts +384 -463
  14. package/src/cli/Help.test.ts +47 -0
  15. package/src/cli/Help.ts +70 -0
  16. package/src/cli/Program.test.ts +127 -33
  17. package/src/cli/Program.ts +70 -25
  18. package/src/edge/Edge.ts +4 -4
  19. package/src/emit/Emit.test.ts +1 -1
  20. package/src/emit/Emit.ts +1 -1
  21. package/src/flags/index.test.ts +0 -13
  22. package/src/flags/index.ts +1 -3
  23. package/src/flows/factory.ts +3 -1
  24. package/src/gherkin/index.ts +135 -71
  25. package/src/gherkin/messages.test.ts +90 -0
  26. package/src/gherkin/messages.ts +84 -0
  27. package/src/install/Install.test.ts +14 -11
  28. package/src/install/Install.ts +38 -15
  29. package/src/install/index.ts +1 -2
  30. package/src/replay/Replay.test.ts +1 -1
  31. package/src/replay/index.ts +1 -1
  32. package/src/step/planStart.ts +6 -1
  33. package/src/testkit/TestRepo.test.ts +47 -2
  34. package/src/testkit/TestRepo.ts +51 -6
  35. package/src/twins/EmittedScriptRecognizer.ts +5 -8
  36. package/src/twins/FlowScripts.ts +1 -1
  37. package/src/wire/Land.ts +1 -1
  38. package/src/workflow/index.ts +1 -0
  39. package/src/workflow/load.test.ts +138 -113
  40. package/src/workflow/load.ts +7 -7
  41. package/src/workflow/skills.test.ts +5 -5
  42. package/src/workflow/skills.ts +1 -1
  43. package/src/workflow/vars.test.ts +30 -3
  44. package/src/workflow/vars.ts +34 -2
  45. package/src/workflow-kit/index.ts +1 -1
  46. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  47. package/dist/chunks/review-Bfaror38.mjs +0 -427
  48. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  49. package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
  50. package/dist/workflows/cleanup.mjs +0 -36
  51. package/dist/workflows/feature.mjs +0 -1018
  52. package/dist/workflows/fix.mjs +0 -41
  53. package/dist/workflows/review.mjs +0 -45
  54. package/dist/workflows/rollout.mjs +0 -48
  55. package/dist/workflows.mjs +0 -40651
  56. package/src/install/ConfigScaffold.ts +0 -22
  57. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  58. package/src/workflows/cleanup/cleanup.feature +0 -315
  59. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  60. package/src/workflows/cleanup/cleanup.ts +0 -52
  61. package/src/workflows/cleanup/index.ts +0 -6
  62. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  63. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  64. package/src/workflows/feature/criteria-coverage.feature +0 -102
  65. package/src/workflows/feature/criteria.test.ts +0 -147
  66. package/src/workflows/feature/criteria.ts +0 -267
  67. package/src/workflows/feature/fast-path.feature +0 -93
  68. package/src/workflows/feature/feature.test.ts +0 -338
  69. package/src/workflows/feature/feature.ts +0 -156
  70. package/src/workflows/feature/green.ts +0 -19
  71. package/src/workflows/feature/index.ts +0 -18
  72. package/src/workflows/feature/lens-models.feature +0 -69
  73. package/src/workflows/feature/lens-review.feature +0 -566
  74. package/src/workflows/feature/naive-acceptance.feature +0 -147
  75. package/src/workflows/feature/naive-test.feature +0 -263
  76. package/src/workflows/feature/one-step-land.feature +0 -93
  77. package/src/workflows/feature/planning.ts +0 -41
  78. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  79. package/src/workflows/feature/refactor-first.feature +0 -158
  80. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  81. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  82. package/src/workflows/feature/refactor-review.feature +0 -1528
  83. package/src/workflows/feature/refactor.test.ts +0 -1220
  84. package/src/workflows/feature/refactor.ts +0 -368
  85. package/src/workflows/feature/release-flag.feature +0 -167
  86. package/src/workflows/feature/release-flag.test.ts +0 -49
  87. package/src/workflows/feature/release-flag.ts +0 -46
  88. package/src/workflows/feature/review-gate.feature +0 -241
  89. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  90. package/src/workflows/feature/risk-tier.feature +0 -351
  91. package/src/workflows/feature/seam-rejection.feature +0 -146
  92. package/src/workflows/feature/seam.feature +0 -297
  93. package/src/workflows/feature/seam.test.ts +0 -527
  94. package/src/workflows/feature/seam.ts +0 -426
  95. package/src/workflows/feature/standards.feature +0 -354
  96. package/src/workflows/feature/steps.test.ts +0 -65
  97. package/src/workflows/feature/steps.ts +0 -45
  98. package/src/workflows/feature/tier-pin.feature +0 -66
  99. package/src/workflows/feature/tier.test.ts +0 -166
  100. package/src/workflows/feature/tier.ts +0 -56
  101. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  102. package/src/workflows/fix/fix-quality.feature +0 -212
  103. package/src/workflows/fix/fix.feature +0 -79
  104. package/src/workflows/fix/fix.test.ts +0 -74
  105. package/src/workflows/fix/fix.ts +0 -52
  106. package/src/workflows/fix/index.ts +0 -6
  107. package/src/workflows/index.ts +0 -47
  108. package/src/workflows/review/index.ts +0 -6
  109. package/src/workflows/review/review-door.feature +0 -199
  110. package/src/workflows/review/review.test.ts +0 -27
  111. package/src/workflows/review/review.ts +0 -60
  112. package/src/workflows/rollout/index.ts +0 -6
  113. package/src/workflows/rollout/rollout.feature +0 -510
  114. package/src/workflows/rollout/rollout.test.ts +0 -263
  115. package/src/workflows/rollout/rollout.ts +0 -82
  116. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  117. package/src/workflows/shared/access.test.ts +0 -62
  118. package/src/workflows/shared/access.ts +0 -24
  119. package/src/workflows/shared/config.fixture.ts +0 -12
  120. package/src/workflows/shared/fences.ts +0 -24
  121. package/src/workflows/shared/health.test.ts +0 -148
  122. package/src/workflows/shared/health.ts +0 -191
  123. package/src/workflows/shared/learn.test.ts +0 -140
  124. package/src/workflows/shared/learn.ts +0 -201
  125. package/src/workflows/shared/lenses.test.ts +0 -39
  126. package/src/workflows/shared/lenses.ts +0 -294
  127. package/src/workflows/shared/options.test.ts +0 -157
  128. package/src/workflows/shared/options.ts +0 -92
  129. package/src/workflows/shared/progress.feature +0 -124
  130. package/src/workflows/shared/prose.ts +0 -263
  131. package/src/workflows/shared/recheck.feature +0 -60
  132. package/src/workflows/shared/registry.test.ts +0 -345
  133. package/src/workflows/shared/registry.ts +0 -183
  134. package/src/workflows/shared/reserved.ts +0 -11
  135. package/src/workflows/shared/review.test.ts +0 -1062
  136. package/src/workflows/shared/review.ts +0 -446
  137. package/src/workflows/shared/skills.test.ts +0 -121
  138. package/src/workflows/shared/skills.ts +0 -49
  139. package/src/workflows/shared/standards.test.ts +0 -90
  140. package/src/workflows/shared/standards.ts +0 -156
  141. package/src/workflows/shared/steps.test.ts +0 -219
  142. package/src/workflows/shared/steps.ts +0 -201
  143. package/src/workflows/shared/tail.test.ts +0 -454
  144. package/src/workflows/shared/tail.ts +0 -264
  145. package/src/workflows/shared/text.fixture.ts +0 -128
  146. package/src/workflows/shared/text.test.ts +0 -1430
  147. package/src/workflows/shared/text.ts +0 -1700
  148. package/src/workflows/shared/threads.test.ts +0 -129
  149. package/src/workflows/shared/vars.ts +0 -47
  150. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -1,124 +0,0 @@
1
- @inmem
2
- Feature: a fix loop still making progress at the cap gets one extra turn
3
-
4
- `healthy` escalates once three fix turns are spent. When `health.judge`
5
- calls that round progress — still red, but closer to green — with at least
6
- `judgeIdenticalMinP` confidence, one more fix runs first, once per run of
7
- fixes. The round after it escalates without asking the judge. Any other
8
- verdict escalates at the cap as before.
9
-
10
- Background:
11
- Given a gtd config with:
12
- """
13
- import { healthy } from "@pmelab/gtd/workflows"
14
- import { agent } from "@pmelab/gtd/flows"
15
-
16
- export const defaults = { judgeIdenticalMinP: "0.7" }
17
- export const held = async () => {
18
- await agent("first", "go")
19
- await healthy(() => agent("fix", "go"))
20
- }
21
- export default held
22
- """
23
- And step "first" writes "src/a.ts" with:
24
- """
25
- export const a = 1
26
- """
27
- And step "health.check" writes ".gtd/FEEDBACK.md" with:
28
- """
29
- tests failing
30
- """
31
- And step "fix" deletes ".gtd/FEEDBACK.md"
32
- And step "health.recheck" writes ".gtd/FEEDBACK.md" with:
33
- """
34
- tests still failing
35
- """
36
-
37
- Scenario: progress at the cap buys exactly one extra fix turn
38
- Given judge step "health.judge" answers:
39
- """
40
- [{"id":"verdict","answer":"progress","p":0.9}]
41
- """
42
- When gtd starts workflow "held"
43
- And the process is driven
44
- Then the process rests at "health.describe"
45
- And the commit subjects end with:
46
- """
47
- gtd(agent): first → health.check
48
- gtd(check): health.check → fix
49
- gtd(agent): fix → health.check
50
- gtd(check): health.check → health.judge
51
- gtd(judge): health.judge → fix
52
- gtd(agent): fix → health.check
53
- gtd(check): health.check → health.judge
54
- gtd(judge): health.judge → fix
55
- gtd(agent): fix → health.check
56
- gtd(check): health.check → health.judge
57
- gtd(judge): health.judge → fix
58
- gtd(agent): fix → health.check
59
- gtd(check): health.check → health.recheck
60
- gtd(check): health.recheck → health.describe
61
- """
62
-
63
- Scenario Outline: <verdict> at <p> escalates at the cap without an extra turn
64
- Given judge step "health.judge" answers:
65
- """
66
- [{"id":"verdict","answer":"<verdict>","p":<p>}]
67
- """
68
- When gtd starts workflow "held"
69
- And the process is driven
70
- Then the process rests at "health.describe"
71
- And the commit subjects end with:
72
- """
73
- gtd(agent): first → health.check
74
- gtd(check): health.check → fix
75
- gtd(agent): fix → health.check
76
- gtd(check): health.check → health.judge
77
- gtd(judge): health.judge → fix
78
- gtd(agent): fix → health.check
79
- gtd(check): health.check → health.judge
80
- gtd(judge): health.judge → fix
81
- gtd(agent): fix → health.check
82
- gtd(check): health.check → health.judge
83
- gtd(judge): health.judge → health.recheck
84
- gtd(check): health.recheck → health.describe
85
- """
86
-
87
- Examples:
88
- | verdict | p |
89
- | new-failure | 0.9 |
90
- | progress | 0.5 |
91
-
92
- Scenario: a person's escalation stop starts a new run of fixes with its own extra turn
93
- Given judge step "health.judge" answers:
94
- """
95
- [{"id":"verdict","answer":"progress","p":0.9}]
96
- """
97
- And step "health.describe" writes ".gtd/ESCALATION.md" with:
98
- """
99
- try something else
100
- """
101
- And step "health.stop" writes "NOTES.md" with:
102
- """
103
- narrowed
104
- """
105
- When gtd starts workflow "held"
106
- And the process is driven
107
- Then the process rests at "health.exhausted"
108
- And the commit subjects end with:
109
- """
110
- gtd(agent): health.describe → health.stop
111
- gtd(human): health.stop → fix
112
- gtd(agent): fix → health.check
113
- gtd(check): health.check → health.judge
114
- gtd(judge): health.judge → fix
115
- gtd(agent): fix → health.check
116
- gtd(check): health.check → health.judge
117
- gtd(judge): health.judge → fix
118
- gtd(agent): fix → health.check
119
- gtd(check): health.check → health.judge
120
- gtd(judge): health.judge → fix
121
- gtd(agent): fix → health.check
122
- gtd(check): health.check → health.recheck
123
- gtd(check): health.recheck → health.exhausted
124
- """
@@ -1,263 +0,0 @@
1
- // Prose the bundled workflow's prompts share: its voice, its personas and
2
- // the rules several steps repeat. A workflow that wants different words
3
- // writes its own prompts.
4
-
5
- export const skillsPreamble = `- Load whatever's listed here that your harness actually has installed,
6
- skip anything it doesn't — silently, never stopping or asking about a
7
- missing one: {skills}
8
- - A loaded skill offers technique, never authority: this state's own file
9
- format and its own completion condition are the final word over
10
- anything a skill's instructions say, regardless of which one this
11
- prompt states first
12
- - Never let a loaded skill turn this turn interactive — answer nothing,
13
- ask nothing; this runs unattended, with no one at a keyboard`
14
-
15
- /** One bullet per restricted side; empty when neither is. An empty glob list reads "nothing". */
16
- export const accessPreamble = (access: {
17
- readonly read: readonly string[] | null
18
- readonly write: readonly string[] | null
19
- }): string => {
20
- const globs = (list: readonly string[]): string =>
21
- list.length === 0 ? "nothing" : list.join(", ")
22
- return [
23
- ...(access.read === null ? [] : [`- This turn may read only: ${globs(access.read)}`]),
24
- ...(access.write === null
25
- ? []
26
- : [
27
- `- This turn may write only: ${globs(access.write)} — anything else is refused when the turn lands`,
28
- ]),
29
- ].join("\n")
30
- }
31
-
32
- export const styleBlock = `- A deliverable, not a chat reply — size follows the work; cut padding
33
- - Lead with the answer; never circle back to restate it
34
- - Flat, commanding sentences — commit to the claim, never hedge
35
- - Everyday words; define an unavoidable term in five words or fewer, on
36
- first use
37
- - Bold carries the load: bold the claim, not the sentence around it —
38
- the bold text alone must yield the full point and every risk
39
- - **Never trim a risk, a number, a threshold, or a scoped condition to
40
- save space — this outranks every other rule here**
41
- - Ship the artifact bare — no lead-in, no sign-off
42
- - Compressing is not dropping: three load-bearing parts ship as three,
43
- each shorter, never as two
44
- - One idea per block; break when the idea shifts
45
- - Flag risk in one blunt line, never hedged prose; never narrate — do it`
46
-
47
- export const styleFormatContract = `- Machine-read: its format contract outranks every style rule above —
48
- keep every \`##\`/\`###\` heading, checkbox row, and marker line exactly
49
- as specified. Never renumber or rename a heading; a parser reads
50
- these literally and a violation refuses the turn
51
- - Voice rules above govern only the prose between these elements`
52
-
53
- export const agentConduct = `- You have shell and file tools — bash, read, write, edit — use them
54
- without asking first; this runs unattended, no one grants permission
55
- - Investigate with real commands rather than assuming a file's, a
56
- commit's, or a decision's status — then act on what you find
57
- - No injected status block — no cwd, no branch, no history, no tree
58
- state — orient yourself first with \`git status\`, \`git log\`, \`ls\`
59
- - The turn's message names a commit, a range, or a file to go inspect
60
- yourself — never a diff or summary inlined into the conversation
61
- - Across turns one conversation may span, work from what you already
62
- read and settled, unless told this is a fresh, cold pass
63
- - Your turn ends when you return, and nothing you leave running survives
64
- it in any useful way — never detach or background work you intend to
65
- act on, never end a turn planning to "check back": no later turn
66
- inherits your intent. Run it in the foreground and wait, however long
67
- it takes. If it genuinely cannot finish inside one turn, write down
68
- where it stands and what to resume, so the tree carries the state`
69
-
70
- export const designPersona = `You are the product-facing planning voice in gtd's build pipeline: turn
71
- a raw sketch — a hand-edit, a scratch note, or both — into an ordered,
72
- classified list of concerns, and hold the running product conversation
73
- with the human it depends on. You are read by that human — write
74
- plainly — not by a parser, except where a state says otherwise.`
75
-
76
- export const reviewerPersona = `You are the independent reviewing mind in gtd's build pipeline —
77
- deliberately separate from whoever wrote the code, with no attachment
78
- to it. Write a structured review document grouping a diff into
79
- chunks; classify a round of the human's feedback as actionable or
80
- just approving; answer the human's questions inline in the review;
81
- and, when asked, fix the small nits the human flagged and the risks
82
- you marked yourself, each in one batch.
83
- Beyond that you never fix or build anything yourself.`
84
-
85
- export const builderPersona = `You are the TDD implementer in gtd's build pipeline: build the
86
- task in front of you end to end, tests first, and come back to fix
87
- things when redirected — a failing check, or reviewer feedback. Stay
88
- strictly inside that task; never touch code outside it. Treat feedback as the
89
- diagnosis to act on; discard it, with reason, only when simply wrong.`
90
-
91
- export const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
92
- before it closes. Fix a late-breaking failing check after sign-off,
93
- focused and minimal. Every turn lands its own commit — nothing here
94
- gets squashed away.`
95
-
96
- export const escalationPersona = `You are the diagnosing voice in gtd's build pipeline, called in once a
97
- check has stayed red past repeated fix attempts. Read the failing
98
- output, the previous round's, and the code those attempts touched, then
99
- write down what is failing, why the earlier attempts didn't resolve it,
100
- and concrete approaches worth trying next. Never fix the code yourself —
101
- naming the problem precisely enough for the next fix turn is the whole
102
- job.`
103
-
104
- export const stateFileRules = `- You are an autonomous coding agent
105
- - This workflow's own state files are its private scratchpad, never
106
- project code or documentation`
107
-
108
- export const questionBar = `- The goal is shared understanding, not a quota or an empty section: a
109
- question exists to close a gap between what the human wants and
110
- what the agent is about to build. Ask whenever that gap is open —
111
- even when the point looks cheap to undo, settled-looking, or
112
- narrow. This goal outranks what follows; the conditions below are
113
- signals a gap is real, strong evidence to ask, never permission
114
- withheld
115
- - Before writing \`## Open Questions\`, walk every concern you grouped and
116
- collect every point above the bar into that ONE section — a question
117
- held back for a later lap is a bug. One lap is the target; a second is
118
- the exception
119
- - Raise first, narrow second: only once every concern above the bar is
120
- raised into \`## Open Questions\`, walk that same set once more and
121
- answer the ones a confident default settles, moving each into
122
- \`## Answered Questions\` with its answer. This pass narrows what's
123
- already raised — it is never license to raise less, and skipping the
124
- raise to answer straight through is the same bug as holding a
125
- question back for a later lap
126
- - Treat each of these as strong evidence a gap is real: it's a genuine
127
- fork with divergent outcomes (user-visible for product, materially
128
- different builds for technical); the diff and history don't already
129
- settle it (treat an explicit \`.gtd/TODO.md\` directive, a committed
130
- hand-edit, or — on a loop-back lap — the human's own review-round
131
- edit/note as settled); or it would be expensive to undo once
132
- code is written
133
- - Where no gap in shared understanding exists, decide it yourself:
134
- record it under \`## Answered Questions\` as
135
- \`### <the point phrased as a question>\`
136
- plus a one-line rationale in prose, no checkboxes. That heading
137
- always comes last, after every other \`##\` section
138
- - Above the bar, write it under \`## Open Questions\` — always the first
139
- \`##\` section — as \`### <question>\` plus a body plus a checkbox list.
140
- The count of concrete options is your call, taken from how many
141
- genuinely distinct answers the fork has — never padded to a fixed
142
- number. A floor still applies: at least two real options plus the
143
- free-text slot, or the question is not a fork
144
- - The heading is followed by one or two lines of body naming the fork
145
- and what actually differs, THEN the option list. The body frames the
146
- fork; each option's own content carries that option's impacts — they
147
- never repeat each other. A body that lists the options back is
148
- padding, and an option line that restates the fork is padding too
149
- - Each option is one short line plus its own impacts nested under it as
150
- a bullet list: one to four bullets, free-form — what it costs, what
151
- it buys, how it differs from the others. Write the dimensions this
152
- fork actually has and skip the ones it does not; no \`Costs:\`/\`Buys:\`/
153
- \`Differs:\` labels, no fixed bullet count. The free-text slot is last,
154
- mandatory, identified by position rather than by label:
155
-
156
- ### <the question>
157
-
158
- <one or two lines: what the fork is, what actually differs>
159
-
160
- - [ ] <first option — a short line>
161
- - <impact>
162
- - <impact>
163
- - [ ] <second option — a short line>
164
- - <impact>
165
- - [ ] <third option — a short line>
166
- - <impact>
167
- - <impact>
168
- - <impact>
169
- - [ ] _your answer_
170
-
171
- - Never tick a box yourself — the human ticks exactly one per question`
172
-
173
- export const questionBarReturn = `- This lap continues the same goal as the first: close the gap
174
- between what the human wants and what gets built. Folding in
175
- answers and deciding what's left is that same goal continued, not
176
- a different job
177
- - The human answered — a ticked box, a free-text answer in place of
178
- \`_your answer_\`, a deleted question, or a deleted \`## Open Questions\`
179
- section are all answers. Fold each resolved answer into its concern's
180
- prose, then move the question into \`## Answered Questions\` as
181
- \`### <question>\` with the resolved answer in plain prose — no
182
- checkboxes
183
- - \`## Answered Questions\` is always the last \`##\` section — a moved
184
- question lands there, never wherever \`## Open Questions\` used to sit
185
- - Never re-raise a deleted question. A settled \`## Answered Questions\`
186
- entry stays settled, except one carrying a human footnote on its
187
- \`### \` heading: that is the only entry that may change. Fold the note
188
- in, then either rewrite the recorded answer in place (still under
189
- \`## Answered Questions\`) or, when the note leaves the point genuinely
190
- open, move it back to \`## Open Questions\` as a fresh fork with
191
- options and the \`_your answer_\` slot. A note that only asks a
192
- question follows the footnote rule: one \`- A:\` reply, answer unchanged
193
- - An answer may earn a follow-up: if it opens a genuinely new fork above
194
- the bar — one the answer itself created — raise it as a fresh \`##
195
- Open Questions\` entry on this same lap. Never restate a question
196
- already asked, and never treat this as licence to re-open a question
197
- already settled under \`## Answered Questions\` that carries no
198
- footnote on its heading
199
- - Recognise a silent lap from \`## Open Questions\` still present with
200
- nothing ticked and nothing else changed — the human's way of saying
201
- the gap is already closed. That lap ends the questions, whatever the
202
- goal says: decide every remaining question yourself, move each to
203
- \`## Answered Questions\` with a one-line rationale, raise nothing new,
204
- and leave no \`## Open Questions\` section behind`
205
-
206
- export const fixFeedbackPrompt = `- The only state file this turn writes is \`.gtd/FEEDBACK.md\` — no other
207
- files for notes or output
208
- - Read \`.gtd/FEEDBACK.md\` (the failing test output) and fix the code so
209
- the suite passes
210
- - When \`.gtd/ESCALATION.md\` is present, it is a human-reviewed analysis
211
- of why earlier attempts failed — read it and treat it as the primary
212
- instruction for this turn. Never edit or delete it yourself, even once
213
- you believe you've resolved it: only a genuinely green check retires
214
- it, so an attempt that turns out to be wrong still leaves the next
215
- turn's instruction in place. Its absence is the ordinary case: an
216
- unremarkable red round with nothing to escalate yet
217
- - Delete \`.gtd/FEEDBACK.md\` either way — once the suite passes, or once
218
- you have established the feedback was wrong; leaving it in place is
219
- never the right end state, the next check writes its own`
220
-
221
- export const footnoteRules = `- Leave a footnote anywhere: mark the exact spot with \`[^name]\` (any
222
- name, no whitespace or \`]\`), then define it below as \`[^name]:
223
- explain what you mean\` — indent continuation lines by 4 spaces or
224
- more. (A newly seeded definition starts with an empty body — a
225
- definition whose body is still empty or whitespace-only is flagged
226
- as unfilled, so write your own words there; words left under-indented
227
- are flagged separately, naming the indent as the fix)
228
- - A footnote is a comment on that exact spot — the hunk, line, or
229
- paragraph it marks — never a whole-file remark
230
- - \`name\` is yours to pick; only a definition's name must be unique in
231
- this document — the same name may mark more than one spot
232
- - To start a conversation, open the definition with a list: \`[^name]:\`
233
- then an indented \`- H: <your question>\`. The agent answers with
234
- \`- A: <reply>\`. Reply with a new \`- H:\` entry to continue; to
235
- close the thread, reply with a conclusion (the agent folds it in and
236
- deletes the thread) or delete the thread yourself. Entries alternate
237
- \`H:\`/\`A:\` and the agent never starts a thread
238
- - While a thread's last entry is the agent's, it is open and moving on
239
- is refused: reply with a conclusion, or delete it
240
- - The same conversation works as \`// H: <question>\` comment lines in a
241
- file changed in this process (\`#\`, \`--\` or \`;\` by language); the phone UI does not show them,
242
- and \`gtd check --open-threads\` lists the open ones`
243
-
244
- export const footnoteAnchor = `- A footnote (\`[^name]\` plus its \`[^name]:\` definition) is a comment on
245
- its exact anchor — fold it in as a mandatory concern described
246
- against that anchor's hunk or paragraph, never flattened into a
247
- whole-file remark
248
- `
249
-
250
- export const footnoteFoldIn = `${footnoteAnchor}- A one-shot footnote needs no reply: fold it in and DELETE it in this
251
- same turn — marker and definition together. Never re-read a footnote
252
- already acted on. A one-shot footnote that asks a question becomes a
253
- thread instead: rewrite its definition as \`- H: <its body>\` then
254
- \`- A: <your reply>\`, keeping the marker
255
- - A thread is a definition whose body is a list of \`- H:\`/\`- A:\`
256
- entries. Handle each by its last entry:
257
- - last is \`- H:\` and asks something — reply with exactly one
258
- \`- A: <reply>\` entry and change nothing else for it
259
- - last concludes or advises — fold it in like a one-shot footnote,
260
- then delete marker and definition
261
- - last is \`- A:\` — it waits on the human; leave it alone
262
- - Never start a thread yourself, and never leave a thread whose last
263
- entry is \`- H:\` — a turn that does is refused`
@@ -1,60 +0,0 @@
1
- @inmem
2
- Feature: a red check is re-run once before it reaches a person
3
-
4
- Before the fix loop escalates, `healthy` re-runs the suite as
5
- `health.recheck`. Green there ends the loop as if the check had passed, and
6
- the re-check's landing records `Gtd-Mark: flaky=health.check`. Red again
7
- escalates as before.
8
-
9
- Background:
10
- Given a gtd config with:
11
- """
12
- import { healthy } from "@pmelab/gtd/workflows"
13
- import { agent, human } from "@pmelab/gtd/flows"
14
-
15
- export const held = async () => {
16
- await agent("first", "go")
17
- await healthy(() => agent("fix", "go"))
18
- await human("done")
19
- }
20
- export default held
21
- """
22
- And step "first" writes "src/a.ts" with:
23
- """
24
- export const a = 1
25
- """
26
- And step "health.check" writes ".gtd/FEEDBACK.md" with:
27
- """
28
- 1 test failing
29
- """
30
- And step "fix" deletes ".gtd/FEEDBACK.md"
31
- And judge step "health.judge" answers:
32
- """
33
- [{"id":"verdict","answer":"progress","p":0.9}]
34
- """
35
-
36
- Scenario: a re-check that passes ends the loop without a person, recorded as flaky
37
- Given step "health.recheck" deletes ".gtd/FEEDBACK.md"
38
- When gtd starts workflow "held"
39
- And the process is driven
40
- Then the process rests at "done"
41
- And the commit subjects end with:
42
- """
43
- gtd(agent): fix → health.check
44
- gtd(check): health.check → health.judge
45
- gtd(judge): health.judge → health.recheck
46
- gtd(check): health.recheck → done
47
- """
48
- And the last commit body contains "Gtd-Mark: flaky=health.check"
49
- And ".gtd/FEEDBACK.md" does not exist
50
-
51
- Scenario: a re-check that stays red escalates, with nothing recorded as flaky
52
- Given step "health.recheck" writes ".gtd/FEEDBACK.md" with:
53
- """
54
- 1 test still failing
55
- """
56
- When gtd starts workflow "held"
57
- And the process is driven
58
- Then the process rests at "health.describe"
59
- And the last commit subject is "gtd(check): health.recheck → health.describe"
60
- And no commit body contains "Gtd-Mark: flaky="