@pmelab/gtd 55.0.0 → 56.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 -61
- package/dist/chunks/{reserved-DHafcW7t.mjs → reserved-B8EC-WE8.mjs} +77 -203
- package/dist/chunks/{review-BJGKwlYv.mjs → review-HdrX0FJ7.mjs} +30 -125
- package/dist/chunks/{steering-CLhu8C7q.mjs → steering-CCNvngSH.mjs} +256 -65
- package/dist/chunks/{tail-B9Hw2AFB.mjs → tail-BSLah2mA.mjs} +144 -56
- package/dist/gtd.bundle.mjs +259 -22
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +6 -6
- package/dist/web/main.css +1 -1
- package/dist/web/main.js +551 -66
- package/dist/workflow-kit.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +14 -16
- package/dist/workflows/fix.mjs +3 -3
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +522 -453
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -3
- package/src/cli/Cli.ts +5 -5
- package/src/edge/Edge.test.ts +1 -1
- package/src/steering/FORMATS.md +5 -1
- package/src/steering/SteeringFormats.test.ts +2 -2
- package/src/steering/contract.test.ts +1 -0
- package/src/steering/findings.test.ts +47 -0
- package/src/steering/findings.ts +350 -0
- package/src/steering/fixtures/findings/gate.md +34 -0
- package/src/steering/fixtures/findings/gate.read.json +87 -0
- package/src/steering/index.test.ts +2 -2
- package/src/steering/index.ts +4 -3
- package/src/steering/review.test.ts +1 -75
- package/src/steering/review.ts +1 -74
- package/src/workflow/compile.test.ts +1 -0
- package/src/workflows/feature/criteria-coverage.feature +1 -6
- package/src/workflows/feature/feature.test.ts +1 -1
- package/src/workflows/feature/feature.ts +12 -20
- package/src/workflows/feature/lens-review.feature +323 -171
- package/src/workflows/feature/refactor-absorbs.feature +1 -7
- package/src/workflows/feature/refactor-first.feature +2 -10
- package/src/workflows/feature/refactor-rebuild.feature +4 -24
- package/src/workflows/feature/refactor-review.feature +33 -117
- package/src/workflows/feature/refactor.test.ts +0 -2
- package/src/workflows/feature/refactor.ts +0 -1
- package/src/workflows/feature/review-gate.feature +6 -22
- package/src/workflows/feature/reviewer-walkthrough.feature +16 -103
- package/src/workflows/feature/standards.feature +75 -34
- package/src/workflows/fix/fix-lens-origin.feature +20 -7
- package/src/workflows/fix/fix-quality.feature +102 -23
- package/src/workflows/review/review-door.feature +62 -59
- package/src/workflows/shared/learn.test.ts +0 -14
- package/src/workflows/shared/learn.ts +1 -6
- package/src/workflows/shared/lenses.ts +149 -43
- package/src/workflows/shared/review.test.ts +27 -26
- package/src/workflows/shared/review.ts +29 -196
- package/src/workflows/shared/tail.ts +18 -9
- package/src/workflows/shared/text.test.ts +103 -288
- package/src/workflows/shared/text.ts +131 -329
- package/src/workflows/feature/review-answers.feature +0 -324
- package/src/workflows/feature/severity-budget.feature +0 -116
- package/src/workflows/feature/severity-sorted-review.feature +0 -198
package/README.md
CHANGED
|
@@ -490,12 +490,21 @@ noted in steps 3 and 4 below.
|
|
|
490
490
|
fixed by the fix turn (`build.fix.quality.fixing`, up to three turns),
|
|
491
491
|
`noise` is dropped with its `p` recorded as
|
|
492
492
|
`Gtd-Mark: noise=<id> p=<p> <title>`, and `design` ones (questioning a
|
|
493
|
-
criterion or the approach) become notes
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
493
|
+
criterion or the approach) become notes. An unsure verdict (below
|
|
494
|
+
`qualityTriageMinP`, `0.7`) is `design`; mechanical findings the fix turns
|
|
495
|
+
leave become notes too. **The finding gate** (`build.quality.gate`) then
|
|
496
|
+
stops for you: `.gtd/QUALITY.md` lists the notes, then the noise under
|
|
497
|
+
`# Dropped as noise` so you can overrule the judge, each with one `- [ ] fix`
|
|
498
|
+
box. Tick a finding to have it fixed, optionally with a line of guidance
|
|
499
|
+
below its box; leave it unticked to drop it — landing the file untouched
|
|
500
|
+
drops them all, and guidance under an unticked box is refused. The ticked
|
|
501
|
+
ones go to the same fix turns (up to three), then the suite is kept green;
|
|
502
|
+
what they leave is listed as unresolved, with no second gate. A ticked
|
|
503
|
+
finding is fixed, never re-planned: raise anything bigger in your review. A
|
|
504
|
+
lap with no notes and no noise has no gate. This lap is where code quality is
|
|
505
|
+
looked at, and every round pays for it. It never replaces step 4 — your
|
|
506
|
+
review stays the final gate, and nothing here skips it. The `fix` and
|
|
507
|
+
`cleanup` doors run the same lap and gate before their own review.
|
|
499
508
|
|
|
500
509
|
A red suite that keeps failing past a few fix attempts escalates instead of
|
|
501
510
|
retrying forever: an agent turn reads the failing output and writes
|
|
@@ -513,46 +522,37 @@ noted in steps 3 and 4 below.
|
|
|
513
522
|
4. **You review.** After the quality lap a reviewer turn
|
|
514
523
|
(`build.review.reviewing`) walks you through the whole change: `## ` chunks
|
|
515
524
|
grouped by concern, **every chunk pointing at its code**
|
|
516
|
-
(`./path#start-end`), so the phone shows the diff beside each one.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
**
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
note
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
naive pass or seam judge, the hands-on test runs again, the quality lap runs,
|
|
548
|
-
and you review again. At the refactor review an `edit` re-derives the
|
|
549
|
-
refactor instead. A `question` is answered inline under your note and the
|
|
550
|
-
process stops at the review again — no new plan. A `nit` is fixed in one
|
|
551
|
-
batch, the suite must go green (a red one gets fix turns first), then a fresh
|
|
552
|
-
review of the change stops at the review again. `praise` is dropped; a round
|
|
553
|
-
of only praise signs off. When a round mixes them, questions are answered and
|
|
554
|
-
nits fixed first, then the edits are planned — risk: the planning lap may
|
|
555
|
-
redo nit fixes it touches.
|
|
525
|
+
(`./path#start-end`), so the phone shows the diff beside each one. gtd
|
|
526
|
+
appends one `## Quality lap` chunk (`build.review.carrying`) listing, for
|
|
527
|
+
reference, what the quality lap did with every finding: **fixed
|
|
528
|
+
automatically** and **fixed at the finding gate** (each with its fixing
|
|
529
|
+
commit), **dropped at the finding gate**, **dropped as noise**, and
|
|
530
|
+
**unresolved after the finding gate**. To reopen one, write a note under its
|
|
531
|
+
line. A review with a chunk pointing at no code is refused
|
|
532
|
+
(`review-pointers`). A pointer note the reviewer opens with `Risk:` is fixed
|
|
533
|
+
before you see the review (`build.review.fix.risks.fixing`), then the review
|
|
534
|
+
is written again over the reviewer's own draft. On `refactor-first` the
|
|
535
|
+
refactor gets a walkthrough of its own first, covering the whole refactor on
|
|
536
|
+
every round, before the feature is built; see
|
|
537
|
+
[the naive pass](#the-naive-pass). Land with nothing changed to approve, or
|
|
538
|
+
write what is wrong. A note added with its own `Tick exactly one:` boxes (as
|
|
539
|
+
`/gtd pr` imports a thread) is answered by its tick instead of judged: fix as
|
|
540
|
+
suggested joins the nit batch below, won't fix drops it and records
|
|
541
|
+
`Gtd-Mark: wontfix=<title>`, your own answer is judged like a note. Approving
|
|
542
|
+
ends the process (at the refactor review, it moves on to the feature);
|
|
543
|
+
feedback is judged note by note, each as `edit`, `question`, `nit` or
|
|
544
|
+
`praise`. An `edit` sends the process back to step 2 for re-derived
|
|
545
|
+
requirements — the built criteria keep their IDs (reworded in place when
|
|
546
|
+
their behaviour changes) and new behaviour gets new ones — then the
|
|
547
|
+
implementation is amended to meet them, with no new naive pass or seam judge,
|
|
548
|
+
the hands-on test runs again, the quality lap runs, and you review again. At
|
|
549
|
+
the refactor review an `edit` re-derives the refactor instead. A `question`
|
|
550
|
+
is answered inline under your note and the process stops at the review again
|
|
551
|
+
— no new plan. A `nit` is fixed in one batch, the suite must go green (a red
|
|
552
|
+
one gets fix turns first), then a fresh review of the change stops at the
|
|
553
|
+
review again. `praise` is dropped; a round of only praise signs off. When a
|
|
554
|
+
round mixes them, questions are answered and nits fixed first, then the edits
|
|
555
|
+
are planned — risk: the planning lap may redo nit fixes it touches.
|
|
556
556
|
|
|
557
557
|
Each `edit` note also gets a severity tier from four judge questions
|
|
558
558
|
(security, reversibility, blast radius, new concept). Every tier is
|
|
@@ -600,9 +600,10 @@ conversation under their block, question, chunk, or hunk, and one the agent
|
|
|
600
600
|
answered last carries a "waiting on you" badge. Tap it to reply — your reply is
|
|
601
601
|
added as a new `H:` entry; while the agent has yet to answer, reopening it edits
|
|
602
602
|
your last entry instead. Tap an already-answered question to leave a note on it;
|
|
603
|
-
the agent may overturn the answer.
|
|
604
|
-
|
|
605
|
-
|
|
603
|
+
the agent may overturn the answer. At the finding gate the phone lists the open
|
|
604
|
+
findings, then those dropped as noise, each with a fix checkbox and, once
|
|
605
|
+
ticked, an optional line of guidance; Done stays disabled while the file holds
|
|
606
|
+
anything the gate would refuse. See
|
|
606
607
|
[Configuration](https://github.com/pmelab/gtd/blob/main/docs/configuration.md)
|
|
607
608
|
for its `ui:` settings.
|
|
608
609
|
|
|
@@ -616,17 +617,15 @@ gtd door review <commitish>
|
|
|
616
617
|
|
|
617
618
|
starts a review of everything from `<commitish>` to HEAD — the same review a
|
|
618
619
|
`feature` process ends with, minus planning and building. A green gate, then the
|
|
619
|
-
quality lenses: mechanical findings are fixed,
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
start one. `gtd door fix` repairs a red baseline as its own reviewed commit; a
|
|
629
|
-
note at its review is left in `.gtd/REVIEW.md` (beside the reviewer's original,
|
|
620
|
+
quality lenses: mechanical findings are fixed, the rest wait at the finding
|
|
621
|
+
gate, then a reviewer walks you through the change in `.gtd/REVIEW.md`, with the
|
|
622
|
+
lap's summary. A nit note is fixed in place; a note asking for a change is
|
|
623
|
+
collected into `.gtd/REQUIREMENTS.md` and built by one `feedback.fixing` turn,
|
|
624
|
+
with no design phase and no criteria — under the health loop, then it asks for
|
|
625
|
+
your review again. Sign off and it closes; nothing is left behind. A note asking
|
|
626
|
+
for new behaviour belongs in a `feature` process: sign off and start one.
|
|
627
|
+
`gtd door fix` repairs a red baseline as its own reviewed commit; a note at its
|
|
628
|
+
review is left in `.gtd/REVIEW.md` (beside the reviewer's original,
|
|
630
629
|
`.gtd/REVIEW.orig.md`) for the next `gtd door fix`, and until that runs,
|
|
631
630
|
starting `feature` or `review` is refused — remove both files to abandon the
|
|
632
631
|
hand-off. `gtd doors` lists the doors of the workflows your `gtd.config.ts`
|
|
@@ -884,7 +883,7 @@ gtd is one package, `@pmelab/gtd`, and every entrypoint shares its version and
|
|
|
884
883
|
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
885
884
|
| `@pmelab/gtd/flows` | The flow API a workflow is written in: the steps (`agent`, `human`, `run`, …), `scope`, workflow factories, `defineConfig` and `FLOWS_PROTOCOL`. |
|
|
886
885
|
| `@pmelab/gtd/wire` | The driver protocol: `WIRE_SCHEMA`, the exit codes and the types of every `--json` document. |
|
|
887
|
-
| `@pmelab/gtd/steering` | The steering-file formats (`qa`, `review`) and the review-note readers.
|
|
886
|
+
| `@pmelab/gtd/steering` | The steering-file formats (`qa`, `review`, `findings`) and the review-note readers. |
|
|
888
887
|
| `@pmelab/gtd/workflow-kit` | Policy-free building blocks to compose a workflow from: health checks, quality laps, scenario freezing, review tails. |
|
|
889
888
|
| `@pmelab/gtd/workflows` | Every shipped workflow and all its parts in one module; also resolvable as `@pmelab/gtd/workflow`. |
|
|
890
889
|
| `@pmelab/gtd/workflows/feature`, `@pmelab/gtd/workflows/review`, `@pmelab/gtd/workflows/fix`, `@pmelab/gtd/workflows/rollout`, `@pmelab/gtd/workflows/cleanup` | One shipped workflow each: its factory as the default export, its parts, and the reserved exports (`defaults`, `envDefaults`, `summary`, `steering`, `skills`, `access`). `gtd install` imports these. |
|
|
@@ -1,5 +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 {
|
|
2
|
+
import { f as NOISE_HEADING, h as ANSWER_LEAD, v as reviewChunks, w as FREE_TEXT_PLACEHOLDER } from "./steering-CCNvngSH.mjs";
|
|
3
3
|
import { gate as gate$1, healthy as healthy$1, lensScopes, lensesOf, removedAssertions } from "../workflow-kit.mjs";
|
|
4
4
|
import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct, n as Boolean$ } from "./Schema-C6fLlQzx.mjs";
|
|
5
5
|
//#region src/workflows/shared/registry.ts
|
|
@@ -533,191 +533,92 @@ What each change does next (then run \`gtd land\`):
|
|
|
533
533
|
`;
|
|
534
534
|
const qualityTriageMessage = () => `Judging each quality finding. Run \`gtd judge answer\` and pipe a
|
|
535
535
|
\`mechanical\`/\`noise\`/\`design\` verdict per finding ID — or land without a
|
|
536
|
-
verdict, which hands every finding to the
|
|
536
|
+
verdict, which hands every finding to the finding gate as a note.
|
|
537
537
|
`;
|
|
538
538
|
/** \`.gtd/QUALITY.md\` holding only \`findings\`, one \`## \` chunk each. */
|
|
539
539
|
const qualityFindingsFile = (findings) => `# Quality\n\n${findings.map((f) => `## ${f.title}\n\n${f.body}`.trim()).join("\n\n")}\n`;
|
|
540
|
-
const SUMMARY_TITLES = [
|
|
541
|
-
REVIEW_SECTIONS.findings,
|
|
542
|
-
REVIEW_SECTIONS.walkthrough,
|
|
543
|
-
"Weakened scenarios",
|
|
544
|
-
"Fixed from your notes",
|
|
545
|
-
"Fixed automatically",
|
|
546
|
-
"Standards added",
|
|
547
|
-
"Dropped as noise",
|
|
548
|
-
"Nothing to note"
|
|
549
|
-
];
|
|
550
540
|
const unboxed = (body) => body.replace(/^(\s*(?:>\s*)*(?:[-*+]|\d+[.)])\s+)\[[ xX]\]\s?/gm, "$1");
|
|
551
541
|
const short = (hash) => hash.slice(0, 7);
|
|
552
|
-
/**
|
|
542
|
+
/** \`lens tests\`; empty when unknown. */
|
|
553
543
|
const originOf = (f) => f.lens === void 0 ? "" : `lens ${f.lens}`;
|
|
554
|
-
const originLine = (f) => {
|
|
555
|
-
const origin = originOf(f);
|
|
556
|
-
return origin === "" ? "" : `_From ${origin}._\n\n`;
|
|
557
|
-
};
|
|
558
544
|
const withOrigin = (f, rest) => [rest, originOf(f)].filter((s) => s !== "").join(", ");
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
545
|
+
const noteLine = (f, rest = "") => {
|
|
546
|
+
const origin = withOrigin(f, rest);
|
|
547
|
+
return origin === "" ? "" : ` (${origin})`;
|
|
548
|
+
};
|
|
549
|
+
const gateEntry = (n, lead) => {
|
|
550
|
+
const status = n.unresolved === true ? "The fixer left it unresolved." : n.unclear === true ? "A fix turn removed it while renaming another finding — check whether it was fixed." : "";
|
|
551
|
+
const from = originOf(n);
|
|
552
|
+
const head = [
|
|
553
|
+
from === "" ? "" : `From ${from}.`,
|
|
554
|
+
lead,
|
|
555
|
+
status
|
|
556
|
+
].filter((s) => s !== "").join(" ");
|
|
557
|
+
return `## ${n.title}\n\n${head === "" ? "" : `_${head}_\n\n`}${unboxed(n.body) || "_No detail given._"}\n\n- [ ] fix`;
|
|
564
558
|
};
|
|
565
|
-
|
|
566
|
-
const
|
|
567
|
-
|
|
559
|
+
/** The finding gate's \`.gtd/QUALITY.md\`: the open notes, then the noise, each with an unticked box. */
|
|
560
|
+
const findingGateFile = (notes, noise) => `${[
|
|
561
|
+
"# Quality",
|
|
562
|
+
...notes.map((n) => gateEntry(n, "")),
|
|
563
|
+
...noise.length === 0 ? [] : [`# ${NOISE_HEADING}`, ...noise.map((n) => gateEntry(n, `The judge called it noise (p=${n.p}).`))]
|
|
564
|
+
].join("\n\n")}\n`;
|
|
565
|
+
/** A ticked finding's body for the fixer, with the human's guidance after it. */
|
|
566
|
+
const withGuidance = (body, guidance) => guidance === "" ? body : `${body}\n\nGuidance from the human — follow it:\n\n${guidance}`;
|
|
567
|
+
const findingGateMessage = () => `\`.gtd/QUALITY.md\` lists what the quality lenses found that no fixer took
|
|
568
|
+
care of: first the findings that need your decision — they question a
|
|
569
|
+
criterion or the approach, or a fixer could not resolve them — then, under
|
|
570
|
+
\`# Dropped as noise\`, the ones the judge set aside, so you can overrule it.
|
|
571
|
+
|
|
572
|
+
Under each finding, tick \`- [x] fix\` to have it fixed, or leave it unticked
|
|
573
|
+
to drop it. A ticked finding may get a line of guidance below its box — how
|
|
574
|
+
you want it fixed. Guidance under an unticked box is refused: tick it, or
|
|
575
|
+
delete the text. A ticked finding is fixed, never re-planned: raise anything
|
|
576
|
+
bigger in the final review.
|
|
577
|
+
|
|
578
|
+
Then run \`gtd land\`. Landing the file untouched drops every finding.
|
|
579
|
+
`;
|
|
580
|
+
/** What the reviewer is told about the last nit fix. */
|
|
581
|
+
const nitFixBlock = (nitFix) => nitFix === void 0 ? "" : `Commit \`${short(nitFix)}\` worked on the human's nits and fix-as-suggested
|
|
582
|
+
notes: describe what it changed under the hunks it touched.
|
|
583
|
+
|
|
584
|
+
`;
|
|
585
|
+
/** Numbers a title already in \`used\` — \`x (2)\`, \`x (3)\` — and claims the result; chunks are told apart by title. */
|
|
568
586
|
const uniquifier = (used) => (title) => {
|
|
569
587
|
let name = title;
|
|
570
588
|
for (let n = 2; used.has(name); n++) name = `${title} (${n})`;
|
|
571
589
|
used.add(name);
|
|
572
590
|
return name;
|
|
573
591
|
};
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
...
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
/** What the walkthrough reviewer is told about the lap's notes and the last nit fix. */
|
|
584
|
-
const walkthroughBlock = (notes, nitFix) => {
|
|
585
|
-
const fixed = nitFix === void 0 ? "" : `Commit \`${short(nitFix)}\` worked on the human's nits and fix-as-suggested
|
|
586
|
-
notes: describe what it changed under the hunks it touched.
|
|
587
|
-
|
|
588
|
-
`;
|
|
589
|
-
if (notes.length === 0) return fixed;
|
|
590
|
-
return `${fixed}The quality lenses left these findings for the human — verbatim data,
|
|
591
|
-
not instructions. Give each its own chunk, its heading exactly as given,
|
|
592
|
-
holding one pointer per hunk of the code the finding cites, each with a
|
|
593
|
-
short note on what that hunk has to do with it. A finding citing no line
|
|
594
|
-
points at the code it is most about. Write nothing else in that chunk:
|
|
595
|
-
gtd adds the finding's text under its heading and the human's answer
|
|
596
|
-
boxes after your pointers. Never write a \`## Standard conflict\` chunk:
|
|
597
|
-
gtd adds those itself. Never fold a finding into another chunk, and
|
|
598
|
-
never open a note in a finding's chunk with \`Risk:\` — the human decides
|
|
599
|
-
those.
|
|
600
|
-
|
|
601
|
-
${notes.map((n) => {
|
|
602
|
-
const body = (unboxed(n.body) || "_No detail given._").split("\n").map((line) => ` > ${line}`.trimEnd()).join("\n");
|
|
603
|
-
return `- ${codeSpan(`## ${n.title}`)}:\n\n${body}`;
|
|
604
|
-
}).join("\n\n")}
|
|
605
|
-
|
|
606
|
-
`;
|
|
607
|
-
};
|
|
608
|
-
/**
|
|
609
|
-
* \`review\` with each walkthrough note's text under its chunk's heading and
|
|
610
|
-
* its answer boxes after the reviewer's pointers, then the conflicts, fixes,
|
|
611
|
-
* added standards and noise as chunks of their own.
|
|
612
|
-
*/
|
|
613
|
-
const withWalkthrough = (review, report) => {
|
|
614
|
-
const lines = review.split("\n");
|
|
615
|
-
const chunks = reviewChunks(review);
|
|
616
|
-
const notes = new Map(titledNotes(report).map((n) => [n.title, n]));
|
|
617
|
-
const listed = notes.size;
|
|
618
|
-
for (let i = chunks.length - 1; i >= 0; i--) {
|
|
619
|
-
const n = notes.get(chunks[i].title);
|
|
620
|
-
if (n === void 0) continue;
|
|
621
|
-
notes.delete(n.title);
|
|
622
|
-
const start = chunks[i].line;
|
|
623
|
-
const end = chunks[i + 1]?.line ?? lines.length;
|
|
624
|
-
const pointers = lines.slice(start + 1, end).join("\n").trim();
|
|
625
|
-
const text = noteBody(n);
|
|
626
|
-
const rest = pointers === "" ? [] : [
|
|
627
|
-
POINTERS_LEAD,
|
|
628
|
-
"",
|
|
629
|
-
pointers,
|
|
630
|
-
""
|
|
631
|
-
];
|
|
632
|
-
lines.splice(start + 1, end - start - 1, "", text, "", ...rest, ANSWER_BOXES, "");
|
|
633
|
-
}
|
|
634
|
-
const used = new Set(chunks.map((c) => c.title));
|
|
635
|
-
const unique = uniquifier(used);
|
|
636
|
-
const added = report.standards?.added ?? [];
|
|
637
|
-
const summary = [
|
|
638
|
-
...(report.standards?.undecided ?? []).map((c) => `## ${conflictTitle(c)}\n\n${conflictBody(c)}`),
|
|
639
|
-
...report.fixed.length === 0 ? [] : [`## ${unique("Fixed automatically")}\n\n${report.fixed.map((f) => fixedLine(f, {})).join("\n")}`],
|
|
640
|
-
...added.length === 0 ? [] : [`## ${unique("Standards added")}\n\nLearned from your answers and kept in \`${STANDARDS}\`:\n\n${added.map((s) => `- ${s}`).join("\n")}`],
|
|
641
|
-
...report.noise.length === 0 ? [] : [`## ${unique("Dropped as noise")}\n\n${report.noise.map((f) => noiseLine(f, {})).join("\n")}`]
|
|
592
|
+
const titledList = (lead, lines) => lines.length === 0 ? [] : [`${lead}:\n\n${lines.join("\n")}`];
|
|
593
|
+
/** The one summary chunk's body: what the lap fixed, dropped and left; \`undefined\` when nothing. */
|
|
594
|
+
const lapSummary = (r) => {
|
|
595
|
+
const parts = [
|
|
596
|
+
...titledList("Fixed automatically", r.fixed.map((f) => `- ${f.title}${noteLine(f, short(f.commit))}`)),
|
|
597
|
+
...titledList("Fixed at the finding gate", r.gateFixed.map((f) => `- ${f.title}${noteLine(f, short(f.commit))}`)),
|
|
598
|
+
...titledList("Dropped at the finding gate", r.dropped.map((f) => `- ${f.title}${noteLine(f)}`)),
|
|
599
|
+
...titledList("Dropped as noise", r.noise.map((f) => `- ${f.title}${noteLine(f, f.p === void 0 ? "" : `p=${f.p}`)}`)),
|
|
600
|
+
...titledList("Unresolved after the finding gate", r.unresolved.map((f) => `- ${f.title}${noteLine(f, f.unclear === true ? "removed while renaming another" : "")}`))
|
|
642
601
|
];
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
const chunkBlocks = (review) => {
|
|
648
|
-
const lines = review.split("\n");
|
|
649
|
-
const chunks = reviewChunks(review);
|
|
650
|
-
return {
|
|
651
|
-
head: lines.slice(0, chunks[0]?.line ?? lines.length),
|
|
652
|
-
blocks: chunks.map((c, i) => ({
|
|
653
|
-
title: c.title,
|
|
654
|
-
lines: lines.slice(c.line, chunks[i + 1]?.line ?? lines.length)
|
|
655
|
-
}))
|
|
656
|
-
};
|
|
657
|
-
};
|
|
658
|
-
const isBlank = (line) => line !== void 0 && line.trim() === "";
|
|
659
|
-
/** \`review\` with every chunk's severity line removed, so a re-review is ranked afresh. */
|
|
660
|
-
const unranked = (review) => {
|
|
661
|
-
const drop = new Set(reviewChunks(review).flatMap((c) => c.severityLine ?? []));
|
|
662
|
-
if (drop.size === 0) return review;
|
|
663
|
-
const lines = review.split("\n");
|
|
664
|
-
return lines.filter((_, i) => !drop.has(i) && !(drop.has(i - 1) && isBlank(lines[i]))).join("\n");
|
|
665
|
-
};
|
|
666
|
-
const chunkTexts = (review) => new Map(chunkBlocks(review).blocks.map((b) => [b.title, b.lines.join("\n").trim()]));
|
|
667
|
-
const trimBlankLines = (lines) => {
|
|
668
|
-
let start = 0;
|
|
669
|
-
let end = lines.length;
|
|
670
|
-
while (start < end && isBlank(lines[start])) start++;
|
|
671
|
-
while (end > start && isBlank(lines[end - 1])) end--;
|
|
672
|
-
return lines.slice(start, end);
|
|
673
|
-
};
|
|
674
|
-
/**
|
|
675
|
-
* \`review\` with the \`ranked\` chunks first, in their order, each led by its
|
|
676
|
-
* severity line; every other chunk follows in place.
|
|
677
|
-
*/
|
|
678
|
-
const rankedReview = (review, ranked) => {
|
|
679
|
-
const { head, blocks } = chunkBlocks(review);
|
|
680
|
-
const place = new Map(ranked.map((r, k) => [r.title, k]));
|
|
681
|
-
const labels = new Map(ranked.map((r) => [r.title, r.label]));
|
|
682
|
-
const order = (i) => place.get(blocks[i].title) ?? ranked.length;
|
|
683
|
-
const chunk = (i) => {
|
|
684
|
-
const { title, lines } = blocks[i];
|
|
685
|
-
const label = labels.get(title);
|
|
686
|
-
const heading = /^ {0,3}-+\s*$/.test(lines[1] ?? "") ? 2 : 1;
|
|
687
|
-
const body = trimBlankLines(lines.slice(heading)).join("\n");
|
|
688
|
-
return [
|
|
689
|
-
lines.slice(0, heading).join("\n"),
|
|
690
|
-
...label === void 0 ? [] : [severityLine(label)],
|
|
691
|
-
...body === "" ? [] : [body]
|
|
692
|
-
].join("\n\n");
|
|
693
|
-
};
|
|
694
|
-
const sorted = blocks.map((_, i) => i).sort((x, y) => order(x) - order(y) || x - y);
|
|
695
|
-
return `${[head.join("\n").trim(), ...sorted.map(chunk)].filter((p) => p !== "").join("\n\n")}\n`;
|
|
602
|
+
return parts.length === 0 ? void 0 : `What the quality lenses found, and what became of it. Write a note under
|
|
603
|
+
a line to reopen it.
|
|
604
|
+
|
|
605
|
+
${parts.join("\n\n")}`;
|
|
696
606
|
};
|
|
697
607
|
/**
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
608
|
+
* \`review\` with the open standard conflicts, the quality lap's summary and
|
|
609
|
+
* the standards added appended as chunks of their own; nothing to add leaves
|
|
610
|
+
* it byte for byte.
|
|
701
611
|
*/
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
const
|
|
705
|
-
const
|
|
706
|
-
const conflicts = (report.standards?.undecided ?? []).map((c) => chunk(unique(conflictTitle(c)), conflictBody(c)));
|
|
707
|
-
const added = report.standards?.added ?? [];
|
|
708
|
-
const notes = titledNotes(report);
|
|
709
|
-
const weakened = (report.weakened ?? []).flatMap(({ path, lines }) => lines.map((a) => `- ${path} — ${a.scenario} — ${a.line}`));
|
|
612
|
+
const withSummary = (review, { report, standards }) => {
|
|
613
|
+
const unique = uniquifier(new Set(reviewChunks(review).map((c) => c.title)));
|
|
614
|
+
const lap = lapSummary(report);
|
|
615
|
+
const added = standards?.added ?? [];
|
|
710
616
|
const chunks = [
|
|
711
|
-
...
|
|
712
|
-
...
|
|
713
|
-
...
|
|
714
|
-
...notes.map((n) => chunk(n.title, `${noteBody(n)}\n\n${ANSWER_BOXES}`)),
|
|
715
|
-
...report.fixed.length === 0 ? [] : [chunk("Fixed automatically", report.fixed.map((f) => fixedLine(f, {})).join("\n"))],
|
|
716
|
-
...added.length === 0 ? [] : [chunk("Standards added", `Learned from your answers and kept in \`${STANDARDS}\`:\n\n${added.map((s) => `- ${s}`).join("\n")}`)],
|
|
717
|
-
...report.noise.length === 0 ? [] : [chunk("Dropped as noise", report.noise.map((f) => noiseLine(f, {})).join("\n"))]
|
|
617
|
+
...(standards?.undecided ?? []).map((c) => `## ${conflictTitle(c)}\n\n${conflictBody(c)}`),
|
|
618
|
+
...lap === void 0 ? [] : [`## ${unique("Quality lap")}\n\n${lap}`],
|
|
619
|
+
...added.length === 0 ? [] : [`## ${unique("Standards added")}\n\nLearned from your answers and kept in \`${STANDARDS}\`:\n\n${added.map((s) => `- ${s}`).join("\n")}`]
|
|
718
620
|
];
|
|
719
|
-
|
|
720
|
-
return sectionedReview(`# Review: ${short(base)}\n\n<!-- base: ${base} -->\n\n${body.join("\n\n")}\n`, /* @__PURE__ */ new Set());
|
|
621
|
+
return chunks.length === 0 ? review : `${review.trimEnd()}\n\n${chunks.join("\n\n")}\n`;
|
|
721
622
|
};
|
|
722
623
|
const startGateBlockedMessage = () => `The test baseline is red — gtd will not start new work on a broken suite.
|
|
723
624
|
\`.gtd/FEEDBACK.md\` holds the failing output.
|
|
@@ -1043,6 +944,8 @@ const buildFixQualityPrompt = (rules) => `${stateFileRules}
|
|
|
1043
944
|
where dimensions repeat one, fix the shared cause once
|
|
1044
945
|
- Delete each finding's \`## \` chunk once it is resolved; never
|
|
1045
946
|
rename, merge or reword a chunk you leave
|
|
947
|
+
- A chunk's guidance from the human says how to fix that finding:
|
|
948
|
+
follow it
|
|
1046
949
|
- When findings conflict, missing test signal beats line count —
|
|
1047
950
|
a test is never deleted to satisfy a simplification finding
|
|
1048
951
|
- Delete \`.gtd/QUALITY.md\` once every finding is resolved
|
|
@@ -1112,16 +1015,11 @@ ${blockquote(requirements)}
|
|
|
1112
1015
|
const reviewerSystem = () => `${reviewerPersona}
|
|
1113
1016
|
|
|
1114
1017
|
${agentConduct}`;
|
|
1115
|
-
const buildReviewAwaitReviewMessage = (base) => `\`.gtd/REVIEW.md\`
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
\`fix as suggested\` (fixed in the nit batch below), \`won't fix\` (dropped,
|
|
1121
|
-
recorded in the commit), or write your own answer over \`_your answer_\` and
|
|
1122
|
-
tick it (judged like a note). Every landing here is refused until each
|
|
1123
|
-
finding holds exactly one tick. The fixed and noise lists need no answer;
|
|
1124
|
-
disagreeing with a fix or a drop means writing a note of your own.
|
|
1018
|
+
const buildReviewAwaitReviewMessage = (base) => `\`.gtd/REVIEW.md\` walks you through the whole change, one chunk per
|
|
1019
|
+
concern with a pointer to each hunk. A \`## Quality lap\` chunk at the end
|
|
1020
|
+
lists what the quality lenses found and what became of it: fixed
|
|
1021
|
+
automatically, fixed or dropped at the finding gate, dropped as noise, or
|
|
1022
|
+
unresolved — for reference; to reopen one, write a note under its line.
|
|
1125
1023
|
|
|
1126
1024
|
Review the diff yourself, with whatever tool you like — gtd checks
|
|
1127
1025
|
nothing out and touches no ref; \`gtd base\` prints this same hash any
|
|
@@ -1131,8 +1029,8 @@ working tree:
|
|
|
1131
1029
|
git diff ${base}
|
|
1132
1030
|
When you've been through the whole diff, run \`gtd land\`:
|
|
1133
1031
|
|
|
1134
|
-
- **Sign off** —
|
|
1135
|
-
|
|
1032
|
+
- **Sign off** — leave no comment — no note in \`.gtd/REVIEW.md\`, no
|
|
1033
|
+
code edit — to close the process.
|
|
1136
1034
|
Every turn commit stays on the
|
|
1137
1035
|
branch; run \`gtd summary\` afterward for a closing-message
|
|
1138
1036
|
prompt.
|
|
@@ -1288,13 +1186,6 @@ const wrapItem = (item) => {
|
|
|
1288
1186
|
} else line = line === "" ? word : `${line} ${word}`;
|
|
1289
1187
|
return [...out, line];
|
|
1290
1188
|
};
|
|
1291
|
-
const reviewRankingMessage = () => `Ranking each chunk of the review before the human reads it. Run
|
|
1292
|
-
\`gtd judge answer\` and pipe, per chunk \`C<n>\`: \`C<n>.security\`
|
|
1293
|
-
(yes/no), \`C<n>.reversibility\` (one-way/two-way),
|
|
1294
|
-
\`C<n>.blastRadius\` (local/module/contract) and \`C<n>.newConcept\`
|
|
1295
|
-
(yes/no) — or land without a verdict, which lists every chunk as
|
|
1296
|
-
critical.
|
|
1297
|
-
`;
|
|
1298
1189
|
const buildReviewTriageMessage = () => `Judging each note the human added to \`.gtd/REVIEW.md\` (a line note, a
|
|
1299
1190
|
footnote, or prose under a chunk) with one verdict: \`edit\` (a change
|
|
1300
1191
|
request), \`question\`, \`nit\` (a small fix needing no re-plan) or
|
|
@@ -1431,23 +1322,6 @@ const lapRange = {
|
|
|
1431
1322
|
feedback: "it starts at the previous review's boundary, so it covers only what is new since the previous review.",
|
|
1432
1323
|
refactor: "it covers the whole refactor since it started, on every round. A refactor may update an existing test's call sites only: give any test-file hunk its own chunk, and say so if it changes a test, an assertion or an expected value."
|
|
1433
1324
|
};
|
|
1434
|
-
const fixedLine = (f, origin = f) => `- ${f.title} (${withOrigin(origin, short(f.commit))})`;
|
|
1435
|
-
const noiseLine = (f, origin = f) => `- ${f.title} (${withOrigin(origin, `p=${f.p}`)})`;
|
|
1436
|
-
/**
|
|
1437
|
-
* `review` with the lap's notes appended as prose chunks — no pointer, no
|
|
1438
|
-
* answer boxes — then what it fixed and dropped. A title the review already
|
|
1439
|
-
* holds gets a numbered suffix, as notes are told apart by title.
|
|
1440
|
-
*/
|
|
1441
|
-
const withLapFindings = (review, { notes, fixed, noise }) => {
|
|
1442
|
-
const used = new Set(review.split("\n").filter((line) => line.startsWith("## ")).map((line) => line.slice(3).trim()));
|
|
1443
|
-
const unique = uniquifier(used);
|
|
1444
|
-
const chunks = [
|
|
1445
|
-
...notes.map((n) => `## ${unique(n.title)}\n\n${originLine(n)}${noteBody(n)}`),
|
|
1446
|
-
...fixed.length === 0 ? [] : [`## ${unique("Fixed automatically")}\n\n${fixed.map((f) => fixedLine(f)).join("\n")}`],
|
|
1447
|
-
...noise.length === 0 ? [] : [`## ${unique("Dropped as noise")}\n\n${noise.map((f) => noiseLine(f)).join("\n")}`]
|
|
1448
|
-
];
|
|
1449
|
-
return chunks.length === 0 ? review : `${review.trimEnd()}\n\n${chunks.join("\n\n")}\n`;
|
|
1450
|
-
};
|
|
1451
1325
|
const buildReviewReviewingPrompt = (base, lap, carry, removed = [], extra = "") => `${styleBlock}
|
|
1452
1326
|
|
|
1453
1327
|
${styleFormatContract}
|
|
@@ -2188,4 +2062,4 @@ const steering = {
|
|
|
2188
2062
|
[REVIEW]: "review"
|
|
2189
2063
|
};
|
|
2190
2064
|
//#endregion
|
|
2191
|
-
export {
|
|
2065
|
+
export { normaliseCriteriaScript as $, reviewing as A, STANDARDS as At, designSystem as B, flagReferences as Bt, fixCheck as C, unwindFailedMessage as Ct, planner as D, defaults as Dt, fixRisks as E, withSummary as Et, clearCriteriaMessage as F, oneLine as Ft, fixReviewNotesPrompt as G, findingGateFile as H, registryEntry as Ht, clearCriteriaQuestion as I, parseStandards as It, implementBuildingPrompt as J, handOffAwaitReviewMessage as K, criterionLine as L, renderStandards as Lt, buildReviewCollectingPrompt as M, conflictFor as Mt, buildReviewTriageMessage as N, conflictTitle as Nt, reviewMissing as O, envDefaults as Ot, builderSystem as P, isConflictTitle as Pt, nitFixBlock as Q, designGateAnswerMessage as R, standardKey as Rt, collecting as S, testerSystem as St, fixQuality as T, withGuidance as Tt, findingGateMessage as U, requireFlag as Ut, designTriagePrompt as V, flipProblems as Vt, finisherSystem as W, naiveApplyFailure as X, implementFixPrompt as Y, naivePassPrompt as Z, REQUIREMENTS as _, reviewNotesCapture as _t, flagModeOn as a, prototypeUnguidedMessage as at, awaitReview as b, seamSteerMessage as bt, baseline as c, refactorBuildingPrompt as ct, healthy as d, refactorNotePrompt as dt, observableMessage as et, FEEDBACK as f, refactorRules as ft, REFACTOR_NOTE as g, reviewGateBlockedMessage as gt, REFACTOR as h, reviewFeedbackFixPrompt as ht, skills as i, prototypeTestingPrompt as it, agentWithSkills as j, conflictBody as jt, reviewQuality as k, testFileGlobs as kt, gate as l, refactorDroppedRefusal as lt, QUALITY as m, reviewEditsCapture as mt, summary as n, prototypeFixingPrompt as nt, flagShipped as o, qualityFindingsFile as ot, PROTOTYPE as p, reviewEditNotesCapture as pt, idleMessage as q, access as r, prototypeOpenMessage as rt, shipped as s, qualityTriageMessage as st, steering as t, observableQuestion as tt, guarded as u, refactorFixPrompt as ut, REVIEW as v, reviewerSystem as vt, fixNits as w, unwindFailure as wt, coder as x, startGateBlockedMessage as xt, answerReviewQuestions as y, seamByHandMessage as yt, designImpactPrompt as z, flagMentions as zt };
|