@pmelab/gtd 33.0.0 → 34.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 +60 -50
- package/dist/chunks/{reserved-BU5Zi-nX.mjs → reserved-BcJgt5_R.mjs} +12 -1
- package/dist/chunks/{tail-DuXanjIY.mjs → tail-CM_fa93v.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +20 -10
- 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 +29 -8
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +2 -2
- package/src/testkit/steps.ts +4 -0
- package/src/workflows/feature/fast-path.feature +3 -33
- package/src/workflows/feature/feature.test.ts +0 -101
- package/src/workflows/feature/feature.ts +29 -16
- package/src/workflows/feature/flag-mode.feature +2 -91
- package/src/workflows/feature/one-step-land.feature +3 -32
- package/src/workflows/feature/quality-unresolved.feature +96 -0
- package/src/workflows/feature/refactor-absorbs.feature +11 -24
- package/src/workflows/feature/refactor-first.feature +2 -32
- package/src/workflows/feature/refactor-review.feature +17 -355
- package/src/workflows/feature/seam-rejection.feature +1 -31
- package/src/workflows/feature/seam.feature +2 -103
- package/src/workflows/feature/seam.ts +1 -1
- package/src/workflows/review/review-door.feature +108 -0
- package/src/workflows/shared/tail.ts +2 -2
- package/src/workflows/shared/text.ts +13 -0
package/README.md
CHANGED
|
@@ -405,9 +405,10 @@ phone UI does not show them).
|
|
|
405
405
|
|
|
406
406
|
### The workflow it ships with
|
|
407
407
|
|
|
408
|
-
One built-in workflow drives all of that. From where you sit, it has
|
|
408
|
+
One built-in workflow drives all of that. From where you sit, it has three
|
|
409
409
|
moments — everything between them runs without you, with the judged exceptions
|
|
410
|
-
noted in
|
|
410
|
+
noted in step 3 below — plus a fourth, review, only on `refactor-first` or when
|
|
411
|
+
you open the `review` door.
|
|
411
412
|
|
|
412
413
|
1. **You sketch.** Change anything, or write the idea into `.gtd/TODO.md`. Rough
|
|
413
414
|
is fine; it is treated as a sketch, not as work.
|
|
@@ -431,13 +432,15 @@ noted in steps 3 and 4 below.
|
|
|
431
432
|
[the naive pass](#the-naive-pass) below; with [flag mode](#the-naive-pass)
|
|
432
433
|
off, neither runs). Then the change is built test-first, checked against the
|
|
433
434
|
full suite (`testCommand`) and fixed until it passes — so keep that suite
|
|
434
|
-
fast; gtd has no lighter one. Scenario wording is not frozen:
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
435
|
+
fast; gtd has no lighter one. Scenario wording is not frozen: a review's
|
|
436
|
+
`## Scenarios` chunk marks any assertion an existing scenario lost as
|
|
437
|
+
`Weakened:` — **risk: `feature` has no review, so a build turn that weakens a
|
|
438
|
+
scenario reaches `idle` unflagged unless you open the `review` door.** After
|
|
439
|
+
the build the quality lap runs, and the process ends: the feature workflow
|
|
440
|
+
has no human review of the finished change. Two points along the process (one
|
|
441
|
+
with flag mode off) are judged rather than always asking you outright — each
|
|
442
|
+
stops and hands you a verdict to make (`gtd judge answer`, or land with a
|
|
443
|
+
clean tree to accept the conservative default, which never skips work;
|
|
441
444
|
`gtd judge run --provider fixed --answers <path>` — or the
|
|
442
445
|
`GTD_JUDGE_ANSWERS` env var, inline JSON — answers one from a file, piped
|
|
443
446
|
between `gtd judge --json` and `gtd judge answer`;
|
|
@@ -456,8 +459,6 @@ noted in steps 3 and 4 below.
|
|
|
456
459
|
or get rejected and retried?
|
|
457
460
|
- Every red round after the first: was the failure identical, new, or
|
|
458
461
|
progress?
|
|
459
|
-
- After you review: is each of your notes an edit, a question, a nit or
|
|
460
|
-
praise?
|
|
461
462
|
|
|
462
463
|
gtd itself never calls a judge: `gtd loop`, the Claude Code mod and the
|
|
463
464
|
reference driver answer these through `gtd judge run` (auto selection: jev
|
|
@@ -469,18 +470,22 @@ noted in steps 3 and 4 below.
|
|
|
469
470
|
and skip a gate unattended.**
|
|
470
471
|
|
|
471
472
|
Once the change is built, the whole change goes through a qualitative review
|
|
472
|
-
lap
|
|
473
|
-
`
|
|
474
|
-
`
|
|
475
|
-
`
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
473
|
+
lap: six lenses, one turn each, in order — `correctness`, `owasp-security`,
|
|
474
|
+
`ponytail-review`, `test-audit`, `conventions`, `spec-challenge`.
|
|
475
|
+
`correctness`, `owasp-security` and `spec-challenge` run on the planner model
|
|
476
|
+
(`smart`), the other three on the coder model (`base`); a `qualityReviews`
|
|
477
|
+
entry `<lens>=<model>` picks a lens's own. Each traces the change from its
|
|
478
|
+
own angle and records every finding, blocking or not; one fix turn then fixes
|
|
479
|
+
ALL of them once, with no re-review after the fix. A clean turn means
|
|
480
|
+
approval only when that lens found nothing at all. This lap is where code
|
|
481
|
+
quality is looked at, and every round pays for it. It is the last look the
|
|
482
|
+
feature workflow takes: the process ends at `idle` once it is clean (and the
|
|
483
|
+
suite green), with no review rest — **risk: nothing re-reads the code the
|
|
484
|
+
single fix turn writes; only the suite gates it.** Findings the fix cap
|
|
485
|
+
leaves in `.gtd/QUALITY.md` rest at `build.quality-unresolved` until you
|
|
486
|
+
delete or empty the file; the suite then runs on any hand edits. **Nobody
|
|
487
|
+
signs the finished change off unless you open the `review` door** (below).
|
|
488
|
+
The `fix` door repairs a red baseline through this same lap.
|
|
484
489
|
|
|
485
490
|
A red suite that keeps failing past a few fix attempts escalates instead of
|
|
486
491
|
retrying forever: an agent turn reads the failing output and writes
|
|
@@ -491,22 +496,28 @@ noted in steps 3 and 4 below.
|
|
|
491
496
|
instead of a third being written, and editing it is what gets the next
|
|
492
497
|
attempt anywhere new.
|
|
493
498
|
|
|
494
|
-
4. **You review
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
499
|
+
4. **You review — the refactor, or on request.** The feature workflow asks for
|
|
500
|
+
one review only on `refactor-first`: of the refactor slice, before the
|
|
501
|
+
feature is built (see [the naive pass](#the-naive-pass)). For the finished
|
|
502
|
+
change run `gtd door review <commitish>` (`/gtd review [base]`); that review
|
|
503
|
+
hands notes to `fix` instead of re-planning (below). Either way you get a
|
|
504
|
+
review document listing what changed and what to look at. Before you see it,
|
|
505
|
+
an automatic risk-fix pass (`build.review.fix.risks.fixing`) runs. Any risk
|
|
506
|
+
the reviewer names (a note opening with `Risk:`) is fixed first, the suite
|
|
507
|
+
kept green, and the review rewritten — once per review round, so a risk the
|
|
508
|
+
rewrite still names reaches you unfixed; risk: a fix lands with no check that
|
|
509
|
+
the risk was real. Tick the boxes to approve, or write what is wrong. At the
|
|
510
|
+
refactor review, approving moves on to the feature; feedback is judged note
|
|
511
|
+
by note (a third judged point), each as `edit`, `question`, `nit` or
|
|
512
|
+
`praise`. An `edit` re-derives the refactor (in a custom workflow built on
|
|
513
|
+
`buildTail`, it sends the process back to step 2 for re-derived
|
|
514
|
+
requirements). A `question` is answered inline under your note and the
|
|
515
|
+
process stops at the review again — no new plan. A `nit` is fixed in one
|
|
516
|
+
batch, the suite must go green (a red one gets fix turns first), then a fresh
|
|
517
|
+
review of the change stops at the review again. `praise` is dropped; a round
|
|
518
|
+
of only praise signs off. When a round mixes them, questions are answered and
|
|
519
|
+
nits fixed first, then the edits are planned — risk: the planning lap may
|
|
520
|
+
redo nit fixes it touches.
|
|
510
521
|
|
|
511
522
|
Each `edit` note also gets a severity tier from four judge questions
|
|
512
523
|
(security, reversibility, blast radius, new concept). `critical` and `should`
|
|
@@ -526,7 +537,7 @@ noted in steps 3 and 4 below.
|
|
|
526
537
|
You never talk to it. Every exchange is a file in `.gtd/` that you edit in your
|
|
527
538
|
own editor, and every answer you give is a commit. Your test suite is the gate
|
|
528
539
|
throughout: a red baseline stops the process before it starts, and a red build
|
|
529
|
-
is fixed before
|
|
540
|
+
is fixed before the process ends.
|
|
530
541
|
|
|
531
542
|
Prefer a phone or another device over your editor? `gtd ui` opens the file whose
|
|
532
543
|
turn it is as a small web app, over Tailscale — including a step that names no
|
|
@@ -596,16 +607,15 @@ file it added, or the suite is red on it. A judge checks whether it changed
|
|
|
596
607
|
anything outside the expected set (tests, steering files; see `seamAllow`).
|
|
597
608
|
Incidental changes land; a needed seam change is refactored first, in its own
|
|
598
609
|
slice with its own quality lap (`refactorQualityReviews`) and review, before the
|
|
599
|
-
feature is implemented
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
quality lap reviews only that slice. Details in
|
|
610
|
+
feature is implemented; a wrong approach is retried twice, then rests for you to
|
|
611
|
+
steer or overrule it. `gtd slices` lists the `refactor` and `feature` slices. A
|
|
612
|
+
refactor that already carried the whole feature leaves the implementation
|
|
613
|
+
nothing to do: its empty turn passes, and the feature's quality lap covers the
|
|
614
|
+
whole change. Feedback at the refactor review re-runs the refactor, never
|
|
615
|
+
design; a refactor-review change the refactor cannot build is refused, and the
|
|
616
|
+
only way out is `gtd abandon`. A process from 27.5.0 or later whose judge ruled
|
|
617
|
+
`refactor-first` must `gtd abandon` too. Each slice's quality lap reviews only
|
|
618
|
+
that slice. Details in
|
|
609
619
|
[the naive-pass path](docs/configuration.md#the-naive-pass-path).
|
|
610
620
|
|
|
611
621
|
**Flag mode (`flagMode`, on by default).** On, the implementation is asked for a
|
|
@@ -416,6 +416,17 @@ carries it.
|
|
|
416
416
|
What each change does next (then run \`gtd land\`):
|
|
417
417
|
- **Continue** — having undone the sketch by hand, check the test baseline is green and start triage (**start-gate.check**).
|
|
418
418
|
`;
|
|
419
|
+
const qualityUnresolvedMessage = () => `The quality lap's fix turns hit their cap and \`.gtd/QUALITY.md\` still
|
|
420
|
+
holds findings. Nothing reviews the change after this, so the process
|
|
421
|
+
will not end with them open.
|
|
422
|
+
|
|
423
|
+
Fix what matters by hand, then delete or empty \`.gtd/QUALITY.md\` —
|
|
424
|
+
dropping a finding is your call to make.
|
|
425
|
+
|
|
426
|
+
What each change does next (then run \`gtd land\`):
|
|
427
|
+
- **Delete or empty \`.gtd/QUALITY.md\`** — the suite runs on your edits, then the process ends (**idle**).
|
|
428
|
+
- **Anything else** — rests here again.
|
|
429
|
+
`;
|
|
419
430
|
const startGateBlockedMessage = () => `The test baseline is red — gtd will not start new work on a broken suite.
|
|
420
431
|
\`.gtd/FEEDBACK.md\` holds the failing output.
|
|
421
432
|
|
|
@@ -1629,4 +1640,4 @@ const steering = {
|
|
|
1629
1640
|
[REVIEW]: "review"
|
|
1630
1641
|
};
|
|
1631
1642
|
//#endregion
|
|
1632
|
-
export {
|
|
1643
|
+
export { refactorRules as $, buildReviewCollectingPrompt as A, designTriagePrompt as B, planner as C, agentWithSkills as D, reviewing as E, clearCriteriaQuestion as F, implementBuildingPrompt as G, fixReviewNotesPrompt as H, criterionLine as I, normaliseCriteriaScript as J, implementFixPrompt as K, deferNotesScript as L, buildReviewTriageMessage as M, builderSystem as N, buildReviewAnswerQuestionsPrompt as O, clearCriteriaMessage as P, refactorFixPrompt as Q, designGateAnswerMessage as R, fixRisks as S, reviewQuality as T, handOffAwaitReviewMessage as U, finisherSystem as V, idleMessage as W, refactorBuildingPrompt as X, qualityUnresolvedMessage as Y, refactorDroppedRefusal as Z, REVIEW as _, requireFlag as _t, flagModeOn as a, seamByHandMessage as at, fixCheck as b, baseline as c, unwindFailedMessage as ct, guarded as d, envDefaults as dt, reviewEditNotesCapture as et, healthy as f, testFileGlobs as ft, REQUIREMENTS as g, registryEntry as gt, REFACTOR as h, flipProblems as ht, skills as i, reviewerSystem as it, buildReviewFixNitsPrompt as j, buildReviewAwaitAfterDeferMessage as k, escalation as l, unwindFailure as lt, QUALITY as m, flagReferences as mt, summary as n, reviewGateBlockedMessage as nt, flagShipped as o, seamSteerMessage as ot, FEEDBACK as p, flagMentions as pt, naivePassPrompt as q, access as r, reviewNotesCapture as rt, shipped as s, startGateBlockedMessage as st, steering as t, reviewEditsCapture as tt, gate as u, defaults as ut, awaitReview as v, reviewMissing as w, fixQuality as x, coder as y, designSystem as z };
|
|
@@ -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 { E as reviewing, S as fixRisks, T as reviewQuality, U as handOffAwaitReviewMessage, _ as REVIEW, b as fixCheck, d as guarded, f as healthy, l as escalation, m as QUALITY, v as awaitReview, w as reviewMissing, x as fixQuality } from "./reserved-
|
|
3
|
+
import { E as reviewing, S as fixRisks, T as reviewQuality, U as handOffAwaitReviewMessage, _ as REVIEW, b as fixCheck, d as guarded, f as healthy, l as escalation, m as QUALITY, v as awaitReview, w as reviewMissing, x as fixQuality } from "./reserved-BcJgt5_R.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 {
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { _t as requireFlag, dt as envDefaults, gt as registryEntry, i as skills, mt as flagReferences, n as summary, o as flagShipped, r as access, t as steering, ut as defaults, y as coder } from "../chunks/reserved-BcJgt5_R.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-CM_fa93v.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 { answered, changes, changesSince, check, 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 } from "../flows.mjs";
|
|
2
2
|
import { lensEntriesOf, review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-
|
|
3
|
+
import { $ as refactorRules, A as buildReviewCollectingPrompt, B as designTriagePrompt, C as planner, D as agentWithSkills, E as reviewing, F as clearCriteriaQuestion, G as implementBuildingPrompt, I as criterionLine, J as normaliseCriteriaScript, K as implementFixPrompt, L as deferNotesScript, M as buildReviewTriageMessage, N as builderSystem, O as buildReviewAnswerQuestionsPrompt, P as clearCriteriaMessage, Q as refactorFixPrompt, R as designGateAnswerMessage, S as fixRisks, W as idleMessage, X as refactorBuildingPrompt, Y as qualityUnresolvedMessage, Z as refactorDroppedRefusal, _ as REVIEW, a as flagModeOn, at as seamByHandMessage, b as fixCheck, ct as unwindFailedMessage, d as guarded, dt as envDefaults, et as reviewEditNotesCapture, f as healthy, ft as testFileGlobs, g as REQUIREMENTS, h as REFACTOR, i as skills, it as reviewerSystem, j as buildReviewFixNitsPrompt, k as buildReviewAwaitAfterDeferMessage, lt as unwindFailure, m as QUALITY, n as summary, ot as seamSteerMessage, p as FEEDBACK, q as naivePassPrompt, r as access, rt as reviewNotesCapture, s as shipped, st as startGateBlockedMessage, t as steering, tt as reviewEditsCapture, u as gate, ut as defaults, v as awaitReview, w as reviewMissing, y as coder, z as designSystem } from "../chunks/reserved-BcJgt5_R.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-CM_fa93v.mjs";
|
|
5
5
|
import { posix } from "node:path";
|
|
6
6
|
//#region src/workflows/shared/fences.ts
|
|
7
7
|
const markFenced = (text) => {
|
|
@@ -958,7 +958,7 @@ const unwind = () => {
|
|
|
958
958
|
const commit = head();
|
|
959
959
|
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
960
960
|
};
|
|
961
|
-
/** A judged naive pass (none with `flagMode` off), then implement and
|
|
961
|
+
/** A judged naive pass (none with `flagMode` off), then implement and the quality lap; design must have run since `firstBase`. */
|
|
962
962
|
const buildFeature = async (firstBase) => {
|
|
963
963
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
964
964
|
const seam = flagModeOn() ? await seamBuild(firstBase) : { base: firstBase };
|
|
@@ -969,18 +969,31 @@ const buildFeature = async (firstBase) => {
|
|
|
969
969
|
afterRefactor: seam.refactored
|
|
970
970
|
});
|
|
971
971
|
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path)) ? firstBase : seam.base;
|
|
972
|
-
await
|
|
972
|
+
await tail(settleQuality, {
|
|
973
973
|
green: true,
|
|
974
974
|
lensBase: base
|
|
975
|
-
})
|
|
975
|
+
});
|
|
976
976
|
});
|
|
977
977
|
};
|
|
978
|
+
const settleQuality = async () => {
|
|
979
|
+
let rested = false;
|
|
980
|
+
while ((read(".gtd/QUALITY.md") ?? "").length > 0) {
|
|
981
|
+
rested = true;
|
|
982
|
+
await human("quality-unresolved", {
|
|
983
|
+
message: qualityUnresolvedMessage(),
|
|
984
|
+
label: "Quality findings left unresolved",
|
|
985
|
+
file: QUALITY
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
if (rested) await healthy(() => fixCheck());
|
|
989
|
+
};
|
|
978
990
|
/** Design — skipped when the sketch's own criteria are judged clear — then `buildFeature`. */
|
|
979
991
|
const planAndBuild = async (firstBase, stated) => {
|
|
980
992
|
if (!await skipDesign(riskTierOn() ? void 0 : stated)) await design(firstBase);
|
|
981
993
|
await buildFeature(firstBase);
|
|
982
994
|
};
|
|
983
|
-
|
|
995
|
+
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
|
|
996
|
+
const afterTail = async (first) => {
|
|
984
997
|
let outcome = first;
|
|
985
998
|
while (outcome.verdict === "feedback") {
|
|
986
999
|
await reUnwind(outcome);
|
|
@@ -991,13 +1004,10 @@ const featureFeedback = async (first, lensBase) => {
|
|
|
991
1004
|
});
|
|
992
1005
|
outcome = await buildTail(outcome.base, {
|
|
993
1006
|
green: true,
|
|
994
|
-
lap: "feedback"
|
|
995
|
-
lensBase
|
|
1007
|
+
lap: "feedback"
|
|
996
1008
|
});
|
|
997
1009
|
}
|
|
998
1010
|
};
|
|
999
|
-
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
|
|
1000
|
-
const afterTail = (outcome) => featureFeedback(outcome);
|
|
1001
1011
|
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
1002
1012
|
const ordinaryStart = async () => {
|
|
1003
1013
|
await human("idle", {
|
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 { D as agentWithSkills, H as fixReviewNotesPrompt, V as finisherSystem, _ as REVIEW, c as baseline,
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { D as agentWithSkills, H as fixReviewNotesPrompt, V as finisherSystem, _ as REVIEW, c as baseline, dt as envDefaults, i as skills, n as summary, r as access, s as shipped, t as steering, ut as defaults, y as coder } from "../chunks/reserved-BcJgt5_R.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-CM_fa93v.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 {
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { dt as envDefaults, i as skills, n as summary, nt as reviewGateBlockedMessage, r as access, s as shipped, t as steering, u as gate, ut as defaults } from "../chunks/reserved-BcJgt5_R.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-CM_fa93v.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 {
|
|
2
|
+
import { _t as requireFlag, dt as envDefaults, ht as flipProblems, i as skills, n as summary, o as flagShipped, p as FEEDBACK, pt as flagMentions, r as access, t as steering, u as gate, ut as defaults, y as coder } from "../chunks/reserved-BcJgt5_R.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.";
|
package/dist/workflows.mjs
CHANGED
|
@@ -17213,6 +17213,17 @@ carries it.
|
|
|
17213
17213
|
What each change does next (then run \`gtd land\`):
|
|
17214
17214
|
- **Continue** — having undone the sketch by hand, check the test baseline is green and start triage (**start-gate.check**).
|
|
17215
17215
|
`;
|
|
17216
|
+
const qualityUnresolvedMessage = () => `The quality lap's fix turns hit their cap and \`.gtd/QUALITY.md\` still
|
|
17217
|
+
holds findings. Nothing reviews the change after this, so the process
|
|
17218
|
+
will not end with them open.
|
|
17219
|
+
|
|
17220
|
+
Fix what matters by hand, then delete or empty \`.gtd/QUALITY.md\` —
|
|
17221
|
+
dropping a finding is your call to make.
|
|
17222
|
+
|
|
17223
|
+
What each change does next (then run \`gtd land\`):
|
|
17224
|
+
- **Delete or empty \`.gtd/QUALITY.md\`** — the suite runs on your edits, then the process ends (**idle**).
|
|
17225
|
+
- **Anything else** — rests here again.
|
|
17226
|
+
`;
|
|
17216
17227
|
const startGateBlockedMessage = () => `The test baseline is red — gtd will not start new work on a broken suite.
|
|
17217
17228
|
\`.gtd/FEEDBACK.md\` holds the failing output.
|
|
17218
17229
|
|
|
@@ -38760,7 +38771,7 @@ const unwind = () => {
|
|
|
38760
38771
|
const commit = head$1();
|
|
38761
38772
|
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
38762
38773
|
};
|
|
38763
|
-
/** A judged naive pass (none with `flagMode` off), then implement and
|
|
38774
|
+
/** A judged naive pass (none with `flagMode` off), then implement and the quality lap; design must have run since `firstBase`. */
|
|
38764
38775
|
const buildFeature = async (firstBase) => {
|
|
38765
38776
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
38766
38777
|
const seam = flagModeOn() ? await seamBuild(firstBase) : { base: firstBase };
|
|
@@ -38771,18 +38782,31 @@ const buildFeature = async (firstBase) => {
|
|
|
38771
38782
|
afterRefactor: seam.refactored
|
|
38772
38783
|
});
|
|
38773
38784
|
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path)) ? firstBase : seam.base;
|
|
38774
|
-
await
|
|
38785
|
+
await tail(settleQuality, {
|
|
38775
38786
|
green: true,
|
|
38776
38787
|
lensBase: base
|
|
38777
|
-
})
|
|
38788
|
+
});
|
|
38778
38789
|
});
|
|
38779
38790
|
};
|
|
38791
|
+
const settleQuality = async () => {
|
|
38792
|
+
let rested = false;
|
|
38793
|
+
while ((read(".gtd/QUALITY.md") ?? "").length > 0) {
|
|
38794
|
+
rested = true;
|
|
38795
|
+
await human("quality-unresolved", {
|
|
38796
|
+
message: qualityUnresolvedMessage(),
|
|
38797
|
+
label: "Quality findings left unresolved",
|
|
38798
|
+
file: QUALITY
|
|
38799
|
+
});
|
|
38800
|
+
}
|
|
38801
|
+
if (rested) await healthy(() => fixCheck());
|
|
38802
|
+
};
|
|
38780
38803
|
/** Design — skipped when the sketch's own criteria are judged clear — then `buildFeature`. */
|
|
38781
38804
|
const planAndBuild = async (firstBase, stated) => {
|
|
38782
38805
|
if (!await skipDesign(riskTierOn() ? void 0 : stated)) await design(firstBase);
|
|
38783
38806
|
await buildFeature(firstBase);
|
|
38784
38807
|
};
|
|
38785
|
-
|
|
38808
|
+
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
|
|
38809
|
+
const afterTail = async (first) => {
|
|
38786
38810
|
let outcome = first;
|
|
38787
38811
|
while (outcome.verdict === "feedback") {
|
|
38788
38812
|
await reUnwind(outcome);
|
|
@@ -38793,13 +38817,10 @@ const featureFeedback = async (first, lensBase) => {
|
|
|
38793
38817
|
});
|
|
38794
38818
|
outcome = await buildTail(outcome.base, {
|
|
38795
38819
|
green: true,
|
|
38796
|
-
lap: "feedback"
|
|
38797
|
-
lensBase
|
|
38820
|
+
lap: "feedback"
|
|
38798
38821
|
});
|
|
38799
38822
|
}
|
|
38800
38823
|
};
|
|
38801
|
-
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
|
|
38802
|
-
const afterTail = (outcome) => featureFeedback(outcome);
|
|
38803
38824
|
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
38804
38825
|
const ordinaryStart = async () => {
|
|
38805
38826
|
await human("idle", {
|
package/package.json
CHANGED
|
@@ -224,8 +224,8 @@ workflow.
|
|
|
224
224
|
|
|
225
225
|
In the bundled default, `planAndBuild` runs the design phase, then
|
|
226
226
|
`buildFeature`: the naive pass and its seam judge, then the implementation and
|
|
227
|
-
review. Add a human sign-off before the
|
|
228
|
-
|
|
227
|
+
the quality lap, ending with no human review. Add a human sign-off before the
|
|
228
|
+
build:
|
|
229
229
|
|
|
230
230
|
```ts
|
|
231
231
|
const planAndBuild = async (firstBase: string): Promise<void> => {
|
package/src/testkit/steps.ts
CHANGED
|
@@ -108,6 +108,10 @@ Given("a file {string} with:", (world: TestkitWorld, path: string, content: stri
|
|
|
108
108
|
world.repo().write(path, withNewline(content))
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
+
Given("the file {string} is deleted", (world: TestkitWorld, path: string) => {
|
|
112
|
+
world.repo().remove(path)
|
|
113
|
+
})
|
|
114
|
+
|
|
111
115
|
Given("the working tree is committed as {string}", (world: TestkitWorld, message: string) => {
|
|
112
116
|
world.repo().commit(message)
|
|
113
117
|
})
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Feature: a trivial change takes the same few beats at either risk tier, flag-free at low
|
|
3
3
|
|
|
4
4
|
With GTD_FLAG_RISK_TIER on, a one-line copy fix goes
|
|
5
|
-
from idle through the naive pass, the seam judge's land, implement and
|
|
6
|
-
|
|
5
|
+
from idle through the naive pass, the seam judge's land, implement and the
|
|
6
|
+
quality lap back to idle. Low tier walks exactly the steps high does; only the
|
|
7
7
|
implementation prompt and the summary differ.
|
|
8
8
|
|
|
9
9
|
Background:
|
|
@@ -27,18 +27,6 @@ Feature: a trivial change takes the same few beats at either risk tier, flag-fre
|
|
|
27
27
|
And step "implement.health.check" changes nothing
|
|
28
28
|
And step "implement.closing" runs its script
|
|
29
29
|
And step "build.quality.correctness.reviewing" changes nothing
|
|
30
|
-
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
31
|
-
"""
|
|
32
|
-
# Review: abc1234
|
|
33
|
-
|
|
34
|
-
<!-- base: 0000000 -->
|
|
35
|
-
|
|
36
|
-
## greeting
|
|
37
|
-
|
|
38
|
-
- [ ] ./src/greeting.ts#1-1
|
|
39
|
-
typo fixed
|
|
40
|
-
"""
|
|
41
|
-
And step "build.review.closing" runs its script
|
|
42
30
|
|
|
43
31
|
Scenario Outline: a one-line fix runs idle to idle with the same beats at either tier
|
|
44
32
|
Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
@@ -80,21 +68,6 @@ Feature: a trivial change takes the same few beats at either risk tier, flag-fre
|
|
|
80
68
|
export const greeting = "Hello, world!"
|
|
81
69
|
"""
|
|
82
70
|
When the process is driven
|
|
83
|
-
Then the process rests at "build.review.await-review"
|
|
84
|
-
|
|
85
|
-
Given a file ".gtd/REVIEW.md" with:
|
|
86
|
-
"""
|
|
87
|
-
# Review: abc1234
|
|
88
|
-
|
|
89
|
-
<!-- base: 0000000 -->
|
|
90
|
-
|
|
91
|
-
## greeting
|
|
92
|
-
|
|
93
|
-
- [x] ./src/greeting.ts#1-1
|
|
94
|
-
typo fixed
|
|
95
|
-
"""
|
|
96
|
-
When gtd lands
|
|
97
|
-
And the process is driven
|
|
98
71
|
Then the process rests at "idle"
|
|
99
72
|
And "src/greeting.ts" contains "Hello, world!"
|
|
100
73
|
And the commit subjects end with:
|
|
@@ -110,10 +83,7 @@ Feature: a trivial change takes the same few beats at either risk tier, flag-fre
|
|
|
110
83
|
gtd(agent): implement.building → implement.health.check
|
|
111
84
|
gtd(check): implement.health.check → implement.closing
|
|
112
85
|
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
113
|
-
gtd(agent): build.quality.correctness.reviewing →
|
|
114
|
-
gtd(agent): build.review.reviewing → build.review.await-review
|
|
115
|
-
gtd(human): build.review.await-review → build.review.closing
|
|
116
|
-
gtd(check): build.review.closing → idle
|
|
86
|
+
gtd(agent): build.quality.correctness.reviewing → idle
|
|
117
87
|
"""
|
|
118
88
|
And the output of gtd "summary" <summary> "needs a human reviewer"
|
|
119
89
|
|
|
@@ -64,107 +64,6 @@ const naivePlanned = () => {
|
|
|
64
64
|
return repo
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
describe("planAndBuild", () => {
|
|
68
|
-
it("a refactor that carried the whole feature: the empty implement turn passes and the review covers the whole change", async () => {
|
|
69
|
-
const repo = naivePlanned()
|
|
70
|
-
const SUBTRACT = `${CALC}export const subtract = (a: number, b: number) => a - b\n`
|
|
71
|
-
expect(
|
|
72
|
-
(
|
|
73
|
-
await repo.drive({
|
|
74
|
-
unwind: () => undefined,
|
|
75
|
-
"start-gate.check": () => undefined,
|
|
76
|
-
"design.triage": (_, r) =>
|
|
77
|
-
r.write(
|
|
78
|
-
".gtd/REQUIREMENTS.md",
|
|
79
|
-
"# Requirements\n\n## Concerns\n\n### S\n\n- `s`\n\n## Acceptance Criteria\n\n- AC-1: done\n",
|
|
80
|
-
),
|
|
81
|
-
"design.gate.answer": () => undefined,
|
|
82
|
-
"seam.naive": (_, r) => {
|
|
83
|
-
r.write("src/calc.ts", SUBTRACT)
|
|
84
|
-
r.write("src/calc.test.ts", ACCEPTANCE)
|
|
85
|
-
},
|
|
86
|
-
"seam.check": () => undefined,
|
|
87
|
-
"seam.judge": () => '[{"id":"shape","answer":"refactor-first","p":0.9}]',
|
|
88
|
-
"refactor.deriving": runsItsScript,
|
|
89
|
-
"refactor.building": (_, r) => r.write("src/calc.ts", SUBTRACT),
|
|
90
|
-
"refactor.health.check": () => undefined,
|
|
91
|
-
"refactor.closing": runsItsScript,
|
|
92
|
-
"refactor.build.review.reviewing": (_, r) => r.write(".gtd/REVIEW.md", review()),
|
|
93
|
-
})
|
|
94
|
-
).state,
|
|
95
|
-
).toBe("refactor.build.review.await-review")
|
|
96
|
-
await repo.land()
|
|
97
|
-
const rest = await repo.drive({
|
|
98
|
-
"refactor.build.review.closing": runsItsScript,
|
|
99
|
-
"implement.building": () => undefined,
|
|
100
|
-
"implement.health.check": () => undefined,
|
|
101
|
-
"implement.closing": runsItsScript,
|
|
102
|
-
"build.review.reviewing": (_, r) => r.write(".gtd/REVIEW.md", review()),
|
|
103
|
-
})
|
|
104
|
-
expect(rest.state).toBe("build.review.await-review")
|
|
105
|
-
const base = (await repo.gtd("base")).stdout.trim()
|
|
106
|
-
expect(repo.show(base, "src/calc.ts")).toBe(CALC)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it("after land, feedback rests next at design.triage, then implement.building, never seam.naive", async () => {
|
|
110
|
-
const repo = naivePlanned()
|
|
111
|
-
const requirements = (_: unknown, r: typeof repo) =>
|
|
112
|
-
r.write(
|
|
113
|
-
".gtd/REQUIREMENTS.md",
|
|
114
|
-
"# Requirements\n\n## Concerns\n\n### Subtract\n\n- `subtract`\n\n## Acceptance Criteria\n\n- AC-1: done\n",
|
|
115
|
-
)
|
|
116
|
-
const lap = {
|
|
117
|
-
unwind: () => undefined,
|
|
118
|
-
"start-gate.check": () => undefined,
|
|
119
|
-
"design.triage": requirements,
|
|
120
|
-
"design.gate.answer": () => undefined,
|
|
121
|
-
"seam.naive": (_: unknown, r: typeof repo) => {
|
|
122
|
-
r.write("src/calc.ts", `${CALC}// naive\n`)
|
|
123
|
-
r.write("src/calc.test.ts", ACCEPTANCE)
|
|
124
|
-
},
|
|
125
|
-
"seam.check": () => undefined,
|
|
126
|
-
"seam.judge": () => '[{"id":"shape","answer":"land","p":0.9}]',
|
|
127
|
-
"implement.building": (_: unknown, r: typeof repo) =>
|
|
128
|
-
r.write("src/calc.ts", `${CALC}// built\n`),
|
|
129
|
-
"implement.health.check": () => undefined,
|
|
130
|
-
"implement.closing": runsItsScript,
|
|
131
|
-
"build.review.reviewing": (_: unknown, r: typeof repo) => r.write(".gtd/REVIEW.md", review()),
|
|
132
|
-
}
|
|
133
|
-
expect((await repo.drive(lap)).state).toBe("build.review.await-review")
|
|
134
|
-
|
|
135
|
-
repo.write(".gtd/REVIEW.md", review("also export multiply"))
|
|
136
|
-
await repo.land()
|
|
137
|
-
const collected = await repo.drive({
|
|
138
|
-
"build.review.collecting": (_, r) => {
|
|
139
|
-
requirements(_, r)
|
|
140
|
-
r.write(".gtd/REVIEW.md", `${review("also export multiply")} - A: noted.\n`)
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
|
-
expect(collected.state).toBe("build.review.await-review")
|
|
144
|
-
|
|
145
|
-
repo.write(".gtd/REVIEW.md", review())
|
|
146
|
-
await repo.land()
|
|
147
|
-
const rest = await repo.drive({
|
|
148
|
-
"build.review.closing": runsItsScript,
|
|
149
|
-
"re-unwind": runsItsScript,
|
|
150
|
-
"design.triage": (_, r) =>
|
|
151
|
-
r.write(
|
|
152
|
-
".gtd/REQUIREMENTS.md",
|
|
153
|
-
"# Requirements\n\n- multiply\n\n## Acceptance Criteria\n\n- AC-1: done\n",
|
|
154
|
-
),
|
|
155
|
-
"design.gate.answer": () => undefined,
|
|
156
|
-
})
|
|
157
|
-
expect(rest.state).toBe("implement.building")
|
|
158
|
-
expect(rest.content).toContain("already exists")
|
|
159
|
-
expect(rest.content).not.toContain("write the change fresh")
|
|
160
|
-
const subjects = repo.subjects()
|
|
161
|
-
const reUnwind = subjects.findIndex((subject) => subject.endsWith("→ re-unwind"))
|
|
162
|
-
expect(reUnwind).toBeGreaterThan(-1)
|
|
163
|
-
expect(subjects.slice(reUnwind + 1).some((subject) => subject.includes("seam."))).toBe(false)
|
|
164
|
-
expect(subjects.filter((subject) => subject.includes("→ seam.naive"))).toHaveLength(1)
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
67
|
describe("afterTail feedback lap", () => {
|
|
169
68
|
const TAIL = [
|
|
170
69
|
`import { start } from "@pmelab/gtd/flows"`,
|