@pmelab/gtd 38.2.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 +66 -58
- package/dist/chunks/{reserved-C3PgP7XB.mjs → reserved-Qt5FM6Ia.mjs} +29 -10
- package/dist/chunks/{tail-Bkxki2nK.mjs → tail-tAHx_AHe.mjs} +1 -1
- package/dist/gtd.bundle.mjs +14 -5
- package/dist/testkit.mjs +14 -5
- package/dist/workflow-kit.mjs +12 -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 +136 -45
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -2
- package/src/cli/Cli.ts +4 -3
- package/src/cli/Metrics.test.ts +31 -0
- package/src/cli/Metrics.ts +8 -0
- package/src/workflow-kit/health.ts +15 -1
- package/src/workflows/cleanup/cleanup.test.ts +2 -0
- 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/refactor.test.ts +4 -0
- 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/rollout/rollout.feature +16 -0
- package/src/workflows/rollout/rollout.test.ts +2 -0
- package/src/workflows/shared/health.test.ts +2 -0
- package/src/workflows/shared/health.ts +34 -5
- package/src/workflows/shared/recheck.feature +60 -0
- package/src/workflows/shared/text.ts +7 -5
- package/src/workflows/review/review-door.feature +0 -121
package/README.md
CHANGED
|
@@ -213,9 +213,10 @@ Label: Checking the baseline
|
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
A green suite leaves the tree clean, so the process moves on to triage. A red
|
|
216
|
-
suite instead writes `.gtd/FEEDBACK.md
|
|
217
|
-
`start-gate.
|
|
218
|
-
what the beat left in the
|
|
216
|
+
suite instead writes `.gtd/FEEDBACK.md`; the process runs it once more at
|
|
217
|
+
`start-gate.recheck`, and only a second red goes to `start-gate.blocked` — same
|
|
218
|
+
two commands, different outcome, decided entirely by what the beat left in the
|
|
219
|
+
tree.
|
|
219
220
|
|
|
220
221
|
Land the green one, and the beat after it belongs to an agent.
|
|
221
222
|
|
|
@@ -418,10 +419,9 @@ phone UI does not show them).
|
|
|
418
419
|
|
|
419
420
|
### The workflow it ships with
|
|
420
421
|
|
|
421
|
-
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
|
|
422
423
|
moments — everything between them runs without you, with the judged exceptions
|
|
423
|
-
noted in
|
|
424
|
-
you open the `review` door.
|
|
424
|
+
noted in steps 3 and 4 below.
|
|
425
425
|
|
|
426
426
|
1. **You sketch.** Change anything, or write the idea into `.gtd/TODO.md`. Rough
|
|
427
427
|
is fine; it is treated as a sketch, not as work.
|
|
@@ -448,14 +448,12 @@ you open the `review` door.
|
|
|
448
448
|
against the full suite (`testCommand`) and fixed until it passes — so keep
|
|
449
449
|
that suite fast; gtd has no lighter one. Scenario wording is not frozen: a
|
|
450
450
|
review's `## Scenarios` chunk marks any assertion an existing scenario lost
|
|
451
|
-
as `Weakened
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
land with a clean tree to accept the conservative default, which never skips
|
|
458
|
-
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
|
|
459
457
|
`GTD_JUDGE_ANSWERS` env var, inline JSON — answers one from a file, piped
|
|
460
458
|
between `gtd judge --json` and `gtd judge answer`;
|
|
461
459
|
`gtd judge run --provider jev` asks TypeSafe's Jev instead, with the key in
|
|
@@ -473,6 +471,8 @@ you open the `review` door.
|
|
|
473
471
|
or get rejected and retried?
|
|
474
472
|
- Every red round after the first: was the failure identical, new, or
|
|
475
473
|
progress?
|
|
474
|
+
- After you review: is each of your notes an edit, a question, a nit or
|
|
475
|
+
praise?
|
|
476
476
|
|
|
477
477
|
gtd itself never calls a judge: `gtd loop`, the Claude Code mod and the
|
|
478
478
|
reference driver answer these through `gtd judge run` (auto selection: jev
|
|
@@ -484,22 +484,20 @@ you open the `review` door.
|
|
|
484
484
|
and skip a gate unattended.**
|
|
485
485
|
|
|
486
486
|
Once the change is built, the whole change goes through a qualitative review
|
|
487
|
-
lap: six lenses, one turn each, in order —
|
|
488
|
-
`
|
|
489
|
-
`correctness`, `owasp-security` and
|
|
490
|
-
|
|
491
|
-
entry `<lens>=<model>` picks a
|
|
492
|
-
own
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
signs the finished change off unless you open the `review` door** (below).
|
|
502
|
-
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.
|
|
503
501
|
|
|
504
502
|
A red suite that keeps failing past a few fix attempts escalates instead of
|
|
505
503
|
retrying forever: an agent turn reads the failing output and writes
|
|
@@ -508,30 +506,35 @@ you open the `review` door.
|
|
|
508
506
|
as-is, handing that document to the next fix turn as its instruction. Two
|
|
509
507
|
such rounds with no fix is the terminal stop: the last analysis stays on disk
|
|
510
508
|
instead of a third being written, and editing it is what gets the next
|
|
511
|
-
attempt anywhere new.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
509
|
+
attempt anywhere new. Before any escalation or red-baseline stop the suite
|
|
510
|
+
runs once more; a pass there continues without you and is recorded as
|
|
511
|
+
`Gtd-Mark: flaky=<check>` (see
|
|
512
|
+
[Re-check before a person](docs/configuration.md#re-check-before-a-person)).
|
|
513
|
+
|
|
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,
|
|
519
518
|
an automatic risk-fix pass (`build.review.fix.risks.fixing`) runs. Any risk
|
|
520
519
|
the reviewer names (a note opening with `Risk:`) is fixed first, the suite
|
|
521
520
|
kept green, and the review rewritten — once per review round, so a risk the
|
|
522
521
|
rewrite still names reaches you unfixed; risk: a fix lands with no check that
|
|
523
|
-
the risk was real. Tick the boxes to approve, or write what is wrong.
|
|
524
|
-
refactor review,
|
|
525
|
-
|
|
526
|
-
`praise`. An `edit`
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
review
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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.
|
|
535
538
|
|
|
536
539
|
Each `edit` note also gets a severity tier from four judge questions
|
|
537
540
|
(security, reversibility, blast radius, new concept). `critical` and `should`
|
|
@@ -551,7 +554,7 @@ you open the `review` door.
|
|
|
551
554
|
You never talk to it. Every exchange is a file in `.gtd/` that you edit in your
|
|
552
555
|
own editor, and every answer you give is a commit. Your test suite is the gate
|
|
553
556
|
throughout: a red baseline stops the process before it starts, and a red build
|
|
554
|
-
is fixed before
|
|
557
|
+
is fixed before review.
|
|
555
558
|
|
|
556
559
|
Prefer a phone or another device over your editor? `gtd ui` opens the file whose
|
|
557
560
|
turn it is as a small web app, over Tailscale — including a step that names no
|
|
@@ -631,14 +634,15 @@ criterion ID the naive pass's tests named, and the suite must be green with
|
|
|
631
634
|
them. A wrong approach is retried twice, then rests for you to steer or overrule
|
|
632
635
|
it. `gtd slices` lists the `refactor` and `feature` slices. A refactor that
|
|
633
636
|
already carried the whole feature leaves the rebuild only the carried-over
|
|
634
|
-
tests, and the feature's quality lap
|
|
637
|
+
tests, and the feature's quality lap and review cover the whole change. Once the
|
|
635
638
|
implementation is green, a judge checks every criterion ID has a covering test:
|
|
636
639
|
an uncovered one goes back to the agent (twice, then to you) instead of ending
|
|
637
640
|
the feature, and each covered ID is recorded as a `Gtd-Mark: covers=<id>`
|
|
638
641
|
trailer. See
|
|
639
642
|
[Acceptance-criteria coverage](docs/configuration.md#acceptance-criteria-coverage).
|
|
640
|
-
Feedback at the
|
|
641
|
-
|
|
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
|
|
642
646
|
out is `gtd abandon`. A process from 27.5.0 or later whose judge ruled
|
|
643
647
|
`refactor-first` must `gtd abandon` too. Each slice's quality lap reviews only
|
|
644
648
|
that slice. Details in
|
|
@@ -648,10 +652,11 @@ that slice. Details in
|
|
|
648
652
|
criteria than the cap is built in slices: after design an agent groups related
|
|
649
653
|
criteria (same flow or subsystem) into `.gtd/SLICES.md`, as few slices as the
|
|
650
654
|
cap allows, and each slice — naive pass, refactor and rebuild when judged so,
|
|
651
|
-
health loop, quality lap — runs in order from where the previous one landed
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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).
|
|
655
660
|
|
|
656
661
|
**Flag mode (`flagMode`, on by default).** On, the naive pass's judge runs as
|
|
657
662
|
above and a rebuilt implementation is asked for a new release flag (none at a
|
|
@@ -745,6 +750,9 @@ it never replays and a config changed since is no refusal:
|
|
|
745
750
|
- **findings** — the net count of `## ` findings each quality-lap lens added to
|
|
746
751
|
`.gtd/QUALITY.md`, keyed by the lap's lens path (`build.quality.correctness`,
|
|
747
752
|
`refactor.build.quality.correctness`, …)
|
|
753
|
+
- **flaky** — red checks that passed on their re-check (`Gtd-Mark: flaky=…`),
|
|
754
|
+
keyed by the re-check's full step name (`start-gate.recheck`,
|
|
755
|
+
`build.health.recheck`, …)
|
|
748
756
|
|
|
749
757
|
`--json` prints `{ processes, total }`; `--json=total.refactorFirstRate` prints
|
|
750
758
|
one value. Model, phase and lens keys may hold dots a path cannot reach: select
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { accessFor, agent, answered, changesSince, check, codeThreads, defineWorkflow, displayPath, env, head, human, judge, numeric, quote, read, refuse, scope, skillsFor, start, vars } from "../flows.mjs";
|
|
1
|
+
import { accessFor, agent, answered, changesSince, check, codeThreads, defineWorkflow, displayPath, env, head, human, judge, mark, numeric, quote, read, refuse, scope, skillsFor, start, vars } from "../flows.mjs";
|
|
2
2
|
import { escalation as escalation$1, gate as gate$1, healthy as healthy$1, lensScopes, lensesOf, removedAssertions } from "../workflow-kit.mjs";
|
|
3
3
|
import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct, n as Boolean$, s as Number$ } from "./Schema-C6fLlQzx.mjs";
|
|
4
4
|
//#region src/workflows/shared/registry.ts
|
|
@@ -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
|
|
@@ -1580,6 +1582,19 @@ const baseline = (name, label = "Checking the baseline") => check(name, env.test
|
|
|
1580
1582
|
report: FEEDBACK,
|
|
1581
1583
|
label
|
|
1582
1584
|
});
|
|
1585
|
+
/**
|
|
1586
|
+
* Re-run a red check as step `name` before a person sees it. Green records
|
|
1587
|
+
* `Gtd-Mark: flaky=<red>` on the re-check's landing and resolves `true`.
|
|
1588
|
+
*/
|
|
1589
|
+
const recheck = async (name, red, options = {}) => {
|
|
1590
|
+
const green = await check(name, env.testCommand ?? "", {
|
|
1591
|
+
...options,
|
|
1592
|
+
report: FEEDBACK,
|
|
1593
|
+
label: "Re-running checks"
|
|
1594
|
+
});
|
|
1595
|
+
if (green) mark("flaky", red);
|
|
1596
|
+
return green;
|
|
1597
|
+
};
|
|
1583
1598
|
const escalationSteps = (rules) => ({
|
|
1584
1599
|
limit: 2,
|
|
1585
1600
|
describe: () => describeEscalation(rules),
|
|
@@ -1630,15 +1645,17 @@ const sameFailure = async (previous, current) => {
|
|
|
1630
1645
|
});
|
|
1631
1646
|
return comparable && truncated.length === 0 && answered(answers.verdict, "identical", numeric(vars.judgeIdenticalMinP, Infinity));
|
|
1632
1647
|
};
|
|
1648
|
+
const healthSweep = (options) => [ESCALATION, ...options.sweepOnGreen ?? []];
|
|
1633
1649
|
const runSuite = (options) => check("health.check", env.testCommand ?? "", {
|
|
1634
1650
|
report: FEEDBACK,
|
|
1635
1651
|
label: "Running checks",
|
|
1636
|
-
sweepOnGreen:
|
|
1652
|
+
sweepOnGreen: healthSweep(options)
|
|
1637
1653
|
});
|
|
1638
1654
|
/**
|
|
1639
1655
|
* Loop until the suite is green: check, and on red run `fix` — escalating
|
|
1640
1656
|
* first once `FIX_CAP` fixes are spent, or a judge calls the failure
|
|
1641
|
-
* identical to the previous round's.
|
|
1657
|
+
* identical to the previous round's. An escalation re-runs the suite once
|
|
1658
|
+
* first; a green re-run ends the loop.
|
|
1642
1659
|
*/
|
|
1643
1660
|
const healthy = (fix, options = {}) => healthy$1(guarded(fix, options.guard), {
|
|
1644
1661
|
check: () => runSuite(options),
|
|
@@ -1647,14 +1664,16 @@ const healthy = (fix, options = {}) => healthy$1(guarded(fix, options.guard), {
|
|
|
1647
1664
|
fixesSoFar: options.fixesSoFar ?? 0,
|
|
1648
1665
|
sameFailure,
|
|
1649
1666
|
escalations: options.escalations ?? { rounds: 0 },
|
|
1667
|
+
recheck: () => recheck("health.recheck", "health.check", { sweepOnGreen: healthSweep(options) }),
|
|
1650
1668
|
escalation: guardedSteps(options.guard, options.describeGuard, options.guardRests, options.rules)
|
|
1651
1669
|
});
|
|
1652
1670
|
/**
|
|
1653
|
-
* Hold the process at `<name>.blocked` until the suite is green
|
|
1671
|
+
* Hold the process at `<name>.blocked` until the suite is green, re-running a
|
|
1672
|
+
* red check once as `<name>.recheck` before resting. `guard` runs
|
|
1654
1673
|
* after each `blocked` landing only — never after a check, whose refusal would
|
|
1655
1674
|
* re-run the same script forever.
|
|
1656
1675
|
*/
|
|
1657
|
-
const gate = (name, message, guard) => gate$1(name, () => baseline("check"), guarded(() => human("blocked", {
|
|
1676
|
+
const gate = (name, message, guard) => gate$1(name, async () => await baseline("check") || recheck("recheck", `${name}.check`), guarded(() => human("blocked", {
|
|
1658
1677
|
message,
|
|
1659
1678
|
label: "Baseline is red",
|
|
1660
1679
|
file: FEEDBACK
|
|
@@ -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";
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -41406,7 +41406,8 @@ const emptyTally = () => ({
|
|
|
41406
41406
|
naive: 0,
|
|
41407
41407
|
rebuild: 0
|
|
41408
41408
|
},
|
|
41409
|
-
byLens: record()
|
|
41409
|
+
byLens: record(),
|
|
41410
|
+
flaky: record()
|
|
41410
41411
|
});
|
|
41411
41412
|
const sum = (record) => Object.values(record).reduce((a, b) => a + b, 0);
|
|
41412
41413
|
const finish = (t) => ({
|
|
@@ -41426,6 +41427,10 @@ const finish = (t) => ({
|
|
|
41426
41427
|
findings: {
|
|
41427
41428
|
total: sum(t.byLens),
|
|
41428
41429
|
byLens: t.byLens
|
|
41430
|
+
},
|
|
41431
|
+
flaky: {
|
|
41432
|
+
total: sum(t.flaky),
|
|
41433
|
+
byStep: t.flaky
|
|
41429
41434
|
}
|
|
41430
41435
|
});
|
|
41431
41436
|
const tallyCost = (t, open, costs, phase) => {
|
|
@@ -41482,6 +41487,7 @@ const processMetrics = (commits) => {
|
|
|
41482
41487
|
if (subject.actor === "agent" && CONTINUE.test(at)) add(t.continuations, at.replace(CONTINUE, "$1"), 1);
|
|
41483
41488
|
const lens = LENS.exec(at)?.[1];
|
|
41484
41489
|
if (lens !== void 0 && commit.findings > 0) add(t.byLens, lens, commit.findings);
|
|
41490
|
+
if (message.marks.some((m) => m.name === "flaky")) add(t.flaky, at, 1);
|
|
41485
41491
|
tallyDecision(t, open, at, subject.to);
|
|
41486
41492
|
}
|
|
41487
41493
|
return finish(t);
|
|
@@ -41496,6 +41502,7 @@ const totalMetrics = (all) => {
|
|
|
41496
41502
|
t.doubleWork.naive += m.cost.doubleWork.naive;
|
|
41497
41503
|
t.doubleWork.rebuild += m.cost.doubleWork.rebuild;
|
|
41498
41504
|
for (const [k, v] of Object.entries(m.findings.byLens)) add(t.byLens, k, v);
|
|
41505
|
+
for (const [k, v] of Object.entries(m.flaky.byStep)) add(t.flaky, k, v);
|
|
41499
41506
|
}
|
|
41500
41507
|
return {
|
|
41501
41508
|
processes: all.length,
|
|
@@ -41533,7 +41540,8 @@ const line = (label, m) => {
|
|
|
41533
41540
|
`${label}: ${slices.decided} slices, ${slices.rebuilt} rebuilt (refactor-first ${percent}), ${slices.refactored} refactored`,
|
|
41534
41541
|
`${continued} continuations${continued > 0 ? ` (${listed(m.continuations)})` : ""}`,
|
|
41535
41542
|
`cost ${cost.total} (${listed(cost.byPhase)}; by model ${listed(cost.byModel) || "-"}), double work ${cost.doubleWork.total}`,
|
|
41536
|
-
`${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}
|
|
41543
|
+
`${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}`,
|
|
41544
|
+
`${m.flaky.total} flaky${m.flaky.total > 0 ? ` (${listed(m.flaky.byStep)})` : ""}`
|
|
41537
41545
|
].join("; ");
|
|
41538
41546
|
};
|
|
41539
41547
|
const runHistoryMetricsCommand = (refs, json, out) => gen(function* () {
|
|
@@ -42943,9 +42951,10 @@ const COMMAND_ROWS = [
|
|
|
42943
42951
|
"Report how the kept processes went: slices whose naive pass",
|
|
42944
42952
|
"landed or was rebuilt (the refactor-first rate), continuations",
|
|
42945
42953
|
"per phase, Gtd-Cost by model and phase with the double work of",
|
|
42946
|
-
"a rebuild,
|
|
42947
|
-
"
|
|
42948
|
-
"
|
|
42954
|
+
"a rebuild, quality-lap findings per lens, and red checks that",
|
|
42955
|
+
"passed on their re-check — per process and in total. Each <ref>",
|
|
42956
|
+
"is read as by history show, but a branch name means every",
|
|
42957
|
+
"process kept from it; none means every ref.",
|
|
42949
42958
|
"Reads commit subjects and trailers, never replays, so a",
|
|
42950
42959
|
"changed config is no refusal. --json prints { processes, total",
|
|
42951
42960
|
"} as one document; writes nothing"
|
package/dist/testkit.mjs
CHANGED
|
@@ -10327,7 +10327,8 @@ const emptyTally = () => ({
|
|
|
10327
10327
|
naive: 0,
|
|
10328
10328
|
rebuild: 0
|
|
10329
10329
|
},
|
|
10330
|
-
byLens: record()
|
|
10330
|
+
byLens: record(),
|
|
10331
|
+
flaky: record()
|
|
10331
10332
|
});
|
|
10332
10333
|
const sum = (record) => Object.values(record).reduce((a, b) => a + b, 0);
|
|
10333
10334
|
const finish = (t) => ({
|
|
@@ -10347,6 +10348,10 @@ const finish = (t) => ({
|
|
|
10347
10348
|
findings: {
|
|
10348
10349
|
total: sum(t.byLens),
|
|
10349
10350
|
byLens: t.byLens
|
|
10351
|
+
},
|
|
10352
|
+
flaky: {
|
|
10353
|
+
total: sum(t.flaky),
|
|
10354
|
+
byStep: t.flaky
|
|
10350
10355
|
}
|
|
10351
10356
|
});
|
|
10352
10357
|
const tallyCost = (t, open, costs, phase) => {
|
|
@@ -10403,6 +10408,7 @@ const processMetrics = (commits) => {
|
|
|
10403
10408
|
if (subject.actor === "agent" && CONTINUE.test(at)) add(t.continuations, at.replace(CONTINUE, "$1"), 1);
|
|
10404
10409
|
const lens = LENS.exec(at)?.[1];
|
|
10405
10410
|
if (lens !== void 0 && commit.findings > 0) add(t.byLens, lens, commit.findings);
|
|
10411
|
+
if (message.marks.some((m) => m.name === "flaky")) add(t.flaky, at, 1);
|
|
10406
10412
|
tallyDecision(t, open, at, subject.to);
|
|
10407
10413
|
}
|
|
10408
10414
|
return finish(t);
|
|
@@ -10417,6 +10423,7 @@ const totalMetrics = (all) => {
|
|
|
10417
10423
|
t.doubleWork.naive += m.cost.doubleWork.naive;
|
|
10418
10424
|
t.doubleWork.rebuild += m.cost.doubleWork.rebuild;
|
|
10419
10425
|
for (const [k, v] of Object.entries(m.findings.byLens)) add(t.byLens, k, v);
|
|
10426
|
+
for (const [k, v] of Object.entries(m.flaky.byStep)) add(t.flaky, k, v);
|
|
10420
10427
|
}
|
|
10421
10428
|
return {
|
|
10422
10429
|
processes: all.length,
|
|
@@ -10454,7 +10461,8 @@ const line = (label, m) => {
|
|
|
10454
10461
|
`${label}: ${slices.decided} slices, ${slices.rebuilt} rebuilt (refactor-first ${percent}), ${slices.refactored} refactored`,
|
|
10455
10462
|
`${continued} continuations${continued > 0 ? ` (${listed(m.continuations)})` : ""}`,
|
|
10456
10463
|
`cost ${cost.total} (${listed(cost.byPhase)}; by model ${listed(cost.byModel) || "-"}), double work ${cost.doubleWork.total}`,
|
|
10457
|
-
`${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}
|
|
10464
|
+
`${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}`,
|
|
10465
|
+
`${m.flaky.total} flaky${m.flaky.total > 0 ? ` (${listed(m.flaky.byStep)})` : ""}`
|
|
10458
10466
|
].join("; ");
|
|
10459
10467
|
};
|
|
10460
10468
|
const runHistoryMetricsCommand = (refs, json, out) => gen(function* () {
|
|
@@ -11855,9 +11863,10 @@ const COMMAND_ROWS = [
|
|
|
11855
11863
|
"Report how the kept processes went: slices whose naive pass",
|
|
11856
11864
|
"landed or was rebuilt (the refactor-first rate), continuations",
|
|
11857
11865
|
"per phase, Gtd-Cost by model and phase with the double work of",
|
|
11858
|
-
"a rebuild,
|
|
11859
|
-
"
|
|
11860
|
-
"
|
|
11866
|
+
"a rebuild, quality-lap findings per lens, and red checks that",
|
|
11867
|
+
"passed on their re-check — per process and in total. Each <ref>",
|
|
11868
|
+
"is read as by history show, but a branch name means every",
|
|
11869
|
+
"process kept from it; none means every ref.",
|
|
11861
11870
|
"Reads commit subjects and trailers, never replays, so a",
|
|
11862
11871
|
"changed config is no refusal. --json prints { processes, total",
|
|
11863
11872
|
"} as one document; writes nothing"
|
package/dist/workflow-kit.mjs
CHANGED
|
@@ -17,10 +17,15 @@ const untilSettled = async (check, pending) => {
|
|
|
17
17
|
};
|
|
18
18
|
const STAMP = /\n<!-- gtd check [0-9a-f]+ -->\n?$/;
|
|
19
19
|
const stripStamp = (text) => text.replace(STAMP, "\n");
|
|
20
|
+
/** `true` when the re-check passed, else the red re-check's report — the one the escalation describes. */
|
|
21
|
+
const recheckReport = async (health) => {
|
|
22
|
+
if (health.recheck === void 0) return void 0;
|
|
23
|
+
return await health.recheck() || stripStamp(read(health.report) ?? "");
|
|
24
|
+
};
|
|
20
25
|
/**
|
|
21
26
|
* Loop until the suite is green: check, and on red run `fix` — escalating
|
|
22
27
|
* first once `fixCap` fixes are spent, or `sameFailure` calls the failure
|
|
23
|
-
* identical to the previous round's.
|
|
28
|
+
* identical to the previous round's, unless `recheck` passes.
|
|
24
29
|
*/
|
|
25
30
|
const healthy = async (fix, health) => {
|
|
26
31
|
let fixes = health.fixesSoFar ?? 0;
|
|
@@ -34,6 +39,12 @@ const healthy = async (fix, health) => {
|
|
|
34
39
|
const stuck = previous !== void 0 && await health.sameFailure(previous, current);
|
|
35
40
|
previous = current;
|
|
36
41
|
if (stuck || fixes >= health.fixCap) {
|
|
42
|
+
const rechecked = await recheckReport(health);
|
|
43
|
+
if (rechecked === true) {
|
|
44
|
+
health.escalations.rounds = 0;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
previous = rechecked ?? previous;
|
|
37
48
|
await escalation(health.escalations, health.escalation);
|
|
38
49
|
fixes = 0;
|
|
39
50
|
}
|
|
@@ -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 () => {
|