@pmelab/gtd 41.0.0 → 43.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 +107 -53
- package/claude/hooks/sketch.ts +1 -1
- package/dist/chunks/{reserved-Ch4LLrEj.mjs → reserved-COEMFEpy.mjs} +84 -51
- package/dist/chunks/{steering-DVILanVf.mjs → steering-D4LUQ-av.mjs} +95 -12
- package/dist/chunks/tail-C_xn8U7U.mjs +246 -0
- package/dist/flows.mjs +11 -1
- package/dist/gtd.bundle.mjs +498 -30
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +460 -28
- package/dist/web/main.js +42 -13
- package/dist/workflow-kit.mjs +61 -25
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +66 -57
- package/dist/workflows/fix.mjs +3 -3
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +446 -141
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +33 -1
- package/src/cli/Cli.ts +70 -6
- package/src/cli/History.ts +15 -0
- package/src/cli/Judges.test.ts +373 -0
- package/src/cli/Judges.ts +624 -0
- package/src/cli/Metrics.ts +3 -16
- package/src/cli/Program.test.ts +3 -1
- package/src/cli/Program.ts +9 -0
- package/src/flows/scripts.ts +15 -0
- package/src/steering/index.ts +12 -1
- package/src/steering/review.test.ts +109 -20
- package/src/steering/review.ts +160 -9
- package/src/testkit/EmittedScriptRecognizer.ts +0 -14
- package/src/testkit/FlowScripts.test.ts +17 -0
- package/src/testkit/FlowScripts.ts +15 -0
- package/src/testkit/steps.ts +31 -0
- package/src/workflow-kit/review.test.ts +170 -26
- package/src/workflow-kit/review.ts +95 -19
- package/src/workflows/feature/coverage.feature +3 -3
- package/src/workflows/feature/fast-path.feature +2 -2
- package/src/workflows/feature/feature.test.ts +2 -37
- package/src/workflows/feature/feature.ts +12 -14
- package/src/workflows/feature/flag-mode.feature +2 -2
- package/src/workflows/feature/lens-review.feature +246 -0
- package/src/workflows/feature/one-step-land.feature +2 -2
- package/src/workflows/feature/quality-unresolved.feature +47 -32
- 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 +7 -7
- package/src/workflows/feature/refactor-review.feature +19 -100
- package/src/workflows/feature/refactor.test.ts +12 -27
- package/src/workflows/feature/refactor.ts +0 -1
- package/src/workflows/feature/review-answers.feature +179 -0
- package/src/workflows/feature/review-gate.feature +6 -6
- package/src/workflows/feature/review.test.ts +33 -77
- package/src/workflows/feature/review.ts +79 -47
- package/src/workflows/feature/seam-rejection.feature +2 -2
- package/src/workflows/feature/seam.feature +2 -2
- package/src/workflows/feature/seam.test.ts +1 -1
- package/src/workflows/feature/slicing.feature +6 -6
- package/src/workflows/feature/steps.test.ts +0 -12
- package/src/workflows/feature/steps.ts +3 -10
- package/src/workflows/index.ts +1 -0
- package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +6 -5
- package/src/workflows/shared/access.test.ts +2 -11
- package/src/workflows/shared/access.ts +0 -2
- package/src/workflows/shared/lenses.test.ts +39 -0
- package/src/workflows/shared/lenses.ts +176 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/tail.ts +19 -4
- package/src/workflows/shared/text.test.ts +106 -88
- package/src/workflows/shared/text.ts +164 -53
- package/src/workflows/shared/vars.ts +1 -0
- package/dist/chunks/tail-lY1UmJrU.mjs +0 -112
package/README.md
CHANGED
|
@@ -339,10 +339,10 @@ Then, in any repository:
|
|
|
339
339
|
```
|
|
340
340
|
|
|
341
341
|
That starts a process from your requirements and drives it until it needs you.
|
|
342
|
-
The requirements go to the top of `.gtd/TODO.md
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
The requirements go to the top of `.gtd/TODO.md`, above whatever it already
|
|
343
|
+
holds. A long, multi-line requirements paste still starts a process; if Claude
|
|
344
|
+
Code never expanded the paste, gtd says it could not read it, and you add the
|
|
345
|
+
requirements to the top of `.gtd/TODO.md` and run `/gtd`. `/gtd fix`,
|
|
346
346
|
`/gtd review [base]`, `/gtd rollout <flag>` and `/gtd cleanup <flag>` (where the
|
|
347
347
|
config exports them) take the doors described below; text whose word count does
|
|
348
348
|
not fit a door's args (bare `/gtd rollout` included) starts a process from that
|
|
@@ -490,14 +490,22 @@ noted in steps 3 and 4 below.
|
|
|
490
490
|
`spec-challenge` run on the planner model (`smart`), the other three on the
|
|
491
491
|
coder model (`base`); a `qualityReviews` entry `<lens>=<model>` picks a
|
|
492
492
|
lens's own. Each traces the change from its own angle and records every
|
|
493
|
-
finding, blocking or not
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
493
|
+
finding, blocking or not, as its own `## ` chunk of `.gtd/QUALITY.md`. A
|
|
494
|
+
clean turn means approval only when that lens found nothing at all. The
|
|
495
|
+
lenses are the only agentic review: a judge (`build.quality.triage`) then
|
|
496
|
+
classifies each finding — `mechanical` ones are fixed by the fix turn
|
|
497
|
+
(`build.fix.quality.fixing`, up to three turns), `noise` is dropped with its
|
|
498
|
+
`p` recorded as `Gtd-Mark: noise=<id> p=<p> <title>`, and `design` ones
|
|
499
|
+
(questioning a criterion or the approach) become notes for your review. An
|
|
500
|
+
unsure verdict (below `qualityTriageMinP`, `0.7`) is `design`; mechanical
|
|
501
|
+
findings the fix turns leave become notes too. This lap is where code quality
|
|
502
|
+
is looked at, and every round pays for it. It never replaces step 4 — your
|
|
503
|
+
review stays the final gate, and nothing here skips it. Between
|
|
504
|
+
[coarse slices](#coarse-slices) no review follows, so there the fix turn
|
|
505
|
+
fixes every finding, and those the fix cap leaves rest at
|
|
506
|
+
`build.quality-unresolved` until you delete or empty `.gtd/QUALITY.md`; the
|
|
507
|
+
suite then runs on any hand edits before the next slice. The `fix` door
|
|
508
|
+
(below) repairs a red baseline through the untriaged lap.
|
|
501
509
|
|
|
502
510
|
A red suite that keeps failing past a few fix attempts escalates instead of
|
|
503
511
|
retrying forever: an agent turn reads the failing output and writes
|
|
@@ -512,39 +520,43 @@ noted in steps 3 and 4 below.
|
|
|
512
520
|
`Gtd-Mark: flaky=<check>` (see
|
|
513
521
|
[Re-check before a person](docs/configuration.md#re-check-before-a-person)).
|
|
514
522
|
|
|
515
|
-
4. **You review.** After the last slice's quality lap
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
the
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
523
|
+
4. **You review.** After the last slice's quality lap gtd writes the review
|
|
524
|
+
document itself (`build.review.composing`; no reviewer turn runs): one `## `
|
|
525
|
+
chunk per design note the lap left, each ending in three tick boxes — **fix
|
|
526
|
+
as suggested**, **won't fix**, or **your own answer** written over
|
|
527
|
+
`_your answer_` — then, for reference, no tick boxes, no answer needed — what
|
|
528
|
+
was **fixed automatically** (with the fixing commit) and what was **dropped
|
|
529
|
+
as noise**. On `refactor-first` the refactor slice gets one of its own first,
|
|
530
|
+
before the feature is built; see [the naive pass](#the-naive-pass). Review
|
|
531
|
+
the diff yourself (`gtd base` prints its base). **Every note needs exactly
|
|
532
|
+
one tick before `gtd land` accepts the review** (`answer-completeness`; an
|
|
533
|
+
open `- H:` thread waiting on the agent is the one exception). Fix as
|
|
534
|
+
suggested joins the nit batch below, unjudged; won't fix drops the note and
|
|
535
|
+
records it as `Gtd-Mark: wontfix=<title>` on the gate's commit; your own
|
|
536
|
+
answer is judged like a note. A note you answered is left out of the fresh
|
|
537
|
+
review after a nit fix; a round that also answered a question restores the
|
|
538
|
+
review instead, your ticks kept. Land with every note answered and nothing
|
|
539
|
+
else changed to approve, or write what is wrong — disagreeing with a fix or a
|
|
540
|
+
drop means writing a note of your own. Approving ends the process (at the
|
|
541
|
+
refactor review, it moves on to the feature); feedback is judged note by
|
|
542
|
+
note, each as `edit`, `question`, `nit` or `praise`. An `edit` sends the
|
|
543
|
+
process back to step 2 for re-derived requirements — the built criteria keep
|
|
544
|
+
their IDs (reworded in place when their behaviour changes) and new behaviour
|
|
545
|
+
gets new ones — then the implementation is amended to meet them, with no new
|
|
546
|
+
naive pass or seam judge, every criterion's coverage is judged again, the
|
|
547
|
+
quality lap runs, and you review again. At the refactor review an `edit`
|
|
548
|
+
re-derives the refactor instead. A `question` is answered inline under your
|
|
549
|
+
note and the process stops at the review again — no new plan. A `nit` is
|
|
550
|
+
fixed in one batch, the suite must go green (a red one gets fix turns first),
|
|
551
|
+
then a fresh review of the change stops at the review again. `praise` is
|
|
552
|
+
dropped; a round of only praise signs off. When a round mixes them, questions
|
|
553
|
+
are answered and nits fixed first, then the edits are planned — risk: the
|
|
554
|
+
planning lap may redo nit fixes it touches.
|
|
539
555
|
|
|
540
556
|
Each `edit` note also gets a severity tier from four judge questions
|
|
541
|
-
(security, reversibility, blast radius, new concept).
|
|
542
|
-
|
|
543
|
-
a note forces `critical` and skips the judge.
|
|
544
|
-
committed `.gtd/TODO.md`; it never starts a process on its own, and it ships
|
|
545
|
-
in the branch/PR unless you delete it. Landing clean at the gate after a
|
|
546
|
-
deferral signs off without the deferred notes; the gate lists them. To pull
|
|
547
|
-
one in, edit its line in `.gtd/TODO.md` when you start a process.
|
|
557
|
+
(security, reversibility, blast radius, new concept). Every tier is
|
|
558
|
+
re-planned this round, `critical` first, then `should`, then `could`; nothing
|
|
559
|
+
is deferred. A leading `!` on a note forces `critical` and skips the judge.
|
|
548
560
|
`reviewSeverityMinP` sets the confidence floor (see
|
|
549
561
|
[configuration](docs/configuration.md)). A hand-edit to code always plans a
|
|
550
562
|
lap. Only a confident non-`edit` verdict skips the replan — a note whose
|
|
@@ -584,14 +596,16 @@ gtd door review <commitish>
|
|
|
584
596
|
```
|
|
585
597
|
|
|
586
598
|
starts a pure review of everything from `<commitish>` to HEAD. It goes straight
|
|
587
|
-
to
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
599
|
+
to the quality lap and a review, with no planning and no building; unlike
|
|
600
|
+
`feature`, its lap fixes every finding and a reviewer turn
|
|
601
|
+
(`build.review.reviewing`) writes `.gtd/REVIEW.md`, risks and all. Sign off and
|
|
602
|
+
it closes. Leave notes and the process ends with `.gtd/REVIEW.md` (and the
|
|
603
|
+
reviewer's original, `.gtd/REVIEW.orig.md`) left for `gtd door fix`. Until `fix`
|
|
604
|
+
consumes them, starting `feature` or `review` is refused; remove both files to
|
|
605
|
+
abandon the hand-off. `gtd door fix` fixes those notes and every `Risk:` in one
|
|
606
|
+
turn, then asks for your review of the fix. With no review waiting, it repairs a
|
|
607
|
+
red baseline as its own reviewed commit instead. `gtd doors` lists the doors of
|
|
608
|
+
the workflows your `gtd.config.ts` exports, plus any it adds.
|
|
595
609
|
`gtd --workflow <name>` starts any workflow by name.
|
|
596
610
|
|
|
597
611
|
The workflow itself is a plain async TypeScript function, and the
|
|
@@ -730,6 +744,7 @@ full trail with `gtd history fetch | sh`, then `gtd history show <ref>`.
|
|
|
730
744
|
gtd history list # one line per kept process
|
|
731
745
|
gtd history show <ref> # replay its steps, judge verdicts and approvals
|
|
732
746
|
gtd history metrics [ref...] # how the kept processes went, per process and in total
|
|
747
|
+
gtd history judges [ref...] # how often each judge's verdicts were overturned
|
|
733
748
|
gtd history fetch | sh # pull every kept ref from origin
|
|
734
749
|
```
|
|
735
750
|
|
|
@@ -762,6 +777,45 @@ process kept from that branch. A rate is `null` without a decided slice. Each
|
|
|
762
777
|
process names its `workflow` (`null` for the default one); the total spans every
|
|
763
778
|
ref measured, so name the refs to leave another workflow's processes out.
|
|
764
779
|
|
|
780
|
+
`gtd history judges` calibrates three of the bundled `feature` workflow's judges
|
|
781
|
+
from the `Gtd-Judge` verdicts the kept refs recorded. It never replays and asks
|
|
782
|
+
no model. A verdict is **overturned** when later history contradicts it:
|
|
783
|
+
|
|
784
|
+
- **retry** (`health.judge`) — an `identical` verdict, when the next fix turn's
|
|
785
|
+
check went green anyway (no outcome when a person edited code in between)
|
|
786
|
+
- **triage** (`quality.triage`) — a `mechanical` or `noise` verdict the review
|
|
787
|
+
listed under _Fixed automatically_ or _Dropped as noise_, when the human
|
|
788
|
+
edited that finding's line there or wrote under it
|
|
789
|
+
- **clear** (`design.clear`) — a `yes` that skipped design (landed on
|
|
790
|
+
`design.normalise`), when a later human design step in the same process
|
|
791
|
+
changed the acceptance criteria
|
|
792
|
+
|
|
793
|
+
Any other verdict has no outcome. The report counts verdicts, scored verdicts
|
|
794
|
+
and overturns per judge and per `p` bucket (`0.8-0.9: 1/2` — one of two
|
|
795
|
+
overturned). `--var <threshold>=<value>` re-decides every past verdict under
|
|
796
|
+
another threshold and prints the decisions that would change, against the
|
|
797
|
+
threshold each process recorded (a process that recorded none is left out):
|
|
798
|
+
|
|
799
|
+
```sh
|
|
800
|
+
gtd history judges --var qualityTriageMinP=0.9
|
|
801
|
+
# what-if qualityTriageMinP=0.9: 7 noise drops become notes (2 overturned)
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
The thresholds are `judgeIdenticalMinP` (retry) and `qualityTriageMinP`
|
|
805
|
+
(triage). The judge's evidence-budget cut is not recorded, so a cut verdict is
|
|
806
|
+
scored and re-decides as if it was whole. A retry verdict on an empty report is
|
|
807
|
+
not scored, as the flow drops it. `--json` prints
|
|
808
|
+
`{ processes, total, whatIf }`, each process and the total with its `judges`.
|
|
809
|
+
`--jsonl` prints one line per verdict instead — `process`, `judge`, `commit`,
|
|
810
|
+
`step`, `question`, `state` (the evidence the tree still shows: the finding, the
|
|
811
|
+
two check reports, the sketch without its numbered criteria), `answer`, `p` and
|
|
812
|
+
`outcome` (`overturned`, `held` or `null`) — ready to fine-tune a local model
|
|
813
|
+
on:
|
|
814
|
+
|
|
815
|
+
```sh
|
|
816
|
+
gtd history judges --jsonl > judges.jsonl
|
|
817
|
+
```
|
|
818
|
+
|
|
765
819
|
`gtd history fetch` only prints a fetch script; pipe it into `sh` to run it.
|
|
766
820
|
Beyond that one ship push, refs are not pushed or fetched by default. The ref
|
|
767
821
|
holds every step commit, including steering files and code a later step removed,
|
|
@@ -818,8 +872,8 @@ install for any of these but `./testkit`: gtd resolves a `gtd.config.ts`'s
|
|
|
818
872
|
The `gtd` bin's commands — `next`, `land`, `abandon`, `restore`, `validate`,
|
|
819
873
|
`check`, `uncheck`, `install`, `summary`, `base`, `door`, `doors`, `slices`,
|
|
820
874
|
`version`, `help`, `history list`, `history show`, `history metrics`,
|
|
821
|
-
`history fetch`, `strays`, and the tools `ui`, `lsp`, `loop`,
|
|
822
|
-
`judge` (`judge answer`, `judge run`) — are listed in the
|
|
875
|
+
`history judges`, `history fetch`, `strays`, and the tools `ui`, `lsp`, `loop`,
|
|
876
|
+
`context-hook`, `judge` (`judge answer`, `judge run`) — are listed in the
|
|
823
877
|
[CLI reference](https://github.com/pmelab/gtd/blob/main/docs/cli.md).
|
|
824
878
|
`gtd slices` lists the spans a flow's `slice()` calls mark. It refuses (exit 1)
|
|
825
879
|
when HEAD neither closes nor sits inside a process, or when today's config
|
package/claude/hooks/sketch.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// New requirements go above what the steering file already holds, untouched:
|
|
2
|
-
//
|
|
2
|
+
// anything already there that the start diff alters reads as the human's edit.
|
|
3
3
|
export function withRequirements(existing: string, requirements: string): string {
|
|
4
4
|
const head = `${requirements.trimEnd()}\n`
|
|
5
5
|
return existing.trim() ? `${head}\n${existing}` : head
|
|
@@ -1,4 +1,5 @@
|
|
|
1
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
|
+
import { d as ANSWER_FIX, f as ANSWER_LEAD, p as ANSWER_WONT_FIX, v as FREE_TEXT_PLACEHOLDER } from "./steering-D4LUQ-av.mjs";
|
|
2
3
|
import { escalation as escalation$1, gate as gate$1, healthy as healthy$1, lensScopes, lensesOf, removedAssertions } from "../workflow-kit.mjs";
|
|
3
4
|
import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct, n as Boolean$, s as Number$ } from "./Schema-C6fLlQzx.mjs";
|
|
4
5
|
//#region src/workflows/shared/registry.ts
|
|
@@ -130,6 +131,7 @@ const defaults = {
|
|
|
130
131
|
judgeIdenticalMinP: "0.7",
|
|
131
132
|
reviewNoteActionable: "0.7",
|
|
132
133
|
reviewSeverityMinP: "0.7",
|
|
134
|
+
qualityTriageMinP: "0.7",
|
|
133
135
|
refactorFlagPattern: "\\bGTD_FLAG_\\w+",
|
|
134
136
|
seamAllow: [...testFileGlobs, ".gtd/**"].join(", "),
|
|
135
137
|
flagMode: "true",
|
|
@@ -420,6 +422,51 @@ carries it.
|
|
|
420
422
|
What each change does next (then run \`gtd land\`):
|
|
421
423
|
- **Continue** — having undone the sketch by hand, check the test baseline is green and start triage (**start-gate.check**).
|
|
422
424
|
`;
|
|
425
|
+
const qualityTriageMessage = () => `Judging each quality finding. Run \`gtd judge answer\` and pipe a
|
|
426
|
+
\`mechanical\`/\`noise\`/\`design\` verdict per finding ID — or land without a
|
|
427
|
+
verdict, which hands every finding to the review as a note.
|
|
428
|
+
`;
|
|
429
|
+
/** \`.gtd/QUALITY.md\` holding only \`findings\`, one \`## \` chunk each. */
|
|
430
|
+
const qualityFindingsFile = (findings) => `# Quality\n\n${findings.map((f) => `## ${f.title}\n\n${f.body}`.trim()).join("\n\n")}\n`;
|
|
431
|
+
const SUMMARY_TITLES = [
|
|
432
|
+
"Weakened scenarios",
|
|
433
|
+
"Fixed from your notes",
|
|
434
|
+
"Fixed automatically",
|
|
435
|
+
"Dropped as noise",
|
|
436
|
+
"Nothing to note"
|
|
437
|
+
];
|
|
438
|
+
const unboxed = (body) => body.replace(/^(\s*(?:>\s*)*(?:[-*+]|\d+[.)])\s+)\[[ xX]\]\s?/gm, "$1");
|
|
439
|
+
const short = (hash) => hash.slice(0, 7);
|
|
440
|
+
/**
|
|
441
|
+
* \`.gtd/REVIEW.md\` as gtd writes it: one chunk per note, each ending in its
|
|
442
|
+
* answer boxes, then the fixed and noise lists. Chunk titles are made unique —
|
|
443
|
+
* notes and answers are matched to their chunk by title.
|
|
444
|
+
*/
|
|
445
|
+
const composedReview = (base, report) => {
|
|
446
|
+
const used = new Set(SUMMARY_TITLES);
|
|
447
|
+
const unique = (title) => {
|
|
448
|
+
let name = title;
|
|
449
|
+
for (let n = 2; used.has(name); n++) name = `${title} (${n})`;
|
|
450
|
+
used.add(name);
|
|
451
|
+
return name;
|
|
452
|
+
};
|
|
453
|
+
const chunk = (title, body) => `## ${title}\n\n${body.trim()}`;
|
|
454
|
+
const answerBoxes = `${ANSWER_LEAD}\n\n- [ ] ${ANSWER_FIX}\n- [ ] ${ANSWER_WONT_FIX}\n- [ ] ${FREE_TEXT_PLACEHOLDER}`;
|
|
455
|
+
const notes = report.notes.map((n) => ({
|
|
456
|
+
...n,
|
|
457
|
+
title: unique(n.title)
|
|
458
|
+
})).filter((n) => report.answered?.has(n.title) !== true);
|
|
459
|
+
const weakened = (report.weakened ?? []).flatMap(({ path, lines }) => lines.map((a) => `- ${path} — ${a.scenario} — ${a.line}`));
|
|
460
|
+
const chunks = [
|
|
461
|
+
...report.nitFix === void 0 ? [] : [chunk("Fixed from your notes", `Your nits and fix-as-suggested notes were worked on in ${short(report.nitFix)}; the notes you answered are left out — check the diff.`)],
|
|
462
|
+
...weakened.length === 0 ? [] : [chunk("Weakened scenarios", `These scenarios lost an assertion the review base had:\n\n${weakened.join("\n")}`)],
|
|
463
|
+
...notes.map((n) => chunk(n.title, `${n.unresolved === true ? `_The fixer left this unresolved._\n\n${unboxed(n.body)}` : n.unclear === true ? `_A fix turn removed this while renaming another finding — check whether it was fixed._\n\n${unboxed(n.body)}` : unboxed(n.body) || "_No detail given._"}\n\n${answerBoxes}`)),
|
|
464
|
+
...report.fixed.length === 0 ? [] : [chunk("Fixed automatically", report.fixed.map((f) => `- ${f.title} (${short(f.commit)})`).join("\n"))],
|
|
465
|
+
...report.noise.length === 0 ? [] : [chunk("Dropped as noise", report.noise.map((f) => `- ${f.title} (p=${f.p})`).join("\n"))]
|
|
466
|
+
];
|
|
467
|
+
const body = chunks.length > 0 ? chunks : [chunk("Nothing to note", "No quality lens raised anything for you — or none ran. Review the diff yourself.")];
|
|
468
|
+
return `# Review: ${short(base)}\n\n<!-- base: ${base} -->\n\n${body.join("\n\n")}\n`;
|
|
469
|
+
};
|
|
423
470
|
const qualityUnresolvedMessage = () => `The quality lap's fix turns hit their cap and \`.gtd/QUALITY.md\` still
|
|
424
471
|
holds findings. Nothing after this fixes them, so the process will not
|
|
425
472
|
go on with them open.
|
|
@@ -496,10 +543,6 @@ ${stateFileRules}
|
|
|
496
543
|
${footnoteFoldIn}${codeThreadReplies()}
|
|
497
544
|
- The only state file this turn touches is \`.gtd/REQUIREMENTS.md\`
|
|
498
545
|
— no other files for notes or output
|
|
499
|
-
- Lines of a \`## Deferred review notes\` section of \`.gtd/TODO.md\`
|
|
500
|
-
that the start diff does not touch are parked by review, never folded;
|
|
501
|
-
the human's edits to such a line (or its removal) are input like any
|
|
502
|
-
other part of the start diff
|
|
503
546
|
- \`.gtd/TODO.md\` is the likely home of the sketch that started
|
|
504
547
|
this process — the human's input, folded into the concerns
|
|
505
548
|
below like any other part of the start diff; never a state
|
|
@@ -728,8 +771,10 @@ const finisherSystem = () => `${finisherPersona}
|
|
|
728
771
|
${agentConduct}`;
|
|
729
772
|
const buildFixQualityPrompt = (rules) => `${stateFileRules}
|
|
730
773
|
- Read \`.gtd/QUALITY.md\` — one \`## \` chunk per finding a quality
|
|
731
|
-
dimension wrote.
|
|
732
|
-
|
|
774
|
+
dimension wrote. Fix every finding in every chunk, blocking or not;
|
|
775
|
+
where dimensions repeat one, fix the shared cause once
|
|
776
|
+
- Delete each finding's \`## \` chunk once it is resolved; never
|
|
777
|
+
rename, merge or reword a chunk you leave
|
|
733
778
|
- When findings conflict, missing test signal beats line count —
|
|
734
779
|
a test is never deleted to satisfy a simplification finding
|
|
735
780
|
- Delete \`.gtd/QUALITY.md\` once every finding is resolved
|
|
@@ -772,9 +817,9 @@ const buildQualityReviewingPrompt = (lens, brief, base = start()) => `${stateFil
|
|
|
772
817
|
behaviour removed — decide that by reasoning, never by running a
|
|
773
818
|
mutation-testing tool. A test that pins a bug is a finding, not
|
|
774
819
|
praise
|
|
775
|
-
- APPEND every finding you have, blocking or not, as
|
|
776
|
-
chunk to \`.gtd/QUALITY.md\` —
|
|
777
|
-
dimension already wrote there
|
|
820
|
+
- APPEND every finding you have, blocking or not, as its own
|
|
821
|
+
\`## <one-line title>\` chunk to \`.gtd/QUALITY.md\` — one finding per
|
|
822
|
+
chunk, never overwrite what an earlier dimension already wrote there
|
|
778
823
|
- Write nothing only when this lens found nothing at all — then a
|
|
779
824
|
clean turn IS this dimension's approval
|
|
780
825
|
${brief ? `
|
|
@@ -786,12 +831,16 @@ ${brief}
|
|
|
786
831
|
const reviewerSystem = () => `${reviewerPersona}
|
|
787
832
|
|
|
788
833
|
${agentConduct}`;
|
|
789
|
-
const buildReviewAwaitReviewMessage = (base) => `\`.gtd/REVIEW.md\` holds the
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
834
|
+
const buildReviewAwaitReviewMessage = (base) => `\`.gtd/REVIEW.md\` holds what the quality lenses left for you: one chunk
|
|
835
|
+
per finding that needs your judgement — it questions a criterion or the
|
|
836
|
+
approach, or the fixer could not resolve it — then, for reference only, what
|
|
837
|
+
was **fixed automatically** (with the fixing commit) and what was **dropped
|
|
838
|
+
as noise**. Each finding chunk ends in three boxes — tick exactly one:
|
|
839
|
+
\`fix as suggested\` (fixed in the nit batch below), \`won't fix\` (dropped,
|
|
840
|
+
recorded in the commit), or write your own answer over \`_your answer_\` and
|
|
841
|
+
tick it (judged like a note). Every landing here is refused until each
|
|
842
|
+
finding holds exactly one tick. The fixed and noise lists need no answer;
|
|
843
|
+
disagreeing with a fix or a drop means writing a note of your own.
|
|
795
844
|
|
|
796
845
|
Review the diff yourself, with whatever tool you like — gtd checks
|
|
797
846
|
nothing out and touches no ref; \`gtd base\` prints this same hash any
|
|
@@ -801,9 +850,9 @@ working tree:
|
|
|
801
850
|
git diff ${base}
|
|
802
851
|
When you've been through the whole diff, run \`gtd land\`:
|
|
803
852
|
|
|
804
|
-
- **Sign off** — leave no comment — no note
|
|
805
|
-
\`.gtd/REVIEW.md\`, no code edit — to close the process
|
|
806
|
-
|
|
853
|
+
- **Sign off** — answer every finding, leave no other comment — no note
|
|
854
|
+
in \`.gtd/REVIEW.md\`, no code edit — to close the process.
|
|
855
|
+
Every turn commit stays on the
|
|
807
856
|
branch; run \`gtd summary\` afterward for a closing-message
|
|
808
857
|
prompt.
|
|
809
858
|
- **Ask a question** — start a thread (\`- H: <question>\`) on a
|
|
@@ -949,27 +998,6 @@ const wrapItem = (item) => {
|
|
|
949
998
|
} else line = line === "" ? word : `${line} ${word}`;
|
|
950
999
|
return [...out, line];
|
|
951
1000
|
};
|
|
952
|
-
/** Appends one section of `notes` to `.gtd/TODO.md`, wrapped the way oxfmt writes it; an existing file is only ever appended to. */
|
|
953
|
-
const deferNotesScript = (notes, round) => {
|
|
954
|
-
return `mkdir -p .gtd
|
|
955
|
-
if [ -s .gtd/TODO.md ]; then
|
|
956
|
-
[ -z "$(tail -c1 .gtd/TODO.md)" ] || printf '\\n' >> .gtd/TODO.md
|
|
957
|
-
printf '\\n' >> .gtd/TODO.md
|
|
958
|
-
fi
|
|
959
|
-
printf '%s\\n' ${[
|
|
960
|
-
`## Deferred review notes (${round.slice(0, 7)})`,
|
|
961
|
-
"",
|
|
962
|
-
...notes.flatMap((n) => wrapItem(`- ${noteHead(n)}: ${n.text.replace(/\s+/g, " ")}`))
|
|
963
|
-
].map(quote).join(" ")} >> .gtd/TODO.md
|
|
964
|
-
`;
|
|
965
|
-
};
|
|
966
|
-
/** The gate right after a deferral: landing clean signs off without these notes. */
|
|
967
|
-
const buildReviewAwaitAfterDeferMessage = (base, deferred) => `${buildReviewAwaitReviewMessage(base)}
|
|
968
|
-
Deferred to \`.gtd/TODO.md\` just now, NOT acted on in this process — landing
|
|
969
|
-
clean signs off without them:
|
|
970
|
-
|
|
971
|
-
${deferred.map((n) => `- ${noteHead(n)}: ${n.text.replace(/\s*\n\s*/g, " ")}`).join("\n")}
|
|
972
|
-
`;
|
|
973
1001
|
const buildReviewTriageMessage = () => `Judging each note the human added to \`.gtd/REVIEW.md\` (a line note, a
|
|
974
1002
|
footnote, or prose under a chunk) with one verdict: \`edit\` (a change
|
|
975
1003
|
request), \`question\`, \`nit\` (a small fix needing no re-plan) or
|
|
@@ -979,16 +1007,18 @@ default; a skipped judgment never dismisses a note).
|
|
|
979
1007
|
|
|
980
1008
|
Also judge each note's severity, one id per note: \`note-<n>.security\`,
|
|
981
1009
|
\`note-<n>.reversibility\`, \`note-<n>.blastRadius\` and
|
|
982
|
-
\`note-<n>.newConcept\`.
|
|
1010
|
+
\`note-<n>.newConcept\`. An own answer under a finding's boxes is judged
|
|
1011
|
+
the same way, keyed \`answer-<n>\`.
|
|
983
1012
|
`;
|
|
984
1013
|
const closedFold = (round, target) => round === "edits" ? `- Fold every hand-edit and every note the human left that round into
|
|
985
|
-
\`${target}\`
|
|
1014
|
+
\`${target}\` — a note ticked \`fix as suggested\` or answered in its
|
|
1015
|
+
own words included; one ticked \`won't fix\` is dropped
|
|
986
1016
|
` : round === "notes" ? `- Fold each note that asks for a change into \`${target}\`
|
|
987
1017
|
` : `- Fold each \`edit\` note the capture lists, no other note, and every
|
|
988
1018
|
hand-edit, into \`${target}\`
|
|
989
1019
|
`;
|
|
990
1020
|
/** `refactor` retargets the prompt at `.gtd/REFACTOR.md` and fences it to the strays. A concern outside the fence is not folded; if nothing at all is written the landing is refused, but a partly buildable round drops the rest silently. */
|
|
991
|
-
const buildReviewCollectingPrompt = (capture, refactor) => {
|
|
1021
|
+
const buildReviewCollectingPrompt = (capture, refactor, round) => {
|
|
992
1022
|
const target = refactor ? ".gtd/REFACTOR.md" : ".gtd/REQUIREMENTS.md";
|
|
993
1023
|
const fence = refactor ? `- The next refactor may change only these paths, plus an existing test
|
|
994
1024
|
file's call sites, and never a flag read:
|
|
@@ -1063,12 +1093,16 @@ ${refactor ? refactor.round === "reply" ? `- This turn replies inside \`.gtd/REV
|
|
|
1063
1093
|
${closedFold(refactor.round, target)}` : `- This turn writes \`${target}\` (the folded concerns) and
|
|
1064
1094
|
replies inside \`.gtd/REVIEW.md\` (thread replies only; the file stays
|
|
1065
1095
|
in the tree) — you classify, you do not build
|
|
1066
|
-
- Fold every concluded thread
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1096
|
+
- Fold every concluded thread and hand-edit — and each \`edit\` note
|
|
1097
|
+
the capture lists, no other note — into \`${target}\` and
|
|
1098
|
+
delete the folded threads from \`.gtd/REVIEW.md\`; append one \`- A:\`
|
|
1099
|
+
reply to each thread whose last entry is a \`- H:\` question
|
|
1100
|
+
${round === "edits" ? `- A hand-edit round routes no answer box itself: fold each note
|
|
1101
|
+
ticked \`fix as suggested\` or answered in its own words, too; drop one
|
|
1102
|
+
ticked \`won't fix\`
|
|
1103
|
+
` : `- Never fold a note's ticked answer box the capture does not list: gtd
|
|
1104
|
+
routes those itself
|
|
1105
|
+
`}`}- When the capture names an answering commit, carry each answered
|
|
1072
1106
|
question into \`${target}\`'s \`## Answered Questions\` as
|
|
1073
1107
|
\`### <the note>\` plus its answer — the lap replaces \`.gtd/REVIEW.md\`
|
|
1074
1108
|
- Keep capture order, and carry each note's \`(tier: reason)\`
|
|
@@ -1691,6 +1725,7 @@ const ShippedOptions = Struct({
|
|
|
1691
1725
|
judgeIdenticalMinP: setting,
|
|
1692
1726
|
reviewNoteActionable: setting,
|
|
1693
1727
|
reviewSeverityMinP: setting,
|
|
1728
|
+
qualityTriageMinP: setting,
|
|
1694
1729
|
refactorFlagPattern: setting,
|
|
1695
1730
|
seamAllow: setting,
|
|
1696
1731
|
sliceMaxCriteria: setting,
|
|
@@ -1785,11 +1820,9 @@ const skills = (vars) => {
|
|
|
1785
1820
|
const access = (vars) => ({
|
|
1786
1821
|
design: { write: [] },
|
|
1787
1822
|
"build.review": { write: [".gtd/REVIEW.md"] },
|
|
1788
|
-
"build.review.defer": { write: [".gtd/TODO.md"] },
|
|
1789
1823
|
"build.review.fix.nits": {},
|
|
1790
1824
|
"build.review.fix.risks": {},
|
|
1791
1825
|
"refactor.build.review": { write: [".gtd/REVIEW.md"] },
|
|
1792
|
-
"refactor.build.review.defer": { write: [".gtd/TODO.md"] },
|
|
1793
1826
|
...lensScopes("build.quality", lensesOf(vars.qualityReviews ?? ""), () => ({ write: [] })),
|
|
1794
1827
|
...lensScopes("refactor.build.quality", lensesOf(vars.refactorQualityReviews ?? ""), () => ({ write: [] }))
|
|
1795
1828
|
});
|
|
@@ -1801,4 +1834,4 @@ const steering = {
|
|
|
1801
1834
|
[REVIEW]: "review"
|
|
1802
1835
|
};
|
|
1803
1836
|
//#endregion
|
|
1804
|
-
export {
|
|
1837
|
+
export { naivePassPrompt as $, agentWithSkills as A, coverageQuestion as B, fixCheck as C, envDefaults as Ct, reviewMissing as D, flipProblems as Dt, planner as E, flagReferences as Et, builderSystem as F, designTriagePrompt as G, criterionLine as H, clearCriteriaMessage as I, handOffAwaitReviewMessage as J, finisherSystem as K, clearCriteriaQuestion as L, buildReviewCollectingPrompt as M, buildReviewFixNitsPrompt as N, reviewQuality as O, registryEntry as Ot, buildReviewTriageMessage as P, naiveApplyFailure as Q, composedReview as R, coder as S, defaults as St, fixRisks as T, flagMentions as Tt, designGateAnswerMessage as U, coveringPrompt as V, designSystem as W, implementBuildingPrompt as X, idleMessage as Y, implementFixPrompt as Z, REFACTOR_NOTE as _, slicingPrompt as _t, flagModeOn as a, refactorBuildingPrompt as at, SLICES as b, unwindFailedMessage as bt, baseline as c, refactorNotePrompt as ct, guarded as d, reviewEditsCapture as dt, normaliseCriteriaScript as et, healthy as f, reviewGateBlockedMessage as ft, REFACTOR as g, seamSteerMessage as gt, QUALITY as h, seamByHandMessage as ht, skills as i, qualityUnresolvedMessage as it, buildReviewAnswerQuestionsPrompt as j, reviewing as k, requireFlag as kt, escalation as l, refactorRules as lt, HANDOFF as m, reviewerSystem as mt, summary as n, qualityFindingsFile as nt, flagShipped as o, refactorDroppedRefusal as ot, FEEDBACK as p, reviewNotesCapture as pt, fixReviewNotesPrompt as q, access as r, qualityTriageMessage as rt, shipped as s, refactorFixPrompt as st, steering as t, oversizedMessage as tt, gate as u, reviewEditNotesCapture as ut, REQUIREMENTS as v, startGateBlockedMessage as vt, fixQuality as w, testFileGlobs as wt, awaitReview as x, unwindFailure as xt, REVIEW as y, uncoveredMessage as yt, coverageMessage as z };
|