@pmelab/gtd 39.0.0 → 40.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 +54 -53
- package/dist/chunks/{reserved-B6tTYjeb.mjs → reserved-Qt5FM6Ia.mjs} +7 -5
- package/dist/chunks/{tail-_CZTP7Hu.mjs → tail-tAHx_AHe.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +98 -37
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +103 -40
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -2
- package/src/workflows/feature/coverage.feature +5 -5
- package/src/workflows/feature/criteria.test.ts +20 -1
- package/src/workflows/feature/criteria.ts +24 -0
- package/src/workflows/feature/fast-path.feature +4 -4
- package/src/workflows/feature/feature.ts +82 -22
- package/src/workflows/feature/flag-mode.feature +2 -2
- package/src/workflows/feature/one-step-land.feature +4 -4
- package/src/workflows/feature/planning.ts +3 -2
- package/src/workflows/feature/quality-unresolved.feature +6 -6
- package/src/workflows/feature/refactor-absorbs.feature +3 -3
- package/src/workflows/feature/refactor-first.feature +5 -5
- package/src/workflows/feature/refactor-rebuild.feature +2 -2
- package/src/workflows/feature/refactor-review.feature +8 -8
- package/src/workflows/feature/review-gate.feature +271 -0
- package/src/workflows/feature/seam-rejection.feature +4 -4
- package/src/workflows/feature/seam.feature +2 -2
- package/src/workflows/feature/seam.ts +17 -7
- package/src/workflows/feature/slicing.feature +63 -4
- package/src/workflows/shared/text.ts +7 -5
- package/src/workflows/review/review-door.feature +0 -121
package/README.md
CHANGED
|
@@ -419,10 +419,9 @@ phone UI does not show them).
|
|
|
419
419
|
|
|
420
420
|
### The workflow it ships with
|
|
421
421
|
|
|
422
|
-
One built-in workflow drives all of that. From where you sit, it has
|
|
422
|
+
One built-in workflow drives all of that. From where you sit, it has four
|
|
423
423
|
moments — everything between them runs without you, with the judged exceptions
|
|
424
|
-
noted in
|
|
425
|
-
you open the `review` door.
|
|
424
|
+
noted in steps 3 and 4 below.
|
|
426
425
|
|
|
427
426
|
1. **You sketch.** Change anything, or write the idea into `.gtd/TODO.md`. Rough
|
|
428
427
|
is fine; it is treated as a sketch, not as work.
|
|
@@ -449,14 +448,12 @@ you open the `review` door.
|
|
|
449
448
|
against the full suite (`testCommand`) and fixed until it passes — so keep
|
|
450
449
|
that suite fast; gtd has no lighter one. Scenario wording is not frozen: a
|
|
451
450
|
review's `## Scenarios` chunk marks any assertion an existing scenario lost
|
|
452
|
-
as `Weakened
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
land with a clean tree to accept the conservative default, which never skips
|
|
459
|
-
work; `gtd judge run --provider fixed --answers <path>` — or the
|
|
451
|
+
as `Weakened:`, so you sign the scenarios off at step 4. After the build the
|
|
452
|
+
quality lap runs. Three points along the process (two with flag mode off) are
|
|
453
|
+
judged rather than always asking you outright — each stops and hands you a
|
|
454
|
+
verdict to make (`gtd judge answer`, or land with a clean tree to accept the
|
|
455
|
+
conservative default, which never skips work;
|
|
456
|
+
`gtd judge run --provider fixed --answers <path>` — or the
|
|
460
457
|
`GTD_JUDGE_ANSWERS` env var, inline JSON — answers one from a file, piped
|
|
461
458
|
between `gtd judge --json` and `gtd judge answer`;
|
|
462
459
|
`gtd judge run --provider jev` asks TypeSafe's Jev instead, with the key in
|
|
@@ -474,6 +471,8 @@ you open the `review` door.
|
|
|
474
471
|
or get rejected and retried?
|
|
475
472
|
- Every red round after the first: was the failure identical, new, or
|
|
476
473
|
progress?
|
|
474
|
+
- After you review: is each of your notes an edit, a question, a nit or
|
|
475
|
+
praise?
|
|
477
476
|
|
|
478
477
|
gtd itself never calls a judge: `gtd loop`, the Claude Code mod and the
|
|
479
478
|
reference driver answer these through `gtd judge run` (auto selection: jev
|
|
@@ -485,22 +484,20 @@ you open the `review` door.
|
|
|
485
484
|
and skip a gate unattended.**
|
|
486
485
|
|
|
487
486
|
Once the change is built, the whole change goes through a qualitative review
|
|
488
|
-
lap: six lenses, one turn each, in order —
|
|
489
|
-
`
|
|
490
|
-
`correctness`, `owasp-security` and
|
|
491
|
-
|
|
492
|
-
entry `<lens>=<model>` picks a
|
|
493
|
-
own
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
signs the finished change off unless you open the `review` door** (below).
|
|
503
|
-
The `fix` door repairs a red baseline through this same lap.
|
|
487
|
+
lap before you see anything: six lenses, one turn each, in order —
|
|
488
|
+
`correctness`, `owasp-security`, `ponytail-review`, `test-audit`,
|
|
489
|
+
`conventions`, `spec-challenge`. `correctness`, `owasp-security` and
|
|
490
|
+
`spec-challenge` run on the planner model (`smart`), the other three on the
|
|
491
|
+
coder model (`base`); a `qualityReviews` entry `<lens>=<model>` picks a
|
|
492
|
+
lens's own. Each traces the change from its own angle and records every
|
|
493
|
+
finding, blocking or not; one fix turn then fixes ALL of them once, with no
|
|
494
|
+
re-review after the fix. A clean turn means approval only when that lens
|
|
495
|
+
found nothing at all. This lap is where code quality is looked at, and every
|
|
496
|
+
round pays for it. It never replaces step 4 — your review stays the final
|
|
497
|
+
gate, and nothing here skips it. Findings the fix cap leaves in
|
|
498
|
+
`.gtd/QUALITY.md` rest at `build.quality-unresolved` until you delete or
|
|
499
|
+
empty the file; the suite then runs on any hand edits before the review. The
|
|
500
|
+
`fix` door (below) repairs a red baseline through this same lap.
|
|
504
501
|
|
|
505
502
|
A red suite that keeps failing past a few fix attempts escalates instead of
|
|
506
503
|
retrying forever: an agent turn reads the failing output and writes
|
|
@@ -514,28 +511,30 @@ you open the `review` door.
|
|
|
514
511
|
`Gtd-Mark: flaky=<check>` (see
|
|
515
512
|
[Re-check before a person](docs/configuration.md#re-check-before-a-person)).
|
|
516
513
|
|
|
517
|
-
4. **You review
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
hands notes to `fix` instead of re-planning (below). Either way you get a
|
|
522
|
-
review document listing what changed and what to look at. Before you see it,
|
|
514
|
+
4. **You review.** After the last slice's quality lap you get a review document
|
|
515
|
+
over the whole change, listing what changed and what to look at (on
|
|
516
|
+
`refactor-first` the refactor slice gets one of its own first, before the
|
|
517
|
+
feature is built; see [the naive pass](#the-naive-pass)). Before you see it,
|
|
523
518
|
an automatic risk-fix pass (`build.review.fix.risks.fixing`) runs. Any risk
|
|
524
519
|
the reviewer names (a note opening with `Risk:`) is fixed first, the suite
|
|
525
520
|
kept green, and the review rewritten — once per review round, so a risk the
|
|
526
521
|
rewrite still names reaches you unfixed; risk: a fix lands with no check that
|
|
527
|
-
the risk was real. Tick the boxes to approve, or write what is wrong.
|
|
528
|
-
refactor review,
|
|
529
|
-
|
|
530
|
-
`praise`. An `edit`
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
review
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
522
|
+
the risk was real. Tick the boxes to approve, or write what is wrong.
|
|
523
|
+
Approving ends the process (at the refactor review, it moves on to the
|
|
524
|
+
feature); feedback is judged note by note, each as `edit`, `question`, `nit`
|
|
525
|
+
or `praise`. An `edit` sends the process back to step 2 for re-derived
|
|
526
|
+
requirements — the built criteria keep their IDs (reworded in place when
|
|
527
|
+
their behaviour changes) and new behaviour gets new ones — then the
|
|
528
|
+
implementation is amended to meet them, with no new naive pass or seam judge,
|
|
529
|
+
every criterion's coverage is judged again, the quality lap runs, and you
|
|
530
|
+
review again. At the refactor review an `edit` re-derives the refactor
|
|
531
|
+
instead. A `question` is answered inline under your note and the process
|
|
532
|
+
stops at the review again — no new plan. A `nit` is fixed in one batch, the
|
|
533
|
+
suite must go green (a red one gets fix turns first), then a fresh review of
|
|
534
|
+
the change stops at the review again. `praise` is dropped; a round of only
|
|
535
|
+
praise signs off. When a round mixes them, questions are answered and nits
|
|
536
|
+
fixed first, then the edits are planned — risk: the planning lap may redo nit
|
|
537
|
+
fixes it touches.
|
|
539
538
|
|
|
540
539
|
Each `edit` note also gets a severity tier from four judge questions
|
|
541
540
|
(security, reversibility, blast radius, new concept). `critical` and `should`
|
|
@@ -555,7 +554,7 @@ you open the `review` door.
|
|
|
555
554
|
You never talk to it. Every exchange is a file in `.gtd/` that you edit in your
|
|
556
555
|
own editor, and every answer you give is a commit. Your test suite is the gate
|
|
557
556
|
throughout: a red baseline stops the process before it starts, and a red build
|
|
558
|
-
is fixed before
|
|
557
|
+
is fixed before review.
|
|
559
558
|
|
|
560
559
|
Prefer a phone or another device over your editor? `gtd ui` opens the file whose
|
|
561
560
|
turn it is as a small web app, over Tailscale — including a step that names no
|
|
@@ -635,14 +634,15 @@ criterion ID the naive pass's tests named, and the suite must be green with
|
|
|
635
634
|
them. A wrong approach is retried twice, then rests for you to steer or overrule
|
|
636
635
|
it. `gtd slices` lists the `refactor` and `feature` slices. A refactor that
|
|
637
636
|
already carried the whole feature leaves the rebuild only the carried-over
|
|
638
|
-
tests, and the feature's quality lap
|
|
637
|
+
tests, and the feature's quality lap and review cover the whole change. Once the
|
|
639
638
|
implementation is green, a judge checks every criterion ID has a covering test:
|
|
640
639
|
an uncovered one goes back to the agent (twice, then to you) instead of ending
|
|
641
640
|
the feature, and each covered ID is recorded as a `Gtd-Mark: covers=<id>`
|
|
642
641
|
trailer. See
|
|
643
642
|
[Acceptance-criteria coverage](docs/configuration.md#acceptance-criteria-coverage).
|
|
644
|
-
Feedback at the
|
|
645
|
-
|
|
643
|
+
Feedback at the feature review re-runs design and the implementation, never the
|
|
644
|
+
naive pass; feedback at the refactor review re-runs the refactor, never design;
|
|
645
|
+
a refactor-review change the refactor cannot build is refused, and the only way
|
|
646
646
|
out is `gtd abandon`. A process from 27.5.0 or later whose judge ruled
|
|
647
647
|
`refactor-first` must `gtd abandon` too. Each slice's quality lap reviews only
|
|
648
648
|
that slice. Details in
|
|
@@ -652,10 +652,11 @@ that slice. Details in
|
|
|
652
652
|
criteria than the cap is built in slices: after design an agent groups related
|
|
653
653
|
criteria (same flow or subsystem) into `.gtd/SLICES.md`, as few slices as the
|
|
654
654
|
cap allows, and each slice — naive pass, refactor and rebuild when judged so,
|
|
655
|
-
health loop, quality lap — runs in order from where the previous one landed
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
655
|
+
health loop, quality lap — runs in order from where the previous one landed; the
|
|
656
|
+
review follows the last one and covers the whole change. `gtd slices` lists them
|
|
657
|
+
as `feature-1`, `feature-2`, … (`refactor-<n>` ahead of a refactored one). A
|
|
658
|
+
change within the cap is one `feature` slice, as before. See
|
|
659
|
+
[Coarse slices](docs/configuration.md#coarse-slices).
|
|
659
660
|
|
|
660
661
|
**Flag mode (`flagMode`, on by default).** On, the naive pass's judge runs as
|
|
661
662
|
above and a rebuilt implementation is asked for a new release flag (none at a
|
|
@@ -421,14 +421,14 @@ What each change does next (then run \`gtd land\`):
|
|
|
421
421
|
- **Continue** — having undone the sketch by hand, check the test baseline is green and start triage (**start-gate.check**).
|
|
422
422
|
`;
|
|
423
423
|
const qualityUnresolvedMessage = () => `The quality lap's fix turns hit their cap and \`.gtd/QUALITY.md\` still
|
|
424
|
-
holds findings. Nothing
|
|
425
|
-
|
|
424
|
+
holds findings. Nothing after this fixes them, so the process will not
|
|
425
|
+
go on with them open.
|
|
426
426
|
|
|
427
427
|
Fix what matters by hand, then delete or empty \`.gtd/QUALITY.md\` —
|
|
428
428
|
dropping a finding is your call to make.
|
|
429
429
|
|
|
430
430
|
What each change does next (then run \`gtd land\`):
|
|
431
|
-
- **Delete or empty \`.gtd/QUALITY.md\`** — the suite runs on your edits, then the
|
|
431
|
+
- **Delete or empty \`.gtd/QUALITY.md\`** — the suite runs on your edits, then the next slice is built, or after the last one you review the change (**build.review.reviewing**).
|
|
432
432
|
- **Anything else** — rests here again.
|
|
433
433
|
`;
|
|
434
434
|
const startGateBlockedMessage = () => `The test baseline is red — gtd will not start new work on a broken suite.
|
|
@@ -466,8 +466,10 @@ reword a criterion in place, append a new one with the next unused
|
|
|
466
466
|
number, delete a dropped one — never renumber, never reuse an ID. When
|
|
467
467
|
the file holds no list (a review loop-back rewrites it), recover the
|
|
468
468
|
last settled one from history (\`git log -p -- .gtd/REQUIREMENTS.md\`)
|
|
469
|
-
and carry its IDs over
|
|
470
|
-
|
|
469
|
+
and carry its IDs over: after a review a built criterion keeps its ID,
|
|
470
|
+
reworded in place when the review changes its behaviour, and new
|
|
471
|
+
behaviour gets a new ID. Landing is refused while a criterion has no ID
|
|
472
|
+
or two share one.
|
|
471
473
|
`;
|
|
472
474
|
const riskTierBlock = `
|
|
473
475
|
## Risk tier and production signals
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, showScript, vars } from "../flows.mjs";
|
|
2
2
|
import { fixFindings, lensEntriesOf, lensOf, qualityLap as qualityLap$1, reviewToHandOff } from "../workflow-kit.mjs";
|
|
3
|
-
import { C as fixCheck, D as reviewMissing, O as reviewQuality, T as fixRisks, Y as handOffAwaitReviewMessage, d as guarded, f as healthy, h as QUALITY, k as reviewing, l as escalation, w as fixQuality, x as awaitReview, y as REVIEW } from "./reserved-
|
|
3
|
+
import { C as fixCheck, D as reviewMissing, O as reviewQuality, T as fixRisks, Y as handOffAwaitReviewMessage, d as guarded, f as healthy, h as QUALITY, k as reviewing, l as escalation, w as fixQuality, x as awaitReview, y as REVIEW } from "./reserved-Qt5FM6Ia.mjs";
|
|
4
4
|
//#region src/workflows/shared/tail.ts
|
|
5
5
|
/** The reviewer's own `.gtd/REVIEW.md`, kept beside the human's notes so `fix` can tell them apart. */
|
|
6
6
|
const REVIEW_ORIG = ".gtd/REVIEW.orig.md";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
|
|
2
|
-
import { Dt as registryEntry, Ot as requireFlag, S as coder, St as envDefaults, Tt as flagReferences, i as skills, n as summary, o as flagShipped, r as access, t as steering, xt as defaults } from "../chunks/reserved-
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { Dt as registryEntry, Ot as requireFlag, S as coder, St as envDefaults, Tt as flagReferences, i as skills, n as summary, o as flagShipped, r as access, t as steering, xt as defaults } from "../chunks/reserved-Qt5FM6Ia.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-tAHx_AHe.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,7 +1,7 @@
|
|
|
1
1
|
import { agent, answered, applyScript, changes, changesSince, check, codeChanges, env, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, matches, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, slice, start, vars, wrote } from "../flows.mjs";
|
|
2
2
|
import { lensEntriesOf, review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as naiveApplyFailure, A as agentWithSkills, B as coverageQuestion, C as fixCheck, Ct as testFileGlobs, D as reviewMissing, E as planner, F as buildReviewTriageMessage, G as designSystem, H as criterionLine, I as builderSystem, K as designTriagePrompt, L as clearCriteriaMessage, M as buildReviewAwaitAfterDeferMessage, N as buildReviewCollectingPrompt, P as buildReviewFixNitsPrompt, Q as implementFixPrompt, R as clearCriteriaQuestion, S as coder, St as envDefaults, T as fixRisks, U as deferNotesScript, V as coveringPrompt, W as designGateAnswerMessage, X as idleMessage, Z as implementBuildingPrompt, _ as REFACTOR_NOTE, _t as startGateBlockedMessage, a as flagModeOn, at as refactorDroppedRefusal, b as SLICES, bt as unwindFailure, ct as refactorRules, d as guarded, et as naivePassPrompt, f as healthy, ft as reviewNotesCapture, g as REFACTOR, gt as slicingPrompt, h as QUALITY, ht as seamSteerMessage, i as skills, it as refactorBuildingPrompt, j as buildReviewAnswerQuestionsPrompt, k as reviewing, lt as reviewEditNotesCapture, m as HANDOFF, mt as seamByHandMessage, n as summary, nt as oversizedMessage, ot as refactorFixPrompt, p as FEEDBACK, pt as reviewerSystem, r as access, rt as qualityUnresolvedMessage, s as shipped, st as refactorNotePrompt, t as steering, tt as normaliseCriteriaScript, u as gate, ut as reviewEditsCapture, v as REQUIREMENTS, vt as uncoveredMessage, x as awaitReview, xt as defaults, y as REVIEW, yt as unwindFailedMessage, z as coverageMessage } from "../chunks/reserved-
|
|
4
|
-
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-
|
|
3
|
+
import { $ as naiveApplyFailure, A as agentWithSkills, B as coverageQuestion, C as fixCheck, Ct as testFileGlobs, D as reviewMissing, E as planner, F as buildReviewTriageMessage, G as designSystem, H as criterionLine, I as builderSystem, K as designTriagePrompt, L as clearCriteriaMessage, M as buildReviewAwaitAfterDeferMessage, N as buildReviewCollectingPrompt, P as buildReviewFixNitsPrompt, Q as implementFixPrompt, R as clearCriteriaQuestion, S as coder, St as envDefaults, T as fixRisks, U as deferNotesScript, V as coveringPrompt, W as designGateAnswerMessage, X as idleMessage, Z as implementBuildingPrompt, _ as REFACTOR_NOTE, _t as startGateBlockedMessage, a as flagModeOn, at as refactorDroppedRefusal, b as SLICES, bt as unwindFailure, ct as refactorRules, d as guarded, et as naivePassPrompt, f as healthy, ft as reviewNotesCapture, g as REFACTOR, gt as slicingPrompt, h as QUALITY, ht as seamSteerMessage, i as skills, it as refactorBuildingPrompt, j as buildReviewAnswerQuestionsPrompt, k as reviewing, lt as reviewEditNotesCapture, m as HANDOFF, mt as seamByHandMessage, n as summary, nt as oversizedMessage, ot as refactorFixPrompt, p as FEEDBACK, pt as reviewerSystem, r as access, rt as qualityUnresolvedMessage, s as shipped, st as refactorNotePrompt, t as steering, tt as normaliseCriteriaScript, u as gate, ut as reviewEditsCapture, v as REQUIREMENTS, vt as uncoveredMessage, x as awaitReview, xt as defaults, y as REVIEW, yt as unwindFailedMessage, z as coverageMessage } from "../chunks/reserved-Qt5FM6Ia.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-tAHx_AHe.mjs";
|
|
5
5
|
import { posix } from "node:path";
|
|
6
6
|
//#region src/workflows/shared/fences.ts
|
|
7
7
|
const markFenced = (text) => {
|
|
@@ -112,6 +112,19 @@ const requireCriteria = (file) => {
|
|
|
112
112
|
if (criteria.length > 0 && unnamed.length === 0 && twice.length === 0) return;
|
|
113
113
|
refuse(`gtd land: acceptance-criteria: ${file} has ${criteria.length === 0 ? `no "## Acceptance Criteria" list` : unnamed.length > 0 ? `${unnamed.length} criterion without an ID:\n${unnamed.map((c) => ` - ${c.text}`).join("\n")}` : `duplicate IDs: ${[...new Set(twice)].join(", ")}`} — list every criterion under "## Acceptance Criteria" as "- AC-<n>: <checkable criterion>", each ID unique and never reused`);
|
|
114
114
|
};
|
|
115
|
+
const idNumber = (id) => Number(id.slice(3));
|
|
116
|
+
/** Why `listed` renumbers or reuses an earlier ID; `undefined` when it does neither. */
|
|
117
|
+
const unstableIds = (listed, ids) => {
|
|
118
|
+
const top = Math.max(0, ...ids.used.map(idNumber));
|
|
119
|
+
const reused = listed.filter((id) => !ids.closed.includes(id) && idNumber(id) <= top);
|
|
120
|
+
if (reused.length === 0) return void 0;
|
|
121
|
+
return `numbers ${reused.join(", ")} at or below AC-${top}, an ID already handed out — keep a built criterion's ID (reword it in place when the review changes it), and number new ones from AC-${top + 1}`;
|
|
122
|
+
};
|
|
123
|
+
/** Refuse criteria in `file` that renumber or reuse an earlier ID. */
|
|
124
|
+
const requireStableIds = (file, ids) => {
|
|
125
|
+
const why = unstableIds(criterionIds(read(file) ?? ""), ids);
|
|
126
|
+
if (why !== void 0) refuse(`gtd land: criteria-ids: ${file} ${why}`);
|
|
127
|
+
};
|
|
115
128
|
/** Refuse a turn that changed the acceptance criteria in `file`: after the design gate only another design lap may. */
|
|
116
129
|
const requireCriteriaFrozen = (file) => {
|
|
117
130
|
const change = changes().get(file);
|
|
@@ -267,13 +280,14 @@ const noteGate = async (file, answer) => {
|
|
|
267
280
|
if (!hasThreadFor("agent", file)) requireAnswers(file);
|
|
268
281
|
return true;
|
|
269
282
|
};
|
|
270
|
-
/** Triage the sketch since `base` into requirements, until no product question is open. */
|
|
271
|
-
const design = (base) => scope("design", async () => {
|
|
283
|
+
/** Triage the sketch since `base` into requirements, until no product question is open; `guard` holds every round's criteria. */
|
|
284
|
+
const design = (base, guard) => scope("design", async () => {
|
|
272
285
|
do {
|
|
273
286
|
await triage(base);
|
|
274
287
|
requireProgress(REQUIREMENTS);
|
|
275
288
|
requireReplies(REQUIREMENTS);
|
|
276
289
|
requireCriteria(REQUIREMENTS);
|
|
290
|
+
guard?.();
|
|
277
291
|
} while (await noteGate(REQUIREMENTS, answerProductQuestions));
|
|
278
292
|
});
|
|
279
293
|
//#endregion
|
|
@@ -1085,27 +1099,31 @@ const build = (options) => continuing(phase("build", requireCriteriaIfEdited), a
|
|
|
1085
1099
|
}
|
|
1086
1100
|
};
|
|
1087
1101
|
});
|
|
1088
|
-
function implement(first, tier, amend) {
|
|
1089
|
-
|
|
1102
|
+
async function implement(first, tier, amend) {
|
|
1103
|
+
await implementing(typeof first === "object" && first !== null ? first : {
|
|
1090
1104
|
reference: first,
|
|
1091
1105
|
tier,
|
|
1092
1106
|
amend
|
|
1093
|
-
};
|
|
1094
|
-
return scope("implement", async () => {
|
|
1095
|
-
const since = head();
|
|
1096
|
-
const acceptance = options.rebuild?.acceptance;
|
|
1097
|
-
const built = options.naive === void 0 || !await applied(options.naive);
|
|
1098
|
-
if (built) await build(options);
|
|
1099
|
-
else requireCriteriaFrozen(REQUIREMENTS);
|
|
1100
|
-
if (built && acceptance !== void 0) requireAcceptance({
|
|
1101
|
-
...acceptance,
|
|
1102
|
-
ids: settled(acceptance.ids, options.slice)
|
|
1103
|
-
}, since);
|
|
1104
|
-
await green(built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0);
|
|
1105
|
-
await requireCoverage(options.base, options.slice);
|
|
1106
|
-
await closing(options.more === true);
|
|
1107
1107
|
});
|
|
1108
1108
|
}
|
|
1109
|
+
/** `implement`, resolving with the criterion IDs the requirements listed when it closed. */
|
|
1110
|
+
const implementing = (options) => scope("implement", async () => {
|
|
1111
|
+
const since = head();
|
|
1112
|
+
const acceptance = options.rebuild?.acceptance;
|
|
1113
|
+
const built = options.naive === void 0 || !await applied(options.naive);
|
|
1114
|
+
if (built) await build(options);
|
|
1115
|
+
else requireCriteriaFrozen(REQUIREMENTS);
|
|
1116
|
+
if (built && acceptance !== void 0) requireAcceptance({
|
|
1117
|
+
...acceptance,
|
|
1118
|
+
ids: settled(acceptance.ids, options.slice)
|
|
1119
|
+
}, since);
|
|
1120
|
+
await green(built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0);
|
|
1121
|
+
await requireCoverage(options.base, options.slice);
|
|
1122
|
+
const ids = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
1123
|
+
await closing(moreToBuild(options.more));
|
|
1124
|
+
return ids;
|
|
1125
|
+
});
|
|
1126
|
+
const moreToBuild = (more) => typeof more === "function" ? more() : more === true;
|
|
1109
1127
|
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
1110
1128
|
const closeOut = () => scope("implement", () => closing(false));
|
|
1111
1129
|
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
@@ -1340,32 +1358,37 @@ const unwind = () => {
|
|
|
1340
1358
|
const buildFeature = async (firstBase) => {
|
|
1341
1359
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
1342
1360
|
const slices = await slicing();
|
|
1361
|
+
const handedOut = new Set(listedIds());
|
|
1343
1362
|
const one = slices.length === 1;
|
|
1363
|
+
const built = (i) => sliceIds(sliceCriteria(slices, i))?.length !== 0;
|
|
1344
1364
|
for (const i of slices.keys()) {
|
|
1345
|
-
|
|
1346
|
-
const criteria = sliceCriteria(slices, i);
|
|
1347
|
-
if (sliceIds(criteria)?.length === 0) {
|
|
1348
|
-
if (!more) await closeOut();
|
|
1349
|
-
continue;
|
|
1350
|
-
}
|
|
1365
|
+
if (!built(i)) continue;
|
|
1351
1366
|
const n = one ? "" : `-${i + 1}`;
|
|
1352
1367
|
await buildSlice(i === 0 ? firstBase : head(), {
|
|
1353
1368
|
tier,
|
|
1354
|
-
criteria,
|
|
1369
|
+
criteria: sliceCriteria(slices, i),
|
|
1355
1370
|
refactor: `refactor${n}`,
|
|
1356
1371
|
feature: `feature${n}`,
|
|
1357
|
-
more
|
|
1372
|
+
more: () => slices.some((_, j) => j > i && built(j)),
|
|
1373
|
+
reviewBase: one ? void 0 : firstBase,
|
|
1374
|
+
handedOut
|
|
1358
1375
|
});
|
|
1359
1376
|
}
|
|
1360
1377
|
};
|
|
1361
|
-
|
|
1378
|
+
const listedIds = () => criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
1379
|
+
const gather = (handedOut) => {
|
|
1380
|
+
for (const id of listedIds()) handedOut.add(id);
|
|
1381
|
+
return [...handedOut];
|
|
1382
|
+
};
|
|
1383
|
+
/** A naive pass (judged only with `flagMode` on), then implement and the quality lap; after the last slice, the review. */
|
|
1362
1384
|
const buildSlice = async (sliceBase, build) => {
|
|
1363
1385
|
const seam = await seamBuild(sliceBase, {
|
|
1364
1386
|
criteria: build.criteria,
|
|
1365
1387
|
refactor: build.refactor
|
|
1366
1388
|
});
|
|
1389
|
+
const used = gather(build.handedOut);
|
|
1367
1390
|
await slice(build.feature, async () => {
|
|
1368
|
-
await
|
|
1391
|
+
const closed = await implementing({
|
|
1369
1392
|
reference: seam.reference,
|
|
1370
1393
|
tier: build.tier,
|
|
1371
1394
|
afterRefactor: seam.refactored,
|
|
@@ -1375,14 +1398,23 @@ const buildSlice = async (sliceBase, build) => {
|
|
|
1375
1398
|
base: build.criteria === void 0 ? void 0 : sliceBase,
|
|
1376
1399
|
more: build.more
|
|
1377
1400
|
});
|
|
1401
|
+
const more = build.more();
|
|
1378
1402
|
const carried = seam.rebuild?.acceptance?.tests ?? [];
|
|
1379
1403
|
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path) && !carried.includes(c.path)) ? sliceBase : seam.base;
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1404
|
+
if (more) {
|
|
1405
|
+
const guard = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
1406
|
+
await tail(settleQuality, {
|
|
1407
|
+
green: true,
|
|
1408
|
+
lensBase: base,
|
|
1409
|
+
guard,
|
|
1410
|
+
guardRests: false
|
|
1411
|
+
});
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const reviewBase = build.reviewBase ?? base;
|
|
1415
|
+
await featureFeedback(await reviewedTail(reviewBase, base), base, {
|
|
1416
|
+
closed,
|
|
1417
|
+
used
|
|
1386
1418
|
});
|
|
1387
1419
|
});
|
|
1388
1420
|
};
|
|
@@ -1398,6 +1430,35 @@ const settleQuality = async () => {
|
|
|
1398
1430
|
}
|
|
1399
1431
|
if (rested) await healthy(() => fixCheck());
|
|
1400
1432
|
};
|
|
1433
|
+
/** The quality lap, then the human review since `base`. */
|
|
1434
|
+
const reviewedTail = (base, lensBase, lap) => tail(async (escalations) => {
|
|
1435
|
+
await settleQuality();
|
|
1436
|
+
return review(base, escalations, { lap });
|
|
1437
|
+
}, {
|
|
1438
|
+
green: true,
|
|
1439
|
+
lensBase
|
|
1440
|
+
});
|
|
1441
|
+
const featureFeedback = async (first, lensBase, ids) => {
|
|
1442
|
+
let outcome = first;
|
|
1443
|
+
let closed = ids.closed;
|
|
1444
|
+
const handedOut = /* @__PURE__ */ new Set([...ids.used, ...ids.closed]);
|
|
1445
|
+
while (outcome.verdict === "feedback") {
|
|
1446
|
+
await reUnwind(outcome);
|
|
1447
|
+
const lap = {
|
|
1448
|
+
closed,
|
|
1449
|
+
used: [...handedOut]
|
|
1450
|
+
};
|
|
1451
|
+
await design(outcome.base, () => {
|
|
1452
|
+
requireStableIds(REQUIREMENTS, lap);
|
|
1453
|
+
gather(handedOut);
|
|
1454
|
+
});
|
|
1455
|
+
closed = await implementing({
|
|
1456
|
+
tier: riskTierOn() ? recordRisk() : void 0,
|
|
1457
|
+
amend: true
|
|
1458
|
+
});
|
|
1459
|
+
outcome = await reviewedTail(outcome.base, lensBase, "feedback");
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1401
1462
|
/** Design — skipped when the sketch's own criteria are judged clear — then `buildFeature`. */
|
|
1402
1463
|
const planAndBuild = async (firstBase, stated) => {
|
|
1403
1464
|
if (!await skipDesign(riskTierOn() ? void 0 : stated)) await design(firstBase);
|
|
@@ -1444,4 +1505,4 @@ const feature = shipped(ordinaryStart);
|
|
|
1444
1505
|
//#region src/workflows/feature/index.ts
|
|
1445
1506
|
var feature_default = feature;
|
|
1446
1507
|
//#endregion
|
|
1447
|
-
export { COMMENT, REFACTOR_PACKAGE, acceptanceFault, access, afterTail, answerProductQuestions, answerReviewQuestions, buildFeature, buildTail, closeOut, collecting, criteriaOf, criterionIds, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, holdsCallSites, implement, isTestFile, judgeSeam, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, reUnwind, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireCriteriaIfEdited, requireNoNewFlags, requireNoTestFiles, requireSlices, requireTestCallSitesOnly, requireWithin, reviewedRefactor, seamBuild, skills, skipDesign, sliceCap, sliceCriteria, sliceIds, slicesOf, slicing, statedCriteria, steering, summary, triage, unwind };
|
|
1508
|
+
export { COMMENT, REFACTOR_PACKAGE, acceptanceFault, access, afterTail, answerProductQuestions, answerReviewQuestions, buildFeature, buildTail, closeOut, collecting, criteriaOf, criterionIds, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, holdsCallSites, implement, implementing, isTestFile, judgeSeam, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, reUnwind, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireCriteriaIfEdited, requireNoNewFlags, requireNoTestFiles, requireSlices, requireStableIds, requireTestCallSitesOnly, requireWithin, reviewedRefactor, seamBuild, skills, skipDesign, sliceCap, sliceCriteria, sliceIds, slicesOf, slicing, statedCriteria, steering, summary, triage, unstableIds, unwind };
|
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 { d as reviewNotes, f as reviewRisks } from "../chunks/steering-DVILanVf.mjs";
|
|
3
|
-
import { A as agentWithSkills, J as fixReviewNotesPrompt, S as coder, St as envDefaults, c as baseline, i as skills, n as summary, q as finisherSystem, r as access, s as shipped, t as steering, xt as defaults, y as REVIEW } from "../chunks/reserved-
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { A as agentWithSkills, J as fixReviewNotesPrompt, S as coder, St as envDefaults, c as baseline, i as skills, n as summary, q as finisherSystem, r as access, s as shipped, t as steering, xt as defaults, y as REVIEW } from "../chunks/reserved-Qt5FM6Ia.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-tAHx_AHe.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,6 +1,6 @@
|
|
|
1
1
|
import { start } from "../flows.mjs";
|
|
2
|
-
import { St as envDefaults, dt as reviewGateBlockedMessage, i as skills, n as summary, r as access, s as shipped, t as steering, u as gate, xt as defaults } from "../chunks/reserved-
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { St as envDefaults, dt as reviewGateBlockedMessage, i as skills, n as summary, r as access, s as shipped, t as steering, u as gate, xt as defaults } from "../chunks/reserved-Qt5FM6Ia.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-tAHx_AHe.mjs";
|
|
4
4
|
//#region src/workflows/review/review.ts
|
|
5
5
|
/**
|
|
6
6
|
* Pure review of everything since `reviewBase`: a green gate, the quality
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agent, changesSince, glob, head, mark, read, refuse, removeScript, run, scope, vars } from "../flows.mjs";
|
|
2
|
-
import { Et as flipProblems, Ot as requireFlag, S as coder, St as envDefaults, i as skills, n as summary, o as flagShipped, p as FEEDBACK, r as access, t as steering, u as gate, wt as flagMentions, xt as defaults } from "../chunks/reserved-
|
|
2
|
+
import { Et as flipProblems, Ot as requireFlag, S as coder, St as envDefaults, i as skills, n as summary, o as flagShipped, p as FEEDBACK, r as access, t as steering, u as gate, wt as flagMentions, xt as defaults } from "../chunks/reserved-Qt5FM6Ia.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.";
|