@pmelab/gtd 50.0.0 → 51.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 (55) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +32 -24
  3. package/dist/chunks/{reserved-CdOzKFVX.mjs → reserved-CUkk9XgL.mjs} +93 -4
  4. package/dist/chunks/{review-BChXEl2Y.mjs → review-BaCXlIT2.mjs} +2 -2
  5. package/dist/chunks/{tail-DRM2eC1z.mjs → tail-CX6Pdsql.mjs} +1 -1
  6. package/dist/gtd.bundle.mjs +26 -25
  7. package/dist/testkit.mjs +26 -25
  8. package/dist/workflows/cleanup.mjs +2 -2
  9. package/dist/workflows/feature.mjs +144 -11
  10. package/dist/workflows/fix.mjs +2 -2
  11. package/dist/workflows/review.mjs +3 -3
  12. package/dist/workflows/rollout.mjs +1 -1
  13. package/dist/workflows.mjs +234 -12
  14. package/package.json +1 -1
  15. package/src/edge/Edge.ts +1 -30
  16. package/src/replay/Replay.test.ts +4 -8
  17. package/src/replay/Replay.ts +35 -3
  18. package/src/replay/index.ts +7 -1
  19. package/src/workflows/feature/coverage.feature +1 -0
  20. package/src/workflows/feature/fast-path.feature +1 -0
  21. package/src/workflows/feature/feature.ts +4 -1
  22. package/src/workflows/feature/flag-mode.feature +1 -1
  23. package/src/workflows/feature/lens-models.feature +1 -0
  24. package/src/workflows/feature/lens-review.feature +1 -0
  25. package/src/workflows/feature/one-step-land.feature +1 -0
  26. package/src/workflows/feature/prototype-rounds.feature +152 -0
  27. package/src/workflows/feature/prototype.feature +419 -0
  28. package/src/workflows/feature/prototype.test.ts +43 -0
  29. package/src/workflows/feature/prototype.ts +181 -0
  30. package/src/workflows/feature/refactor-absorbs.feature +9 -1
  31. package/src/workflows/feature/refactor-first.feature +13 -3
  32. package/src/workflows/feature/refactor-rebuild.feature +29 -5
  33. package/src/workflows/feature/refactor-review.feature +111 -13
  34. package/src/workflows/feature/refactor.test.ts +14 -4
  35. package/src/workflows/feature/refactor.ts +2 -0
  36. package/src/workflows/feature/review-answers.feature +1 -0
  37. package/src/workflows/feature/review-gate.feature +1 -0
  38. package/src/workflows/feature/reviewer-walkthrough.feature +1 -0
  39. package/src/workflows/feature/seam-rejection.feature +1 -0
  40. package/src/workflows/feature/seam.feature +1 -0
  41. package/src/workflows/feature/seam.test.ts +3 -2
  42. package/src/workflows/feature/seam.ts +19 -5
  43. package/src/workflows/feature/severity-budget.feature +1 -0
  44. package/src/workflows/feature/severity-sorted-review.feature +1 -0
  45. package/src/workflows/feature/slice-findings.feature +1 -0
  46. package/src/workflows/feature/slicing-cap.feature +9 -0
  47. package/src/workflows/feature/slicing.feature +1 -0
  48. package/src/workflows/feature/standards.feature +1 -0
  49. package/src/workflows/shared/options.ts +1 -0
  50. package/src/workflows/shared/prose.ts +6 -0
  51. package/src/workflows/shared/skills.test.ts +4 -0
  52. package/src/workflows/shared/skills.ts +5 -0
  53. package/src/workflows/shared/steps.ts +1 -0
  54. package/src/workflows/shared/text.ts +104 -3
  55. package/src/workflows/shared/vars.ts +3 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtd",
3
- "version": "50.0.0",
3
+ "version": "51.0.1",
4
4
  "description": "Drive the gtd loop inside Claude Code: agent turns run as subagents, gtd decides every beat",
5
5
  "author": {
6
6
  "name": "Philipp Melab"
package/README.md CHANGED
@@ -545,30 +545,31 @@ noted in steps 3 and 4 below.
545
545
  each led by its tier and reason (`_critical: security, one-way door_`) — the
546
546
  phone shows it on the chunk's card. A chunk the judge is unsure of, or whose
547
547
  evidence the budget cut, is `critical`. On `refactor-first` the refactor
548
- slice gets a review of its own first, composed by gtd from its lap's notes,
549
- before the feature is built; see [the naive pass](#the-naive-pass). **Every
550
- note needs exactly one tick before `gtd land` accepts the review**
551
- (`answer-completeness`; an open `- H:` thread waiting on the agent is the one
552
- exception). Fix as suggested joins the nit batch below, unjudged; won't fix
553
- drops the note and records it as `Gtd-Mark: wontfix=<title>` on the gate's
554
- commit; your own answer is judged like a note. A note you answered is left
555
- out of the fresh review after a nit fix. Land with every note answered and
556
- nothing else changed to approve, or write what is wrong — disagreeing with a
557
- fix or a drop means writing a note of your own. Approving ends the process
558
- (at the refactor review, it moves on to the feature); feedback is judged note
559
- by note, each as `edit`, `question`, `nit` or `praise`. An `edit` sends the
560
- process back to step 2 for re-derived requirements — the built criteria keep
561
- their IDs (reworded in place when their behaviour changes) and new behaviour
562
- gets new ones — then the implementation is amended to meet them, with no new
563
- naive pass or seam judge, every criterion's coverage is judged again, the
564
- quality lap runs, and you review again. At the refactor review an `edit`
565
- re-derives the refactor instead. A `question` is answered inline under your
566
- note and the process stops at the review again — no new plan. A `nit` is
567
- fixed in one batch, the suite must go green (a red one gets fix turns first),
568
- then a fresh review of the change stops at the review again. `praise` is
569
- dropped; a round of only praise signs off. When a round mixes them, questions
570
- are answered and nits fixed first, then the edits are planned — risk: the
571
- planning lap may redo nit fixes it touches.
548
+ slice gets a walkthrough of its own first, covering the whole refactor on
549
+ every round, before the feature is built; see
550
+ [the naive pass](#the-naive-pass). **Every note needs exactly one tick before
551
+ `gtd land` accepts the review** (`answer-completeness`; an open `- H:` thread
552
+ waiting on the agent is the one exception). Fix as suggested joins the nit
553
+ batch below, unjudged; won't fix drops the note and records it as
554
+ `Gtd-Mark: wontfix=<title>` on the gate's commit; your own answer is judged
555
+ like a note. A note you answered is left out of the fresh review after a nit
556
+ fix. Land with every note answered and nothing else changed to approve, or
557
+ write what is wrong — disagreeing with a fix or a drop means writing a note
558
+ of your own. Approving ends the process (at the refactor review, it moves on
559
+ to the feature); feedback is judged note by note, each as `edit`, `question`,
560
+ `nit` or `praise`. An `edit` sends the process back to step 2 for re-derived
561
+ requirements — the built criteria keep their IDs (reworded in place when
562
+ their behaviour changes) and new behaviour gets new ones — then the
563
+ implementation is amended to meet them, with no new naive pass or seam judge,
564
+ every criterion's coverage is judged again, the quality lap runs, and you
565
+ review again. At the refactor review an `edit` re-derives the refactor
566
+ instead. A `question` is answered inline under your note and the process
567
+ stops at the review again — no new plan. A `nit` is fixed in one batch, the
568
+ suite must go green (a red one gets fix turns first), then a fresh review of
569
+ the change stops at the review again. `praise` is dropped; a round of only
570
+ praise signs off. When a round mixes them, questions are answered and nits
571
+ fixed first, then the edits are planned — risk: the planning lap may redo nit
572
+ fixes it touches.
572
573
 
573
574
  Each `edit` note also gets a severity tier from four judge questions
574
575
  (security, reversibility, blast radius, new concept). Every tier is
@@ -692,6 +693,13 @@ an uncovered one goes back to the agent (twice, then to you) instead of ending
692
693
  the feature, and each covered ID is recorded as a `Gtd-Mark: covers=<id>`
693
694
  trailer. See
694
695
  [Acceptance-criteria coverage](docs/configuration.md#acceptance-criteria-coverage).
696
+ Then an agent uses the slice by hand — the real program in a scratch directory,
697
+ the real page, guided by a project skill or a manual testing section in
698
+ `AGENTS.md`, `CLAUDE.md` or `README.md` — and writes `.gtd/PROTOTYPE.md`: per
699
+ criterion pass, fail or blocked, with the steps run and what it saw. Failures
700
+ are fixed and re-tested before the quality lap, up to `prototypeRounds` (3) fix
701
+ rounds; past them, with only blocked items, or with no way to test found, it
702
+ rests for you. See [The hands-on test](docs/configuration.md#the-hands-on-test).
695
703
  Feedback at the feature review re-runs design and the implementation, never the
696
704
  naive pass; feedback at the refactor review re-runs the refactor, never design;
697
705
  a refactor-review change the refactor cannot build is refused, and the only way
@@ -243,6 +243,7 @@ const defaults = {
243
243
  qualityReviews: "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
244
244
  refactorQualityReviews: "correctness, ponytail-review, conventions",
245
245
  continuations: "3",
246
+ prototypeRounds: "3",
246
247
  continuationStall: "2"
247
248
  };
248
249
  /** Environment settings. */
@@ -320,6 +321,11 @@ task in front of you end to end, tests first, and come back to fix
320
321
  things when redirected — a failing check, or reviewer feedback. Stay
321
322
  strictly inside that task; never touch code outside it. Treat feedback as the
322
323
  diagnosis to act on; discard it, with reason, only when simply wrong.`;
324
+ const testerPersona = `You are the hands-on tester in gtd's build pipeline: use what was just
325
+ built the way a person would — run the real program, open the real
326
+ page, call the real endpoint — and report exactly what you did and
327
+ what you saw. Never fix anything yourself, and never pass a criterion
328
+ you did not watch hold.`;
323
329
  const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
324
330
  before it closes. Fix a late-breaking failing check after sign-off,
325
331
  focused and minimal. Every turn lands its own commit — nothing here
@@ -745,8 +751,11 @@ const acceptanceCriteriaBlock = `
745
751
  Whichever lap this is, \`.gtd/REQUIREMENTS.md\` carries a
746
752
  \`## Acceptance Criteria\` section: one \`- AC-<n>: <criterion>\` line
747
753
  per criterion, numbered from \`AC-1\`. They bind every later step —
748
- the build is done when each one holds — so each must be checkable: a
749
- test or a command shows it holds or not. A later lap keeps every ID:
754
+ the build is done when each one holds — so each must be behaviour a
755
+ test can pin: one that fails when it does not hold. A green suite, a
756
+ passing typecheck or lint, a generator or build that runs — the health
757
+ check enforces those on every lap, so none is a criterion; fold what it
758
+ must produce into a behaviour a test can observe. A later lap keeps every ID:
750
759
  reword a criterion in place, append a new one with the next unused
751
760
  number, delete a dropped one — never renumber, never reuse an ID. When
752
761
  the file holds no list (a review loop-back rewrites it), recover the
@@ -854,7 +863,7 @@ const clearCriteriaQuestion = {
854
863
  id: "clear",
855
864
  primitive: "noul",
856
865
  instructions: "state.criteria lists the acceptance criteria the human's sketch (state.sketch) stated, as gtd numbered them. Decide whether they can stand as the binding definition of done, with no design conversation.",
857
- criteria: "yes: every criterion is clear and checkable — a test or a command can show it holds or not — and together they say what to build. no: any one criterion is vague, subjective or untestable, or the sketch leaves an open product question. Unsure means no."
866
+ criteria: "yes: every criterion is clear behaviour a test can pin — a test would fail if it did not hold — and together they say what to build. no: any one criterion is vague, subjective or untestable, only a command can check it (a suite, typecheck, lint, build or generator run that passes), or the sketch leaves an open product question. Unsure means no."
858
867
  };
859
868
  const clearCriteriaMessage = () => `The sketch stated its own acceptance criteria. Run \`gtd judge answer\` and
860
869
  pipe \`yes\` when every one is clear and checkable, so design is skipped and
@@ -1773,6 +1782,80 @@ Two ways forward (then run \`gtd land\`):
1773
1782
  - **Overrule** — land a clean tree to finish without them. The slice records
1774
1783
  the IDs it covers and, as \`uncovered\`, the ones it does not
1775
1784
  `;
1785
+ const testerSystem = () => `${testerPersona}
1786
+
1787
+ ${agentConduct}`;
1788
+ const PROTOTYPE_FORMAT = `- Write \`.gtd/PROTOTYPE.md\`, replacing any earlier report: one \`## \` chunk
1789
+ per criterion, headed \`## AC-<n>: pass\`, \`## AC-<n>: fail\` or
1790
+ \`## AC-<n>: blocked\`, then one chunk per problem you found outside any
1791
+ criterion, headed \`## Problem <short title>: fail\` (\`: pass\` once a
1792
+ re-test shows it fixed). Under each: the exact steps you ran, what you
1793
+ observed (output, a screenshot path, the error), and for a failure what
1794
+ you expected instead
1795
+ - \`blocked\` is for a criterion you could not exercise, or one whose
1796
+ expected behaviour itself looks wrong — say which and why: a person
1797
+ decides it, never a fixer`;
1798
+ const prototypeTestingPrompt = (slice, retest) => `${stateFileRules}
1799
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
1800
+ ${sliceLine(slice)}- The change is built and its suite is green. Now test it by hand: use
1801
+ every acceptance criterion the way a person would and watch what
1802
+ happens. Running the test suite is not this — it already ran
1803
+ - Find how this project is exercised for real, in this order: a project
1804
+ skill that launches or drives it (e.g. \`.claude/skills/*\`); a manual
1805
+ testing or running section in \`AGENTS.md\`, \`CLAUDE.md\` or \`README.md\`;
1806
+ failing those, the project's shape — a browser or Playwright for a web
1807
+ project, the built CLI in a scratch directory for a CLI, a request client
1808
+ for an API
1809
+ - Found none and recognised no shape: write only a chunk headed
1810
+ \`## No testing instructions\` saying what you looked for, and finish —
1811
+ a person is asked; never guess
1812
+ - Test in a disposable place only — a scratch directory outside this
1813
+ repository, a throwaway repository, a dev server on a free port — never
1814
+ against the user's real data or this working tree. Point every tool's
1815
+ output (screenshots, traces, test results) outside the repository too
1816
+ ${retest ? `- This is a re-test: \`.gtd/PROTOTYPE.md\` holds the last round's report and
1817
+ a fix turn or a person has changed the code since. Re-test every failed or blocked criterion and
1818
+ every problem it lists in full, then re-run the passed ones quickly
1819
+ ` : ""}${PROTOTYPE_FORMAT}
1820
+ - Change nothing but \`.gtd/PROTOTYPE.md\` — a turn that does is refused
1821
+ - Leave everything uncommitted and finish your turn — do not commit
1822
+ `;
1823
+ const prototypeFixingPrompt = (slice) => `${stateFileRules}
1824
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
1825
+ ${sliceLine(slice)}${criteriaBinding(true)}
1826
+ - Read \`.gtd/PROTOTYPE.md\`: a hands-on test of the change. Fix every
1827
+ \`fail\` chunk of this change's criteria${slice === void 0 ? "" : " (this slice's only)"} or a problem found
1828
+ beside them, at its cause, with a test that shows it; leave \`blocked\`
1829
+ ones to the person
1830
+ - A \`fail\` the code does not explain (the tester misread what it saw) has
1831
+ nothing to fix: leave it and change nothing for it — the next round re-tests
1832
+ - Leave \`.gtd/PROTOTYPE.md\` as it is: the next round re-tests from it
1833
+ - Run \`${env.testCommand}\` and finish only once it is green
1834
+ - Leave everything uncommitted and finish your turn — do not commit
1835
+ `;
1836
+ const openItems = (items) => items.map((item) => `- ${item.subject}: ${item.verdict}`).join("\n");
1837
+ const prototypeOpenMessage = (items, rounds) => `The hands-on test of this slice still has open items${rounds > 0 ? ` after ${rounds} fix round${rounds === 1 ? "" : "s"}` : ", and none a fixer can take"}:
1838
+ ${openItems(items)}
1839
+
1840
+ \`.gtd/PROTOTYPE.md\` holds the last report: the steps run and what was seen.
1841
+
1842
+ Two ways forward (then run \`gtd land\`):
1843
+ - **Fix** — change the code, or the testing instructions, yourself (the
1844
+ criteria are settled: an edit to them is refused); the suite runs and the
1845
+ slice is tested again, with a fresh round budget
1846
+ - **Overrule** — land a clean tree to go on to the quality lap with these
1847
+ items open
1848
+ `;
1849
+ const prototypeUnguidedMessage = () => `The hands-on test found no way to exercise this project for real — no
1850
+ project skill, no manual testing section in \`AGENTS.md\`, \`CLAUDE.md\` or
1851
+ \`README.md\`, and no project shape it recognised. \`.gtd/PROTOTYPE.md\` says
1852
+ what it looked for.
1853
+
1854
+ Two ways forward (then run \`gtd land\`):
1855
+ - **Instruct** — add testing instructions (a project skill, or a manual
1856
+ testing section); the slice is tested again
1857
+ - **Skip** — land a clean tree to go on to the quality lap untested
1858
+ `;
1776
1859
  //#endregion
1777
1860
  //#region src/workflows/shared/steps.ts
1778
1861
  const FEEDBACK = ".gtd/FEEDBACK.md";
@@ -1784,6 +1867,7 @@ const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md";
1784
1867
  const QUALITY = ".gtd/QUALITY.md";
1785
1868
  const SLICES = ".gtd/SLICES.md";
1786
1869
  const HANDOFF = ".gtd/HANDOFF.md";
1870
+ const PROTOTYPE = ".gtd/PROTOTYPE.md";
1787
1871
  const planner = () => env.plannerModel ?? "";
1788
1872
  const coder = () => env.coderModel ?? "";
1789
1873
  const fixCheck = (rules) => agentWithSkills("fix", buildFixPrompt(rules), {
@@ -2028,6 +2112,7 @@ const ShippedOptions = Struct({
2028
2112
  qualityReviews: setting,
2029
2113
  refactorQualityReviews: setting,
2030
2114
  continuations: setting,
2115
+ prototypeRounds: setting,
2031
2116
  continuationStall: setting,
2032
2117
  testCommand: setting,
2033
2118
  plannerModel: setting,
@@ -2107,6 +2192,10 @@ const skills = (vars) => {
2107
2192
  seam: ["incremental-implementation"],
2108
2193
  implement: ["test-driven-development", "incremental-implementation"],
2109
2194
  "implement.fix.suite": ["debugging-and-error-recovery"],
2195
+ "implement.prototype": [],
2196
+ "implement.prototype.fix": ["test-driven-development", "incremental-implementation"],
2197
+ "implement.prototype.fix.suite": ["debugging-and-error-recovery"],
2198
+ "implement.prototype.health": ["debugging-and-error-recovery"],
2110
2199
  "implement.health": ["debugging-and-error-recovery"],
2111
2200
  ...lensScopes("build.quality", lensesOf(vars.qualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens]),
2112
2201
  ...lensScopes("refactor.build.quality", lensesOf(vars.refactorQualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens])
@@ -2131,4 +2220,4 @@ const steering = {
2131
2220
  [REVIEW]: "review"
2132
2221
  };
2133
2222
  //#endregion
2134
- export { naiveApplyFailure as $, reviewQuality as A, defaults as At, coverageMessage as B, renderStandards as Bt, collecting as C, uncoveredMessage as Ct, fixRisks as D, walkthroughBlock as Dt, fixQuality as E, unwindFailure as Et, builderSystem as F, conflictFor as Ft, designSystem as G, registryEntry as Gt, coveringPrompt as H, flagMentions as Ht, chunkTexts as I, conflictTitle as It, fixReviewNotesPrompt as J, designTriagePrompt as K, requireFlag as Kt, clearCriteriaMessage as L, isConflictTitle as Lt, agentWithSkills as M, testFileGlobs as Mt, buildReviewCollectingPrompt as N, STANDARDS as Nt, planner as O, withLapFindings as Ot, buildReviewTriageMessage as P, conflictBody as Pt, implementFixPrompt as Q, clearCriteriaQuestion as R, oneLine as Rt, coder as S, titledNotes as St, fixNits as T, unwindFailedMessage as Tt, criterionLine as U, flagReferences as Ut, coverageQuestion as V, standardKey as Vt, designGateAnswerMessage as W, flipProblems as Wt, idleMessage as X, handOffAwaitReviewMessage as Y, implementBuildingPrompt as Z, REQUIREMENTS as _, reviewerSystem as _t, flagModeOn as a, rankedReview as at, answerReviewQuestions as b, slicingPrompt as bt, baseline as c, refactorFixPrompt as ct, healthy as d, reviewEditNotesCapture as dt, naivePassPrompt as et, FEEDBACK as f, reviewEditsCapture as ft, REFACTOR_NOTE as g, reviewRankingMessage as gt, REFACTOR as h, reviewNotesCapture as ht, skills as i, qualityTriageMessage as it, reviewing as j, envDefaults as jt, reviewMissing as k, withWalkthrough as kt, gate as l, refactorNotePrompt as lt, QUALITY as m, reviewGateBlockedMessage as mt, summary as n, oversizedMessage as nt, flagShipped as o, refactorBuildingPrompt as ot, HANDOFF as p, reviewFeedbackFixPrompt as pt, finisherSystem as q, access as r, qualityFindingsFile as rt, shipped as s, refactorDroppedRefusal as st, steering as t, normaliseCriteriaScript as tt, guarded as u, refactorRules as ut, REVIEW as v, seamByHandMessage as vt, fixCheck as w, unranked as wt, awaitReview as x, startGateBlockedMessage as xt, SLICES as y, seamSteerMessage as yt, composedReview as z, parseStandards as zt };
2223
+ export { implementFixPrompt as $, reviewMissing as A, unranked as At, composedReview as B, conflictBody as Bt, coder as C, seamByHandMessage as Ct, fixQuality as D, testerSystem as Dt, fixNits as E, startGateBlockedMessage as Et, buildReviewTriageMessage as F, withWalkthrough as Ft, designGateAnswerMessage as G, parseStandards as Gt, coverageQuestion as H, conflictTitle as Ht, builderSystem as I, defaults as It, finisherSystem as J, flagMentions as Jt, designSystem as K, renderStandards as Kt, chunkTexts as L, envDefaults as Lt, reviewing as M, unwindFailure as Mt, agentWithSkills as N, walkthroughBlock as Nt, fixRisks as O, titledNotes as Ot, buildReviewCollectingPrompt as P, withLapFindings as Pt, implementBuildingPrompt as Q, requireFlag as Qt, clearCriteriaMessage as R, testFileGlobs as Rt, awaitReview as S, reviewerSystem as St, fixCheck as T, slicingPrompt as Tt, coveringPrompt as U, isConflictTitle as Ut, coverageMessage as V, conflictFor as Vt, criterionLine as W, oneLine as Wt, handOffAwaitReviewMessage as X, flipProblems as Xt, fixReviewNotesPrompt as Y, flagReferences as Yt, idleMessage as Z, registryEntry as Zt, REFACTOR_NOTE as _, reviewEditsCapture as _t, flagModeOn as a, prototypeOpenMessage as at, SLICES as b, reviewNotesCapture as bt, baseline as c, qualityFindingsFile as ct, healthy as d, refactorBuildingPrompt as dt, naiveApplyFailure as et, FEEDBACK as f, refactorDroppedRefusal as ft, REFACTOR as g, reviewEditNotesCapture as gt, QUALITY as h, refactorRules as ht, skills as i, prototypeFixingPrompt as it, reviewQuality as j, unwindFailedMessage as jt, planner as k, uncoveredMessage as kt, gate as l, qualityTriageMessage as lt, PROTOTYPE as m, refactorNotePrompt as mt, summary as n, normaliseCriteriaScript as nt, flagShipped as o, prototypeTestingPrompt as ot, HANDOFF as p, refactorFixPrompt as pt, designTriagePrompt as q, standardKey as qt, access as r, oversizedMessage as rt, shipped as s, prototypeUnguidedMessage as st, steering as t, naivePassPrompt as tt, guarded as u, rankedReview as ut, REQUIREMENTS as v, reviewFeedbackFixPrompt as vt, collecting as w, seamSteerMessage as wt, answerReviewQuestions as x, reviewRankingMessage as xt, REVIEW as y, reviewGateBlockedMessage as yt, clearCriteriaQuestion as z, STANDARDS as zt };
@@ -1,8 +1,8 @@
1
1
  import { answered, changesSince, head, judge, numeric, read, refuse, requireRevert, restoreScript, run, showScript, vars, writeScript } from "../flows.mjs";
2
2
  import { _ as reviewRisks, m as reviewChunks } from "./steering-DtUVUz9l.mjs";
3
3
  import { removedAssertions, review } from "../workflow-kit.mjs";
4
- import { Bt as renderStandards, C as collecting, D as fixRisks, Dt as walkthroughBlock, Ft as conflictFor, I as chunkTexts, It as conflictTitle, Lt as isConflictTitle, Nt as STANDARDS, P as buildReviewTriageMessage, Pt as conflictBody, Rt as oneLine, St as titledNotes, T as fixNits, Vt as standardKey, at as rankedReview, b as answerReviewQuestions, d as healthy, dt as reviewEditNotesCapture, ft as reviewEditsCapture, gt as reviewRankingMessage, ht as reviewNotesCapture, j as reviewing, k as reviewMissing, kt as withWalkthrough, u as guarded, v as REVIEW, w as fixCheck, wt as unranked, x as awaitReview, z as composedReview, zt as parseStandards } from "./reserved-CdOzKFVX.mjs";
5
- import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-DRM2eC1z.mjs";
4
+ import { A as reviewMissing, At as unranked, B as composedReview, Bt as conflictBody, E as fixNits, F as buildReviewTriageMessage, Ft as withWalkthrough, Gt as parseStandards, Ht as conflictTitle, Kt as renderStandards, L as chunkTexts, M as reviewing, Nt as walkthroughBlock, O as fixRisks, Ot as titledNotes, S as awaitReview, T as fixCheck, Ut as isConflictTitle, Vt as conflictFor, Wt as oneLine, _t as reviewEditsCapture, bt as reviewNotesCapture, d as healthy, gt as reviewEditNotesCapture, qt as standardKey, u as guarded, ut as rankedReview, w as collecting, x as answerReviewQuestions, xt as reviewRankingMessage, y as REVIEW, zt as STANDARDS } from "./reserved-CUkk9XgL.mjs";
5
+ import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-CX6Pdsql.mjs";
6
6
  //#region src/workflows/shared/learn.ts
7
7
  const SUMMARY_MAX = 200;
8
8
  const BANNER = /^_(?:The fixer left this unresolved\.|A fix turn removed this while renaming another finding — check whether it was fixed\.|No detail given\.)_\s*/;
@@ -1,6 +1,6 @@
1
1
  import { answered, head, judge, mark, numeric, read, refuse, removeScript, run, scope, sectionBodies, showScript, vars, writeScript } from "../flows.mjs";
2
2
  import { lensEntriesOf, lensOf, qualityLap, reviewToHandOff } from "../workflow-kit.mjs";
3
- import { A as reviewQuality, D as fixRisks, E as fixQuality, Ot as withLapFindings, Y as handOffAwaitReviewMessage, d as healthy, it as qualityTriageMessage, j as reviewing, k as reviewMissing, m as QUALITY, rt as qualityFindingsFile, u as guarded, v as REVIEW, w as fixCheck, x as awaitReview } from "./reserved-CdOzKFVX.mjs";
3
+ import { A as reviewMissing, D as fixQuality, M as reviewing, O as fixRisks, Pt as withLapFindings, S as awaitReview, T as fixCheck, X as handOffAwaitReviewMessage, ct as qualityFindingsFile, d as healthy, h as QUALITY, j as reviewQuality, lt as qualityTriageMessage, u as guarded, y as REVIEW } from "./reserved-CUkk9XgL.mjs";
4
4
  //#region src/workflows/shared/lenses.ts
5
5
  const NO_FINDINGS = {
6
6
  notes: [],
@@ -38078,6 +38078,28 @@ const memoryScopeOf = (name) => {
38078
38078
  const dot = name.lastIndexOf(".");
38079
38079
  return dot === -1 ? "" : name.slice(0, dot);
38080
38080
  };
38081
+ /** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
38082
+ const inScope = (name, scope) => scope === "" || name === scope || name.startsWith(`${scope}.`);
38083
+ /**
38084
+ * Where the current unbroken run of rests inside `scope`'s subtree began —
38085
+ * a dip into a descendant scope does not break it, a sibling or ancestor
38086
+ * does, and so does a fresh `scope()` call of the same scope.
38087
+ */
38088
+ const scopeRunStart = (trace, scope) => {
38089
+ let start = -1;
38090
+ for (let k = 0; k < trace.length; k++) {
38091
+ if (!inScope(trace[k].memoryScope, scope)) continue;
38092
+ const previous = trace[k - 1];
38093
+ if (!(previous !== void 0 && inScope(previous.memoryScope, scope) && scopeCallOf(previous, scope) === scopeCallOf(trace[k], scope))) start = k;
38094
+ }
38095
+ return start;
38096
+ };
38097
+ /** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
38098
+ const scopeCallOf = (step, scope) => {
38099
+ let call;
38100
+ for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call;
38101
+ return call;
38102
+ };
38081
38103
  var Stop = class extends Error {};
38082
38104
  const short = (hash) => hash.slice(0, 7);
38083
38105
  /** One step's changes; content is read only when a flow asks for it. */
@@ -38337,12 +38359,13 @@ const replay = async (input) => {
38337
38359
  skills: step.skills,
38338
38360
  access: unfoldedAccess.get(step)
38339
38361
  };
38340
- const seen = personas.get(step.memoryScope);
38362
+ const conversation = `${step.memoryScope}#${scopeRunStart(trace, step.memoryScope)}`;
38363
+ const seen = personas.get(conversation);
38341
38364
  if (seen === void 0) {
38342
- personas.set(step.memoryScope, identity);
38365
+ personas.set(conversation, identity);
38343
38366
  return;
38344
38367
  }
38345
- return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in memory scope "${step.memoryScope || "root"}" — one scope is one conversation, and a conversation has one identity`;
38368
+ return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in its conversation in memory scope "${step.memoryScope || "root"}" — a conversation has one identity`;
38346
38369
  };
38347
38370
  const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over";
38348
38371
  const blockerAt = (step) => {
@@ -40110,28 +40133,6 @@ const stepDefOf = (step) => {
40110
40133
  case "human": return messageDef(common, request, step);
40111
40134
  }
40112
40135
  };
40113
- /** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
40114
- const inScope = (name, scope) => scope === "" || name === scope || name.startsWith(`${scope}.`);
40115
- /**
40116
- * Where the current unbroken run of rests inside `scope`'s subtree began —
40117
- * a dip into a descendant scope does not break it, a sibling or ancestor
40118
- * does, and so does a fresh `scope()` call of the same scope.
40119
- */
40120
- const scopeRunStart = (trace, scope) => {
40121
- let start = -1;
40122
- for (let k = 0; k < trace.length; k++) {
40123
- if (!inScope(trace[k].memoryScope, scope)) continue;
40124
- const previous = trace[k - 1];
40125
- if (!(previous !== void 0 && inScope(previous.memoryScope, scope) && scopeCallOf(previous, scope) === scopeCallOf(trace[k], scope))) start = k;
40126
- }
40127
- return start;
40128
- };
40129
- /** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
40130
- const scopeCallOf = (step, scope) => {
40131
- let call;
40132
- for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call;
40133
- return call;
40134
- };
40135
40136
  const ROOT_MEMORY_SCOPE_NAME = "root";
40136
40137
  /**
40137
40138
  * An agent rest's memory key, `<scope>#<hash7>`: the hash is the commit the
package/dist/testkit.mjs CHANGED
@@ -7124,6 +7124,28 @@ const memoryScopeOf = (name) => {
7124
7124
  const dot = name.lastIndexOf(".");
7125
7125
  return dot === -1 ? "" : name.slice(0, dot);
7126
7126
  };
7127
+ /** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
7128
+ const inScope = (name, scope) => scope === "" || name === scope || name.startsWith(`${scope}.`);
7129
+ /**
7130
+ * Where the current unbroken run of rests inside `scope`'s subtree began —
7131
+ * a dip into a descendant scope does not break it, a sibling or ancestor
7132
+ * does, and so does a fresh `scope()` call of the same scope.
7133
+ */
7134
+ const scopeRunStart = (trace, scope) => {
7135
+ let start = -1;
7136
+ for (let k = 0; k < trace.length; k++) {
7137
+ if (!inScope(trace[k].memoryScope, scope)) continue;
7138
+ const previous = trace[k - 1];
7139
+ if (!(previous !== void 0 && inScope(previous.memoryScope, scope) && scopeCallOf(previous, scope) === scopeCallOf(trace[k], scope))) start = k;
7140
+ }
7141
+ return start;
7142
+ };
7143
+ /** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
7144
+ const scopeCallOf = (step, scope) => {
7145
+ let call;
7146
+ for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call;
7147
+ return call;
7148
+ };
7127
7149
  var Stop = class extends Error {};
7128
7150
  const short = (hash) => hash.slice(0, 7);
7129
7151
  /** One step's changes; content is read only when a flow asks for it. */
@@ -7383,12 +7405,13 @@ const replay = async (input) => {
7383
7405
  skills: step.skills,
7384
7406
  access: unfoldedAccess.get(step)
7385
7407
  };
7386
- const seen = personas.get(step.memoryScope);
7408
+ const conversation = `${step.memoryScope}#${scopeRunStart(trace, step.memoryScope)}`;
7409
+ const seen = personas.get(conversation);
7387
7410
  if (seen === void 0) {
7388
- personas.set(step.memoryScope, identity);
7411
+ personas.set(conversation, identity);
7389
7412
  return;
7390
7413
  }
7391
- return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in memory scope "${step.memoryScope || "root"}" — one scope is one conversation, and a conversation has one identity`;
7414
+ return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in its conversation in memory scope "${step.memoryScope || "root"}" — a conversation has one identity`;
7392
7415
  };
7393
7416
  const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over";
7394
7417
  const blockerAt = (step) => {
@@ -9145,28 +9168,6 @@ const stepDefOf = (step) => {
9145
9168
  case "human": return messageDef(common, request, step);
9146
9169
  }
9147
9170
  };
9148
- /** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
9149
- const inScope = (name, scope) => scope === "" || name === scope || name.startsWith(`${scope}.`);
9150
- /**
9151
- * Where the current unbroken run of rests inside `scope`'s subtree began —
9152
- * a dip into a descendant scope does not break it, a sibling or ancestor
9153
- * does, and so does a fresh `scope()` call of the same scope.
9154
- */
9155
- const scopeRunStart = (trace, scope) => {
9156
- let start = -1;
9157
- for (let k = 0; k < trace.length; k++) {
9158
- if (!inScope(trace[k].memoryScope, scope)) continue;
9159
- const previous = trace[k - 1];
9160
- if (!(previous !== void 0 && inScope(previous.memoryScope, scope) && scopeCallOf(previous, scope) === scopeCallOf(trace[k], scope))) start = k;
9161
- }
9162
- return start;
9163
- };
9164
- /** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
9165
- const scopeCallOf = (step, scope) => {
9166
- let call;
9167
- for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call;
9168
- return call;
9169
- };
9170
9171
  const ROOT_MEMORY_SCOPE_NAME = "root";
9171
9172
  /**
9172
9173
  * An agent rest's memory key, `<scope>#<hash7>`: the hash is the commit the
@@ -1,6 +1,6 @@
1
1
  import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
2
- import { At as defaults, Gt as registryEntry, Kt as requireFlag, S as coder, Ut as flagReferences, i as skills, jt as envDefaults, n as summary, o as flagShipped, r as access, t as steering } from "../chunks/reserved-CdOzKFVX.mjs";
3
- import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DRM2eC1z.mjs";
2
+ import { C as coder, It as defaults, Lt as envDefaults, Qt as requireFlag, Yt as flagReferences, Zt as registryEntry, i as skills, n as summary, o as flagShipped, r as access, t as steering } from "../chunks/reserved-CUkk9XgL.mjs";
3
+ import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-CX6Pdsql.mjs";
4
4
  //#region src/workflows/cleanup/cleanup.ts
5
5
  const removingPrompt = (name, registry) => `Clean up the release flag "${name}", which is default-on: delete its entry in ${registry}, and remove every \`GTD_FLAG_${name.toUpperCase()}\` check from the shipped sources. Keep the flag-on path; delete code only the off path reached, including tests that pin the off path.`;
6
6
  const cleanupFlow = async () => {