@pmelab/gtd 50.0.0 → 51.0.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +32 -24
- package/dist/chunks/{reserved-CdOzKFVX.mjs → reserved-CUkk9XgL.mjs} +93 -4
- package/dist/chunks/{review-BChXEl2Y.mjs → review-BaCXlIT2.mjs} +2 -2
- package/dist/chunks/{tail-DRM2eC1z.mjs → tail-CX6Pdsql.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +144 -11
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +234 -12
- package/package.json +1 -1
- package/src/workflows/feature/coverage.feature +1 -0
- package/src/workflows/feature/fast-path.feature +1 -0
- package/src/workflows/feature/feature.ts +4 -1
- package/src/workflows/feature/flag-mode.feature +1 -1
- package/src/workflows/feature/lens-models.feature +1 -0
- package/src/workflows/feature/lens-review.feature +1 -0
- package/src/workflows/feature/one-step-land.feature +1 -0
- package/src/workflows/feature/prototype-rounds.feature +152 -0
- package/src/workflows/feature/prototype.feature +419 -0
- package/src/workflows/feature/prototype.test.ts +43 -0
- package/src/workflows/feature/prototype.ts +181 -0
- package/src/workflows/feature/refactor-absorbs.feature +9 -1
- package/src/workflows/feature/refactor-first.feature +13 -3
- package/src/workflows/feature/refactor-rebuild.feature +29 -5
- package/src/workflows/feature/refactor-review.feature +111 -13
- package/src/workflows/feature/refactor.test.ts +14 -4
- package/src/workflows/feature/refactor.ts +2 -0
- package/src/workflows/feature/review-answers.feature +1 -0
- package/src/workflows/feature/review-gate.feature +1 -0
- package/src/workflows/feature/reviewer-walkthrough.feature +1 -0
- package/src/workflows/feature/seam-rejection.feature +1 -0
- package/src/workflows/feature/seam.feature +1 -0
- package/src/workflows/feature/seam.test.ts +3 -2
- package/src/workflows/feature/seam.ts +19 -5
- package/src/workflows/feature/severity-budget.feature +1 -0
- package/src/workflows/feature/severity-sorted-review.feature +1 -0
- package/src/workflows/feature/slice-findings.feature +1 -0
- package/src/workflows/feature/slicing-cap.feature +9 -0
- package/src/workflows/feature/slicing.feature +1 -0
- package/src/workflows/feature/standards.feature +1 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/prose.ts +6 -0
- package/src/workflows/shared/skills.test.ts +4 -0
- package/src/workflows/shared/skills.ts +5 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.ts +104 -3
- package/src/workflows/shared/vars.ts +3 -0
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
|
|
549
|
-
before the feature is built; see
|
|
550
|
-
note needs exactly one tick before
|
|
551
|
-
(`answer-completeness`; an open `- H:` thread
|
|
552
|
-
exception). Fix as suggested joins the nit
|
|
553
|
-
drops the note and records it as
|
|
554
|
-
commit; your own answer is judged
|
|
555
|
-
out of the fresh review after a nit
|
|
556
|
-
nothing else changed to approve, or
|
|
557
|
-
fix or a drop means writing a note
|
|
558
|
-
(at the refactor review, it moves on
|
|
559
|
-
by note, each as `edit`, `question`,
|
|
560
|
-
process back to step 2 for re-derived
|
|
561
|
-
their IDs (reworded in place when
|
|
562
|
-
gets new ones — then the
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
planning lap may redo nit
|
|
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
|
|
749
|
-
test
|
|
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
|
|
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 {
|
|
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 {
|
|
5
|
-
import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-
|
|
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
|
|
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: [],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
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 () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { agent, answered, applyScript, changes, changesSince, check, codeChanges, env, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, matches, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireThreadsClosed, revertScript, run, scope, slice, start, vars, wrote } from "../flows.mjs";
|
|
2
2
|
import { lensEntriesOf } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as refusePendingHandOff, c as joinReports, o as tail } from "../chunks/tail-
|
|
5
|
-
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-
|
|
3
|
+
import { $ as implementFixPrompt, C as coder, Ct as seamByHandMessage, Dt as testerSystem, E as fixNits, Et as startGateBlockedMessage, G as designGateAnswerMessage, H as coverageQuestion, I as builderSystem, It as defaults, K as designSystem, Lt as envDefaults, Mt as unwindFailure, N as agentWithSkills, P as buildReviewCollectingPrompt, Q as implementBuildingPrompt, R as clearCriteriaMessage, Rt as testFileGlobs, St as reviewerSystem, Tt as slicingPrompt, U as coveringPrompt, V as coverageMessage, W as criterionLine, Z as idleMessage, _ as REFACTOR_NOTE, a as flagModeOn, at as prototypeOpenMessage, b as SLICES, d as healthy, dt as refactorBuildingPrompt, et as naiveApplyFailure, f as FEEDBACK, ft as refactorDroppedRefusal, g as REFACTOR, ht as refactorRules, i as skills, it as prototypeFixingPrompt, jt as unwindFailedMessage, k as planner, kt as uncoveredMessage, l as gate, m as PROTOTYPE, mt as refactorNotePrompt, n as summary, nt as normaliseCriteriaScript, ot as prototypeTestingPrompt, p as HANDOFF, pt as refactorFixPrompt, q as designTriagePrompt, r as access, rt as oversizedMessage, s as shipped, st as prototypeUnguidedMessage, t as steering, tt as naivePassPrompt, v as REQUIREMENTS, w as collecting, wt as seamSteerMessage, x as answerReviewQuestions, z as clearCriteriaQuestion } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, c as joinReports, o as tail } from "../chunks/tail-CX6Pdsql.mjs";
|
|
5
|
+
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-BaCXlIT2.mjs";
|
|
6
6
|
import { posix } from "node:path";
|
|
7
7
|
//#region src/workflows/shared/fences.ts
|
|
8
8
|
const markFenced = (text) => {
|
|
@@ -561,7 +561,9 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
561
561
|
feedback,
|
|
562
562
|
fixAccess: strayAccess(p.strays),
|
|
563
563
|
lensBase: entry,
|
|
564
|
-
rules: refactorRules(p.strays, p.pattern)
|
|
564
|
+
rules: refactorRules(p.strays, p.pattern),
|
|
565
|
+
walkthrough: true,
|
|
566
|
+
lap: "refactor"
|
|
565
567
|
});
|
|
566
568
|
if (outcome.verdict === "signoff") return true;
|
|
567
569
|
await reUnwind(outcome);
|
|
@@ -576,7 +578,7 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
576
578
|
const COVERED_MIN_P = .7;
|
|
577
579
|
/** Covering turns before the process rests at `uncovered`; a human edit there refills them. */
|
|
578
580
|
const COVERING_RETRIES = 2;
|
|
579
|
-
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
581
|
+
const frozen$1 = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
580
582
|
const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
581
583
|
label: "Fixing the check",
|
|
582
584
|
file: FEEDBACK,
|
|
@@ -584,7 +586,7 @@ const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", imple
|
|
|
584
586
|
system: builderSystem()
|
|
585
587
|
}), {
|
|
586
588
|
guard: () => {
|
|
587
|
-
frozen();
|
|
589
|
+
frozen$1();
|
|
588
590
|
guard?.();
|
|
589
591
|
},
|
|
590
592
|
guardRests: false
|
|
@@ -637,7 +639,7 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
637
639
|
system: builderSystem(),
|
|
638
640
|
allowEmpty: true
|
|
639
641
|
});
|
|
640
|
-
frozen();
|
|
642
|
+
frozen$1();
|
|
641
643
|
} else {
|
|
642
644
|
await human("uncovered", {
|
|
643
645
|
message: uncoveredMessage(uncovered),
|
|
@@ -646,13 +648,136 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
646
648
|
acceptClean: true
|
|
647
649
|
});
|
|
648
650
|
if (!changes().some((c) => isCode$1(c.path))) return record(covered, uncovered);
|
|
649
|
-
frozen();
|
|
651
|
+
frozen$1();
|
|
650
652
|
retries = 0;
|
|
651
653
|
}
|
|
652
654
|
await green();
|
|
653
655
|
}
|
|
654
656
|
};
|
|
655
657
|
//#endregion
|
|
658
|
+
//#region src/workflows/feature/prototype.ts
|
|
659
|
+
/** The `prototypeRounds` setting. Anything but a non-negative integer fails rather than guess a cap. */
|
|
660
|
+
const prototypeRounds = () => {
|
|
661
|
+
const value = vars.prototypeRounds ?? defaults.prototypeRounds;
|
|
662
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) refuse(`gtd: prototypeRounds must be a non-negative integer, got ${JSON.stringify(value)}`);
|
|
663
|
+
return Number(value);
|
|
664
|
+
};
|
|
665
|
+
const CHUNK = /^(AC-\d+|Problem\b.*?)\s*:\s*(pass|fail|blocked)$/i;
|
|
666
|
+
const UNGUIDED = /^no testing instructions$/i;
|
|
667
|
+
/** The `## ` chunks of a prototype report; a heading in neither shape is prose, not an item. */
|
|
668
|
+
const reportOf = (text) => {
|
|
669
|
+
let unguided = false;
|
|
670
|
+
const items = [];
|
|
671
|
+
const malformed = [];
|
|
672
|
+
for (const { line, fenced } of markFenced(text)) {
|
|
673
|
+
const heading = fenced ? null : HEADING.exec(line);
|
|
674
|
+
if (heading?.[1] !== "##") continue;
|
|
675
|
+
if (UNGUIDED.test(heading[2])) unguided = true;
|
|
676
|
+
const chunk = CHUNK.exec(heading[2]);
|
|
677
|
+
if (chunk === null && /^(ac-\d|problem)/i.test(heading[2])) malformed.push(heading[2]);
|
|
678
|
+
if (chunk !== null) items.push({
|
|
679
|
+
subject: /^ac-/i.test(chunk[1]) ? chunk[1].toUpperCase() : chunk[1],
|
|
680
|
+
verdict: chunk[2].toLowerCase()
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
unguided,
|
|
685
|
+
items,
|
|
686
|
+
malformed
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
/**
|
|
690
|
+
* The report the testing turn left, refused unless it is the only change and
|
|
691
|
+
* names every criterion once, with its open items: another slice's criteria
|
|
692
|
+
* are not this slice's to fix.
|
|
693
|
+
*/
|
|
694
|
+
const requireReport = (slice, retest) => {
|
|
695
|
+
const others = changes().map((c) => c.path).filter((path) => path !== PROTOTYPE);
|
|
696
|
+
if (others.length > 0) refuse(`gtd land: prototype-report: test in a disposable place and change nothing but ${PROTOTYPE} — revert these:\n${others.map((path) => ` - ${path}`).join("\n")}`);
|
|
697
|
+
const text = read(PROTOTYPE);
|
|
698
|
+
if (text === void 0) return refuse(`gtd land: prototype-report: write ${PROTOTYPE}`);
|
|
699
|
+
const report = reportOf(text);
|
|
700
|
+
if (report.malformed.length > 0) refuse(`gtd land: prototype-report: head each chunk \`## <ID>: pass|fail|blocked\` or \`## Problem <title>: pass|fail|blocked\` — these miss it:\n${report.malformed.map((h) => ` - ${h}`).join("\n")}`);
|
|
701
|
+
if (report.unguided && report.items.length === 0 && !retest) return {
|
|
702
|
+
unguided: true,
|
|
703
|
+
open: []
|
|
704
|
+
};
|
|
705
|
+
const requirements = read(".gtd/REQUIREMENTS.md") ?? "";
|
|
706
|
+
const ids = criterionIds(requirements, slice);
|
|
707
|
+
const elsewhere = criterionIds(requirements).filter((id) => !ids.includes(id));
|
|
708
|
+
const count = (id) => report.items.filter((i) => i.subject === id).length;
|
|
709
|
+
const wrong = ids.filter((id) => count(id) !== 1);
|
|
710
|
+
if (wrong.length > 0) refuse(`gtd land: prototype-report: ${PROTOTYPE} needs exactly one chunk headed \`## <ID>: pass|fail|blocked\` for ${wrong.join(", ")}`);
|
|
711
|
+
return {
|
|
712
|
+
unguided: false,
|
|
713
|
+
open: report.items.filter((i) => i.verdict !== "pass" && !elsewhere.includes(i.subject))
|
|
714
|
+
};
|
|
715
|
+
};
|
|
716
|
+
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
717
|
+
const testing = async (slice, retest) => {
|
|
718
|
+
await agentWithSkills("testing", prototypeTestingPrompt(sliceIds(slice), retest), {
|
|
719
|
+
label: retest ? "Re-testing the slice by hand" : "Testing the slice by hand",
|
|
720
|
+
file: PROTOTYPE,
|
|
721
|
+
model: coder(),
|
|
722
|
+
system: testerSystem(),
|
|
723
|
+
allowEmpty: true
|
|
724
|
+
});
|
|
725
|
+
return requireReport(slice, retest);
|
|
726
|
+
};
|
|
727
|
+
const fixing = async (slice, guard) => {
|
|
728
|
+
await scope("fix", () => agentWithSkills("fixing", prototypeFixingPrompt(sliceIds(slice)), {
|
|
729
|
+
label: "Fixing what the hands-on test found",
|
|
730
|
+
file: PROTOTYPE,
|
|
731
|
+
model: coder(),
|
|
732
|
+
system: builderSystem(),
|
|
733
|
+
allowEmpty: true
|
|
734
|
+
}));
|
|
735
|
+
if (changes().some((c) => c.path === ".gtd/PROTOTYPE.md")) refuse(`gtd land: prototype-fix: leave ${PROTOTYPE} as it is — the next round re-tests from it`);
|
|
736
|
+
frozen();
|
|
737
|
+
guard?.();
|
|
738
|
+
};
|
|
739
|
+
/** A person's rest; resolves `true` when they changed something to test again, `false` on a clean overrule. */
|
|
740
|
+
const askPerson = async (name, message, label) => {
|
|
741
|
+
await human(name, {
|
|
742
|
+
message,
|
|
743
|
+
label,
|
|
744
|
+
file: PROTOTYPE,
|
|
745
|
+
acceptClean: true
|
|
746
|
+
});
|
|
747
|
+
if (!changes().some((c) => c.path !== ".gtd/PROTOTYPE.md")) return false;
|
|
748
|
+
frozen();
|
|
749
|
+
return true;
|
|
750
|
+
};
|
|
751
|
+
/**
|
|
752
|
+
* Use the slice by hand: a testing turn reports on every criterion, a fix turn
|
|
753
|
+
* takes each failure, and the slice is tested again — until the report is
|
|
754
|
+
* clean, or `prototypeRounds` fixes are spent and a person decides. `guard`
|
|
755
|
+
* holds every fix turn, as it holds the implementation's.
|
|
756
|
+
*/
|
|
757
|
+
const prototypeTest = async (slice, guard) => {
|
|
758
|
+
const cap = prototypeRounds();
|
|
759
|
+
if (cap === 0) return;
|
|
760
|
+
await scope("prototype", async () => {
|
|
761
|
+
let fixes = 0;
|
|
762
|
+
let retest = false;
|
|
763
|
+
for (;;) {
|
|
764
|
+
const { unguided, open } = await testing(slice, retest);
|
|
765
|
+
retest = !unguided;
|
|
766
|
+
if (unguided) {
|
|
767
|
+
if (!await askPerson("unguided", prototypeUnguidedMessage(), "No way to test by hand")) return;
|
|
768
|
+
} else if (open.length === 0) return;
|
|
769
|
+
else if (fixes < cap && open.some((i) => i.verdict === "fail")) {
|
|
770
|
+
fixes++;
|
|
771
|
+
await fixing(slice, guard);
|
|
772
|
+
} else {
|
|
773
|
+
if (!await askPerson("open", prototypeOpenMessage(open, fixes), "Hands-on test still failing")) return;
|
|
774
|
+
fixes = 0;
|
|
775
|
+
}
|
|
776
|
+
await green(guard);
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
//#endregion
|
|
656
781
|
//#region src/workflows/feature/seam.ts
|
|
657
782
|
/** Naive-pass retries after a `reject` or a red suite; the next one rests at `seam.steer`. */
|
|
658
783
|
const NAIVE_RETRIES = 2;
|
|
@@ -859,8 +984,10 @@ const implementing = (options) => scope("implement", async () => {
|
|
|
859
984
|
...acceptance,
|
|
860
985
|
ids: settled(acceptance.ids, options.slice)
|
|
861
986
|
}, since);
|
|
862
|
-
|
|
987
|
+
const keepsTests = built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0;
|
|
988
|
+
await green(keepsTests);
|
|
863
989
|
await requireCoverage(options.base, options.slice);
|
|
990
|
+
await prototypeTest(options.slice, keepsTests);
|
|
864
991
|
const ids = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
865
992
|
await closing(moreToBuild(options.more));
|
|
866
993
|
return ids;
|
|
@@ -869,11 +996,16 @@ const moreToBuild = (more) => typeof more === "function" ? more() : more === tru
|
|
|
869
996
|
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
870
997
|
const closeOut = () => scope("implement", () => closing(false));
|
|
871
998
|
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
872
|
-
const closing = (more) => run("closing", removeScript(more ? [
|
|
999
|
+
const closing = (more) => run("closing", removeScript(more ? [
|
|
1000
|
+
REFACTOR_NOTE,
|
|
1001
|
+
HANDOFF,
|
|
1002
|
+
PROTOTYPE
|
|
1003
|
+
] : [
|
|
873
1004
|
REQUIREMENTS,
|
|
874
1005
|
REFACTOR_NOTE,
|
|
875
1006
|
SLICES,
|
|
876
|
-
HANDOFF
|
|
1007
|
+
HANDOFF,
|
|
1008
|
+
PROTOTYPE
|
|
877
1009
|
]), { label: "Closing out the implementation" });
|
|
878
1010
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
879
1011
|
const steer = async (retry) => {
|
|
@@ -1238,6 +1370,7 @@ const ordinaryStart = async () => {
|
|
|
1238
1370
|
refusePendingHandOff();
|
|
1239
1371
|
flagModeOn();
|
|
1240
1372
|
sliceCap();
|
|
1373
|
+
prototypeRounds();
|
|
1241
1374
|
const stated = statedCriteria(changes(), head());
|
|
1242
1375
|
await unwind();
|
|
1243
1376
|
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
package/dist/workflows/fix.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, start } from "../flows.mjs";
|
|
2
2
|
import { _ as reviewRisks, g as reviewNotes } from "../chunks/steering-DtUVUz9l.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { C as coder, It as defaults, J as finisherSystem, Lt as envDefaults, N as agentWithSkills, Y as fixReviewNotesPrompt, c as baseline, i as skills, n as summary, r as access, s as shipped, t as steering, y as REVIEW } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-CX6Pdsql.mjs";
|
|
5
5
|
//#region src/workflows/fix/fix.ts
|
|
6
6
|
/**
|
|
7
7
|
* Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as refusePendingHandOff } from "../chunks/tail-
|
|
4
|
-
import { i as reUnwind, t as buildTail } from "../chunks/review-
|
|
2
|
+
import { C as coder, It as defaults, J as finisherSystem, Lt as envDefaults, N as agentWithSkills, i as skills, l as gate, n as summary, r as access, s as shipped, t as steering, v as REQUIREMENTS, vt as reviewFeedbackFixPrompt, yt as reviewGateBlockedMessage } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
3
|
+
import { a as refusePendingHandOff } from "../chunks/tail-CX6Pdsql.mjs";
|
|
4
|
+
import { i as reUnwind, t as buildTail } from "../chunks/review-BaCXlIT2.mjs";
|
|
5
5
|
//#region src/workflows/review/review.ts
|
|
6
6
|
const fixFeedback = async () => {
|
|
7
7
|
await agentWithSkills("feedback.fixing", reviewFeedbackFixPrompt(), {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agent, changesSince, glob, head, mark, read, refuse, removeScript, run, scope, vars } from "../flows.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { C as coder, It as defaults, Jt as flagMentions, Lt as envDefaults, Qt as requireFlag, Xt as flipProblems, f as FEEDBACK, i as skills, l as gate, n as summary, o as flagShipped, r as access, t as steering } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
3
3
|
//#region src/workflows/rollout/rollout.ts
|
|
4
4
|
const flippingPrompt = (name, registry) => `Roll out the release flag "${name}": in ${registry}, set \`defaultOn: true\` on its entry. Change nothing else — no other entry, no other field, no other file, no comments.`;
|
|
5
5
|
const suiteBlockedMessage = () => "The suite is red after the rollout. Fix it by hand — a rollout may only touch the flag's registry entry — then land.";
|