@pmelab/gtd 49.0.0 → 51.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.
Files changed (60) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +16 -3
  3. package/dist/chunks/{reserved-CYZ1bSBy.mjs → reserved-CUkk9XgL.mjs} +151 -5
  4. package/dist/chunks/{review-BewyJflS.mjs → review-BaCXlIT2.mjs} +84 -20
  5. package/dist/chunks/{steering-BXwjmocB.mjs → steering-DtUVUz9l.mjs} +44 -14
  6. package/dist/chunks/{tail-C1Oi2YEy.mjs → tail-CX6Pdsql.mjs} +1 -1
  7. package/dist/gtd.bundle.mjs +22 -5
  8. package/dist/steering.mjs +2 -2
  9. package/dist/testkit.mjs +1 -1
  10. package/dist/web/main.js +27 -3
  11. package/dist/workflow-kit.mjs +1 -1
  12. package/dist/workflows/cleanup.mjs +2 -2
  13. package/dist/workflows/feature.mjs +144 -11
  14. package/dist/workflows/fix.mjs +3 -3
  15. package/dist/workflows/review.mjs +3 -3
  16. package/dist/workflows/rollout.mjs +1 -1
  17. package/dist/workflows.mjs +415 -42
  18. package/package.json +1 -1
  19. package/src/steering/index.ts +1 -0
  20. package/src/steering/review.test.ts +60 -2
  21. package/src/steering/review.ts +66 -9
  22. package/src/workflows/feature/coverage.feature +1 -0
  23. package/src/workflows/feature/fast-path.feature +1 -0
  24. package/src/workflows/feature/feature.ts +4 -1
  25. package/src/workflows/feature/flag-mode.feature +1 -1
  26. package/src/workflows/feature/lens-models.feature +1 -0
  27. package/src/workflows/feature/lens-review.feature +17 -4
  28. package/src/workflows/feature/one-step-land.feature +1 -0
  29. package/src/workflows/feature/prototype-rounds.feature +152 -0
  30. package/src/workflows/feature/prototype.feature +419 -0
  31. package/src/workflows/feature/prototype.test.ts +43 -0
  32. package/src/workflows/feature/prototype.ts +181 -0
  33. package/src/workflows/feature/refactor-absorbs.feature +9 -1
  34. package/src/workflows/feature/refactor-first.feature +13 -3
  35. package/src/workflows/feature/refactor-rebuild.feature +29 -5
  36. package/src/workflows/feature/refactor-review.feature +111 -13
  37. package/src/workflows/feature/refactor.test.ts +14 -4
  38. package/src/workflows/feature/refactor.ts +2 -0
  39. package/src/workflows/feature/review-answers.feature +9 -1
  40. package/src/workflows/feature/review-gate.feature +21 -3
  41. package/src/workflows/feature/reviewer-walkthrough.feature +15 -3
  42. package/src/workflows/feature/seam-rejection.feature +1 -0
  43. package/src/workflows/feature/seam.feature +1 -0
  44. package/src/workflows/feature/seam.test.ts +3 -2
  45. package/src/workflows/feature/seam.ts +19 -5
  46. package/src/workflows/feature/severity-budget.feature +112 -0
  47. package/src/workflows/feature/severity-sorted-review.feature +198 -0
  48. package/src/workflows/feature/slice-findings.feature +15 -2
  49. package/src/workflows/feature/slicing-cap.feature +9 -0
  50. package/src/workflows/feature/slicing.feature +1 -0
  51. package/src/workflows/feature/standards.feature +5 -0
  52. package/src/workflows/shared/options.ts +1 -0
  53. package/src/workflows/shared/prose.ts +6 -0
  54. package/src/workflows/shared/review.ts +124 -18
  55. package/src/workflows/shared/skills.test.ts +4 -0
  56. package/src/workflows/shared/skills.ts +5 -0
  57. package/src/workflows/shared/steps.ts +1 -0
  58. package/src/workflows/shared/text.test.ts +90 -0
  59. package/src/workflows/shared/text.ts +181 -4
  60. package/src/workflows/shared/vars.ts +3 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtd",
3
- "version": "49.0.0",
3
+ "version": "51.0.0",
4
4
  "description": "Drive the gtd loop inside Claude Code: agent turns run as subagents, gtd decides every beat",
5
5
  "author": {
6
6
  "name": "Philipp Melab"
package/README.md CHANGED
@@ -538,9 +538,15 @@ noted in steps 3 and 4 below.
538
538
  commit) and what was **dropped as noise**. A review with a chunk pointing at
539
539
  no code, or a note left out, is refused (`review-pointers`). A pointer note
540
540
  the reviewer opens with `Risk:` is fixed before you see the review
541
- (`build.review.fix.risks.fixing`), then the review is written again. On
542
- `refactor-first` the refactor slice gets a review of its own first, composed
543
- by gtd from its lap's notes, before the feature is built; see
541
+ (`build.review.fix.risks.fixing`), then the review is written again. **What
542
+ matters most comes first:** a judge (`build.review.ranking`) rates every
543
+ chunk on the four severity questions below, and the review lists `critical`
544
+ chunks first (security, then one-way doors), then `should`, then `could`,
545
+ each led by its tier and reason (`_critical: security, one-way door_`) — the
546
+ phone shows it on the chunk's card. A chunk the judge is unsure of, or whose
547
+ evidence the budget cut, is `critical`. On `refactor-first` the refactor
548
+ slice gets a walkthrough of its own first, covering the whole refactor on
549
+ every round, before the feature is built; see
544
550
  [the naive pass](#the-naive-pass). **Every note needs exactly one tick before
545
551
  `gtd land` accepts the review** (`answer-completeness`; an open `- H:` thread
546
552
  waiting on the agent is the one exception). Fix as suggested joins the nit
@@ -687,6 +693,13 @@ an uncovered one goes back to the agent (twice, then to you) instead of ending
687
693
  the feature, and each covered ID is recorded as a `Gtd-Mark: covers=<id>`
688
694
  trailer. See
689
695
  [Acceptance-criteria coverage](docs/configuration.md#acceptance-criteria-coverage).
696
+ Then an agent uses the slice by hand — the real program in a scratch directory,
697
+ the real page, guided by a project skill or a manual testing section in
698
+ `AGENTS.md`, `CLAUDE.md` or `README.md` — and writes `.gtd/PROTOTYPE.md`: per
699
+ criterion pass, fail or blocked, with the steps run and what it saw. Failures
700
+ are fixed and re-tested before the quality lap, up to `prototypeRounds` (3) fix
701
+ rounds; past them, with only blocked items, or with no way to test found, it
702
+ rests for you. See [The hands-on test](docs/configuration.md#the-hands-on-test).
690
703
  Feedback at the feature review re-runs design and the implementation, never the
691
704
  naive pass; feedback at the refactor review re-runs the refactor, never design;
692
705
  a refactor-review change the refactor cannot build is refused, and the only way
@@ -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 { d as ANSWER_FIX, f as ANSWER_LEAD, m as reviewChunks, p as ANSWER_WONT_FIX, y as FREE_TEXT_PLACEHOLDER } from "./steering-BXwjmocB.mjs";
2
+ import { b as FREE_TEXT_PLACEHOLDER, d as ANSWER_FIX, f as ANSWER_LEAD, m as reviewChunks, p as ANSWER_WONT_FIX, v as severityLine } from "./steering-DtUVUz9l.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$, s as Number$ } from "./Schema-C6fLlQzx.mjs";
5
5
  //#region src/workflows/shared/registry.ts
@@ -243,6 +243,7 @@ const defaults = {
243
243
  qualityReviews: "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
244
244
  refactorQualityReviews: "correctness, ponytail-review, conventions",
245
245
  continuations: "3",
246
+ prototypeRounds: "3",
246
247
  continuationStall: "2"
247
248
  };
248
249
  /** Environment settings. */
@@ -320,6 +321,11 @@ task in front of you end to end, tests first, and come back to fix
320
321
  things when redirected — a failing check, or reviewer feedback. Stay
321
322
  strictly inside that task; never touch code outside it. Treat feedback as the
322
323
  diagnosis to act on; discard it, with reason, only when simply wrong.`;
324
+ const testerPersona = `You are the hands-on tester in gtd's build pipeline: use what was just
325
+ built the way a person would — run the real program, open the real
326
+ page, call the real endpoint — and report exactly what you did and
327
+ what you saw. Never fix anything yourself, and never pass a criterion
328
+ you did not watch hold.`;
323
329
  const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
324
330
  before it closes. Fix a late-breaking failing check after sign-off,
325
331
  focused and minimal. Every turn lands its own commit — nothing here
@@ -641,6 +647,56 @@ const withWalkthrough = (review, report) => {
641
647
  const body = lines.join("\n").trimEnd();
642
648
  return summary.length === 0 ? `${body}\n` : `${body}\n\n${summary.join("\n\n")}\n`;
643
649
  };
650
+ const chunkBlocks = (review) => {
651
+ const lines = review.split("\n");
652
+ const chunks = reviewChunks(review);
653
+ return {
654
+ head: lines.slice(0, chunks[0]?.line ?? lines.length),
655
+ blocks: chunks.map((c, i) => ({
656
+ title: c.title,
657
+ lines: lines.slice(c.line, chunks[i + 1]?.line ?? lines.length)
658
+ }))
659
+ };
660
+ };
661
+ const isBlank = (line) => line !== void 0 && line.trim() === "";
662
+ /** \`review\` with every chunk's severity line removed, so a re-review is ranked afresh. */
663
+ const unranked = (review) => {
664
+ const drop = new Set(reviewChunks(review).flatMap((c) => c.severityLine ?? []));
665
+ if (drop.size === 0) return review;
666
+ const lines = review.split("\n");
667
+ return lines.filter((_, i) => !drop.has(i) && !(drop.has(i - 1) && isBlank(lines[i]))).join("\n");
668
+ };
669
+ const chunkTexts = (review) => new Map(chunkBlocks(review).blocks.map((b) => [b.title, b.lines.join("\n").trim()]));
670
+ const trimBlankLines = (lines) => {
671
+ let start = 0;
672
+ let end = lines.length;
673
+ while (start < end && isBlank(lines[start])) start++;
674
+ while (end > start && isBlank(lines[end - 1])) end--;
675
+ return lines.slice(start, end);
676
+ };
677
+ /**
678
+ * \`review\` with the \`ranked\` chunks first, in their order, each led by its
679
+ * severity line; every other chunk follows in place.
680
+ */
681
+ const rankedReview = (review, ranked) => {
682
+ const { head, blocks } = chunkBlocks(review);
683
+ const place = new Map(ranked.map((r, k) => [r.title, k]));
684
+ const labels = new Map(ranked.map((r) => [r.title, r.label]));
685
+ const order = (i) => place.get(blocks[i].title) ?? ranked.length;
686
+ const chunk = (i) => {
687
+ const { title, lines } = blocks[i];
688
+ const label = labels.get(title);
689
+ const heading = /^ {0,3}-+\s*$/.test(lines[1] ?? "") ? 2 : 1;
690
+ const body = trimBlankLines(lines.slice(heading)).join("\n");
691
+ return [
692
+ lines.slice(0, heading).join("\n"),
693
+ ...label === void 0 ? [] : [severityLine(label)],
694
+ ...body === "" ? [] : [body]
695
+ ].join("\n\n");
696
+ };
697
+ const sorted = blocks.map((_, i) => i).sort((x, y) => order(x) - order(y) || x - y);
698
+ return `${[head.join("\n").trim(), ...sorted.map(chunk)].filter((p) => p !== "").join("\n\n")}\n`;
699
+ };
644
700
  /**
645
701
  * \`.gtd/REVIEW.md\` as gtd writes it: one chunk per note, each ending in its
646
702
  * answer boxes, then the fixed and noise lists. Chunk titles are made unique —
@@ -695,8 +751,11 @@ const acceptanceCriteriaBlock = `
695
751
  Whichever lap this is, \`.gtd/REQUIREMENTS.md\` carries a
696
752
  \`## Acceptance Criteria\` section: one \`- AC-<n>: <criterion>\` line
697
753
  per criterion, numbered from \`AC-1\`. They bind every later step —
698
- the build is done when each one holds — so each must be checkable: a
699
- test or a command shows it holds or not. A later lap keeps every ID:
754
+ the build is done when each one holds — so each must be behaviour a
755
+ test can pin: one that fails when it does not hold. A green suite, a
756
+ passing typecheck or lint, a generator or build that runs — the health
757
+ check enforces those on every lap, so none is a criterion; fold what it
758
+ must produce into a behaviour a test can observe. A later lap keeps every ID:
700
759
  reword a criterion in place, append a new one with the next unused
701
760
  number, delete a dropped one — never renumber, never reuse an ID. When
702
761
  the file holds no list (a review loop-back rewrites it), recover the
@@ -804,7 +863,7 @@ const clearCriteriaQuestion = {
804
863
  id: "clear",
805
864
  primitive: "noul",
806
865
  instructions: "state.criteria lists the acceptance criteria the human's sketch (state.sketch) stated, as gtd numbered them. Decide whether they can stand as the binding definition of done, with no design conversation.",
807
- criteria: "yes: every criterion is clear and checkable — a test or a command can show it holds or not — and together they say what to build. no: any one criterion is vague, subjective or untestable, or the sketch leaves an open product question. Unsure means no."
866
+ criteria: "yes: every criterion is clear behaviour a test can pin — a test would fail if it did not hold — and together they say what to build. no: any one criterion is vague, subjective or untestable, only a command can check it (a suite, typecheck, lint, build or generator run that passes), or the sketch leaves an open product question. Unsure means no."
808
867
  };
809
868
  const clearCriteriaMessage = () => `The sketch stated its own acceptance criteria. Run \`gtd judge answer\` and
810
869
  pipe \`yes\` when every one is clear and checkable, so design is skipped and
@@ -1195,6 +1254,13 @@ const wrapItem = (item) => {
1195
1254
  } else line = line === "" ? word : `${line} ${word}`;
1196
1255
  return [...out, line];
1197
1256
  };
1257
+ const reviewRankingMessage = () => `Ranking each chunk of the review before the human reads it. Run
1258
+ \`gtd judge answer\` and pipe, per chunk \`C<n>\`: \`C<n>.security\`
1259
+ (yes/no), \`C<n>.reversibility\` (one-way/two-way),
1260
+ \`C<n>.blastRadius\` (local/module/contract) and \`C<n>.newConcept\`
1261
+ (yes/no) — or land without a verdict, which lists every chunk as
1262
+ critical.
1263
+ `;
1198
1264
  const buildReviewTriageMessage = () => `Judging each note the human added to \`.gtd/REVIEW.md\` (a line note, a
1199
1265
  footnote, or prose under a chunk) with one verdict: \`edit\` (a change
1200
1266
  request), \`question\`, \`nit\` (a small fix needing no re-plan) or
@@ -1716,6 +1782,80 @@ Two ways forward (then run \`gtd land\`):
1716
1782
  - **Overrule** — land a clean tree to finish without them. The slice records
1717
1783
  the IDs it covers and, as \`uncovered\`, the ones it does not
1718
1784
  `;
1785
+ const testerSystem = () => `${testerPersona}
1786
+
1787
+ ${agentConduct}`;
1788
+ const PROTOTYPE_FORMAT = `- Write \`.gtd/PROTOTYPE.md\`, replacing any earlier report: one \`## \` chunk
1789
+ per criterion, headed \`## AC-<n>: pass\`, \`## AC-<n>: fail\` or
1790
+ \`## AC-<n>: blocked\`, then one chunk per problem you found outside any
1791
+ criterion, headed \`## Problem <short title>: fail\` (\`: pass\` once a
1792
+ re-test shows it fixed). Under each: the exact steps you ran, what you
1793
+ observed (output, a screenshot path, the error), and for a failure what
1794
+ you expected instead
1795
+ - \`blocked\` is for a criterion you could not exercise, or one whose
1796
+ expected behaviour itself looks wrong — say which and why: a person
1797
+ decides it, never a fixer`;
1798
+ const prototypeTestingPrompt = (slice, retest) => `${stateFileRules}
1799
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
1800
+ ${sliceLine(slice)}- The change is built and its suite is green. Now test it by hand: use
1801
+ every acceptance criterion the way a person would and watch what
1802
+ happens. Running the test suite is not this — it already ran
1803
+ - Find how this project is exercised for real, in this order: a project
1804
+ skill that launches or drives it (e.g. \`.claude/skills/*\`); a manual
1805
+ testing or running section in \`AGENTS.md\`, \`CLAUDE.md\` or \`README.md\`;
1806
+ failing those, the project's shape — a browser or Playwright for a web
1807
+ project, the built CLI in a scratch directory for a CLI, a request client
1808
+ for an API
1809
+ - Found none and recognised no shape: write only a chunk headed
1810
+ \`## No testing instructions\` saying what you looked for, and finish —
1811
+ a person is asked; never guess
1812
+ - Test in a disposable place only — a scratch directory outside this
1813
+ repository, a throwaway repository, a dev server on a free port — never
1814
+ against the user's real data or this working tree. Point every tool's
1815
+ output (screenshots, traces, test results) outside the repository too
1816
+ ${retest ? `- This is a re-test: \`.gtd/PROTOTYPE.md\` holds the last round's report and
1817
+ a fix turn or a person has changed the code since. Re-test every failed or blocked criterion and
1818
+ every problem it lists in full, then re-run the passed ones quickly
1819
+ ` : ""}${PROTOTYPE_FORMAT}
1820
+ - Change nothing but \`.gtd/PROTOTYPE.md\` — a turn that does is refused
1821
+ - Leave everything uncommitted and finish your turn — do not commit
1822
+ `;
1823
+ const prototypeFixingPrompt = (slice) => `${stateFileRules}
1824
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
1825
+ ${sliceLine(slice)}${criteriaBinding(true)}
1826
+ - Read \`.gtd/PROTOTYPE.md\`: a hands-on test of the change. Fix every
1827
+ \`fail\` chunk of this change's criteria${slice === void 0 ? "" : " (this slice's only)"} or a problem found
1828
+ beside them, at its cause, with a test that shows it; leave \`blocked\`
1829
+ ones to the person
1830
+ - A \`fail\` the code does not explain (the tester misread what it saw) has
1831
+ nothing to fix: leave it and change nothing for it — the next round re-tests
1832
+ - Leave \`.gtd/PROTOTYPE.md\` as it is: the next round re-tests from it
1833
+ - Run \`${env.testCommand}\` and finish only once it is green
1834
+ - Leave everything uncommitted and finish your turn — do not commit
1835
+ `;
1836
+ const openItems = (items) => items.map((item) => `- ${item.subject}: ${item.verdict}`).join("\n");
1837
+ const prototypeOpenMessage = (items, rounds) => `The hands-on test of this slice still has open items${rounds > 0 ? ` after ${rounds} fix round${rounds === 1 ? "" : "s"}` : ", and none a fixer can take"}:
1838
+ ${openItems(items)}
1839
+
1840
+ \`.gtd/PROTOTYPE.md\` holds the last report: the steps run and what was seen.
1841
+
1842
+ Two ways forward (then run \`gtd land\`):
1843
+ - **Fix** — change the code, or the testing instructions, yourself (the
1844
+ criteria are settled: an edit to them is refused); the suite runs and the
1845
+ slice is tested again, with a fresh round budget
1846
+ - **Overrule** — land a clean tree to go on to the quality lap with these
1847
+ items open
1848
+ `;
1849
+ const prototypeUnguidedMessage = () => `The hands-on test found no way to exercise this project for real — no
1850
+ project skill, no manual testing section in \`AGENTS.md\`, \`CLAUDE.md\` or
1851
+ \`README.md\`, and no project shape it recognised. \`.gtd/PROTOTYPE.md\` says
1852
+ what it looked for.
1853
+
1854
+ Two ways forward (then run \`gtd land\`):
1855
+ - **Instruct** — add testing instructions (a project skill, or a manual
1856
+ testing section); the slice is tested again
1857
+ - **Skip** — land a clean tree to go on to the quality lap untested
1858
+ `;
1719
1859
  //#endregion
1720
1860
  //#region src/workflows/shared/steps.ts
1721
1861
  const FEEDBACK = ".gtd/FEEDBACK.md";
@@ -1727,6 +1867,7 @@ const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md";
1727
1867
  const QUALITY = ".gtd/QUALITY.md";
1728
1868
  const SLICES = ".gtd/SLICES.md";
1729
1869
  const HANDOFF = ".gtd/HANDOFF.md";
1870
+ const PROTOTYPE = ".gtd/PROTOTYPE.md";
1730
1871
  const planner = () => env.plannerModel ?? "";
1731
1872
  const coder = () => env.coderModel ?? "";
1732
1873
  const fixCheck = (rules) => agentWithSkills("fix", buildFixPrompt(rules), {
@@ -1971,6 +2112,7 @@ const ShippedOptions = Struct({
1971
2112
  qualityReviews: setting,
1972
2113
  refactorQualityReviews: setting,
1973
2114
  continuations: setting,
2115
+ prototypeRounds: setting,
1974
2116
  continuationStall: setting,
1975
2117
  testCommand: setting,
1976
2118
  plannerModel: setting,
@@ -2050,6 +2192,10 @@ const skills = (vars) => {
2050
2192
  seam: ["incremental-implementation"],
2051
2193
  implement: ["test-driven-development", "incremental-implementation"],
2052
2194
  "implement.fix.suite": ["debugging-and-error-recovery"],
2195
+ "implement.prototype": [],
2196
+ "implement.prototype.fix": ["test-driven-development", "incremental-implementation"],
2197
+ "implement.prototype.fix.suite": ["debugging-and-error-recovery"],
2198
+ "implement.prototype.health": ["debugging-and-error-recovery"],
2053
2199
  "implement.health": ["debugging-and-error-recovery"],
2054
2200
  ...lensScopes("build.quality", lensesOf(vars.qualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens]),
2055
2201
  ...lensScopes("refactor.build.quality", lensesOf(vars.refactorQualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens])
@@ -2074,4 +2220,4 @@ const steering = {
2074
2220
  [REVIEW]: "review"
2075
2221
  };
2076
2222
  //#endregion
2077
- export { naivePassPrompt as $, reviewQuality as A, conflictBody as At, coverageQuestion as B, flipProblems as Bt, collecting as C, walkthroughBlock as Ct, fixRisks as D, envDefaults as Dt, fixQuality as E, defaults as Et, builderSystem as F, parseStandards as Ft, designTriagePrompt as G, criterionLine as H, requireFlag as Ht, clearCriteriaMessage as I, renderStandards as It, handOffAwaitReviewMessage as J, finisherSystem as K, clearCriteriaQuestion as L, standardKey as Lt, agentWithSkills as M, conflictTitle as Mt, buildReviewCollectingPrompt as N, isConflictTitle as Nt, planner as O, testFileGlobs as Ot, buildReviewTriageMessage as P, oneLine as Pt, naiveApplyFailure as Q, composedReview as R, flagMentions as Rt, coder as S, unwindFailure as St, fixNits as T, withWalkthrough as Tt, designGateAnswerMessage as U, coveringPrompt as V, registryEntry as Vt, designSystem as W, implementBuildingPrompt as X, idleMessage as Y, implementFixPrompt as Z, REQUIREMENTS as _, slicingPrompt as _t, flagModeOn as a, refactorDroppedRefusal as at, answerReviewQuestions as b, uncoveredMessage as bt, baseline as c, refactorRules as ct, healthy as d, reviewFeedbackFixPrompt as dt, normaliseCriteriaScript as et, FEEDBACK as f, reviewGateBlockedMessage as ft, REFACTOR_NOTE as g, seamSteerMessage as gt, REFACTOR as h, seamByHandMessage as ht, skills as i, refactorBuildingPrompt as it, reviewing as j, conflictFor as jt, reviewMissing as k, STANDARDS as kt, gate as l, reviewEditNotesCapture as lt, QUALITY as m, reviewerSystem as mt, summary as n, qualityFindingsFile as nt, flagShipped as o, refactorFixPrompt as ot, HANDOFF as p, reviewNotesCapture as pt, fixReviewNotesPrompt as q, access as r, qualityTriageMessage as rt, shipped as s, refactorNotePrompt as st, steering as t, oversizedMessage as tt, guarded as u, reviewEditsCapture as ut, REVIEW as v, startGateBlockedMessage as vt, fixCheck as w, withLapFindings as wt, awaitReview as x, unwindFailedMessage as xt, SLICES as y, titledNotes as yt, coverageMessage as z, flagReferences as zt };
2223
+ export { implementFixPrompt as $, reviewMissing as A, unranked as At, composedReview as B, conflictBody as Bt, coder as C, seamByHandMessage as Ct, fixQuality as D, testerSystem as Dt, fixNits as E, startGateBlockedMessage as Et, buildReviewTriageMessage as F, withWalkthrough as Ft, designGateAnswerMessage as G, parseStandards as Gt, coverageQuestion as H, conflictTitle as Ht, builderSystem as I, defaults as It, finisherSystem as J, flagMentions as Jt, designSystem as K, renderStandards as Kt, chunkTexts as L, envDefaults as Lt, reviewing as M, unwindFailure as Mt, agentWithSkills as N, walkthroughBlock as Nt, fixRisks as O, titledNotes as Ot, buildReviewCollectingPrompt as P, withLapFindings as Pt, implementBuildingPrompt as Q, requireFlag as Qt, clearCriteriaMessage as R, testFileGlobs as Rt, awaitReview as S, reviewerSystem as St, fixCheck as T, slicingPrompt as Tt, coveringPrompt as U, isConflictTitle as Ut, coverageMessage as V, conflictFor as Vt, criterionLine as W, oneLine as Wt, handOffAwaitReviewMessage as X, flipProblems as Xt, fixReviewNotesPrompt as Y, flagReferences as Yt, idleMessage as Z, registryEntry as Zt, REFACTOR_NOTE as _, reviewEditsCapture as _t, flagModeOn as a, prototypeOpenMessage as at, SLICES as b, reviewNotesCapture as bt, baseline as c, qualityFindingsFile as ct, healthy as d, refactorBuildingPrompt as dt, naiveApplyFailure as et, FEEDBACK as f, refactorDroppedRefusal as ft, REFACTOR as g, reviewEditNotesCapture as gt, QUALITY as h, refactorRules as ht, skills as i, prototypeFixingPrompt as it, reviewQuality as j, unwindFailedMessage as jt, planner as k, uncoveredMessage as kt, gate as l, qualityTriageMessage as lt, PROTOTYPE as m, refactorNotePrompt as mt, summary as n, normaliseCriteriaScript as nt, flagShipped as o, prototypeTestingPrompt as ot, HANDOFF as p, refactorFixPrompt as pt, designTriagePrompt as q, standardKey as qt, access as r, oversizedMessage as rt, shipped as s, prototypeUnguidedMessage as st, steering as t, naivePassPrompt as tt, guarded as u, rankedReview as ut, REQUIREMENTS as v, reviewFeedbackFixPrompt as vt, collecting as w, seamSteerMessage as wt, answerReviewQuestions as x, reviewRankingMessage as xt, REVIEW as y, reviewGateBlockedMessage as yt, clearCriteriaQuestion as z, STANDARDS as zt };
@@ -1,8 +1,8 @@
1
1
  import { answered, changesSince, head, judge, numeric, read, refuse, requireRevert, restoreScript, run, showScript, vars, writeScript } from "../flows.mjs";
2
- import { _ as reviewRisks, m as reviewChunks } from "./steering-BXwjmocB.mjs";
2
+ import { _ as reviewRisks, m as reviewChunks } from "./steering-DtUVUz9l.mjs";
3
3
  import { removedAssertions, review } from "../workflow-kit.mjs";
4
- import { At as conflictBody, C as collecting, Ct as walkthroughBlock, D as fixRisks, Ft as parseStandards, It as renderStandards, Lt as standardKey, Mt as conflictTitle, Nt as isConflictTitle, P as buildReviewTriageMessage, Pt as oneLine, R as composedReview, T as fixNits, Tt as withWalkthrough, b as answerReviewQuestions, d as healthy, j as reviewing, jt as conflictFor, k as reviewMissing, kt as STANDARDS, lt as reviewEditNotesCapture, pt as reviewNotesCapture, u as guarded, ut as reviewEditsCapture, v as REVIEW, w as fixCheck, x as awaitReview, yt as titledNotes } from "./reserved-CYZ1bSBy.mjs";
5
- import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-C1Oi2YEy.mjs";
4
+ import { A as reviewMissing, At as unranked, B as composedReview, Bt as conflictBody, E as fixNits, F as buildReviewTriageMessage, Ft as withWalkthrough, Gt as parseStandards, Ht as conflictTitle, Kt as renderStandards, L as chunkTexts, M as reviewing, Nt as walkthroughBlock, O as fixRisks, Ot as titledNotes, S as awaitReview, T as fixCheck, Ut as isConflictTitle, Vt as conflictFor, Wt as oneLine, _t as reviewEditsCapture, bt as reviewNotesCapture, d as healthy, gt as reviewEditNotesCapture, qt as standardKey, u as guarded, ut as rankedReview, w as collecting, x as answerReviewQuestions, xt as reviewRankingMessage, y as REVIEW, zt as STANDARDS } from "./reserved-CUkk9XgL.mjs";
5
+ import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-CX6Pdsql.mjs";
6
6
  //#region src/workflows/shared/learn.ts
7
7
  const SUMMARY_MAX = 200;
8
8
  const BANNER = /^_(?:The fixer left this unresolved\.|A fix turn removed this while renaming another finding — check whether it was fixed\.|No detail given\.)_\s*/;
@@ -205,7 +205,8 @@ const severityOf = (note, judgment, minP) => {
205
205
  if (critical.length > 0) return {
206
206
  tier: "critical",
207
207
  reason: `critical: ${critical.join(", ")}`,
208
- rank: rank()
208
+ rank: rank(),
209
+ ...a("security") === "yes" ? { security: true } : {}
209
210
  };
210
211
  const should = [...a("blastRadius") === "contract" ? ["contract"] : [], ...a("newConcept") === "yes" ? ["new concept"] : []];
211
212
  if (should.length > 0) return {
@@ -219,29 +220,30 @@ const severityOf = (note, judgment, minP) => {
219
220
  rank: 2
220
221
  };
221
222
  };
222
- const severityQuestions = (note) => [
223
+ /** `asks` names the change in full, `change` refers back to it. */
224
+ const severityQuestions = (id, asks, change) => [
223
225
  {
224
- id: `${note.id}.security`,
226
+ id: `${id}.security`,
225
227
  primitive: "noul",
226
- instructions: `Does the change the human asks for in note "${note.id}" (on "${note.anchor}") touch security — authentication, authorisation, secrets, untrusted input or data exposure?`,
228
+ instructions: `Does ${asks} touch security — authentication, authorisation, secrets, untrusted input or data exposure?`,
227
229
  criteria: "yes: it changes or could weaken a security property. no: it does not."
228
230
  },
229
231
  {
230
- id: `${note.id}.reversibility`,
232
+ id: `${id}.reversibility`,
231
233
  primitive: "choice",
232
- instructions: `Once the change in note "${note.id}" ships, can it be undone without breaking anyone who already depends on it?`,
234
+ instructions: `Once ${change} ships, can it be undone without breaking anyone who already depends on it?`,
233
235
  criteria: "one-way: a published API, file format, persisted data, protocol or other contract that cannot be taken back. two-way: can be reverted freely."
234
236
  },
235
237
  {
236
- id: `${note.id}.blastRadius`,
238
+ id: `${id}.blastRadius`,
237
239
  primitive: "choice",
238
- instructions: `How far does the change in note "${note.id}" reach?`,
240
+ instructions: `How far does ${change} reach?`,
239
241
  criteria: "local: one function or file. module: a whole module or several files. contract: a public interface other code or users depend on."
240
242
  },
241
243
  {
242
- id: `${note.id}.newConcept`,
244
+ id: `${id}.newConcept`,
243
245
  primitive: "noul",
244
- instructions: `Does the change in note "${note.id}" introduce a concept new to this codebase — a new abstraction, entity or term? Judge from the evidence, never a glossary; reusing or extending an existing concept is not new.`,
246
+ instructions: `Does ${change} introduce a concept new to this codebase — a new abstraction, entity or term? Judge from the evidence, never a glossary; reusing or extending an existing concept is not new.`,
245
247
  criteria: "yes: a genuinely new concept. no: reuses or extends an existing one."
246
248
  }
247
249
  ];
@@ -258,7 +260,7 @@ const triage = (severities) => async (notes) => {
258
260
  answers: {},
259
261
  truncated: []
260
262
  } : await judge("review.triage", {
261
- questions: asked.flatMap((n) => [verdictQuestion(n), ...severityQuestions(n)]),
263
+ questions: asked.flatMap((n) => [verdictQuestion(n), ...severityQuestions(n.id, `the change the human asks for in note "${n.id}" (on "${n.anchor}")`, `the change in note "${n.id}"`)]),
262
264
  evidence: Object.fromEntries(asked.map((n) => [n.id, `Anchor: ${n.anchor}\nReviewer's text: ${n.before}\nHuman's text: ${n.text}`])),
263
265
  message: buildReviewTriageMessage(),
264
266
  label: "Judging each note"
@@ -274,6 +276,67 @@ const triage = (severities) => async (notes) => {
274
276
  return [n.id, "edit"];
275
277
  }));
276
278
  };
279
+ /** Review order: security, one-way doors, the judge unsure, then should, then could. */
280
+ const chunkKey = (s) => s.tier === "could" ? 4 : s.tier === "should" ? 3 : s.security === true ? 0 : s.rank === 0 ? 1 : 2;
281
+ /** A chunk's text, then the code each of its pointers covers: a bare path the whole file. */
282
+ const chunkEvidence = (text, files) => [text, ...files.map(({ path, line, rangeEnd }) => {
283
+ const where = `${path}${line === void 0 ? "" : `#${line}${rangeEnd === void 0 ? "" : `-${rangeEnd}`}`}`;
284
+ const lines = read(path.replace(/^\.\//, ""))?.split("\n");
285
+ if (lines === void 0) return `${where}: no longer exists`;
286
+ return `${where}:\n${(line === void 0 ? lines : lines.slice(line - 1, rangeEnd ?? line)).join("\n")}`;
287
+ })].join("\n\n");
288
+ const bytes = (text) => new TextEncoder().encode(text).length;
289
+ /** Fills each batch while the budget's even share still holds its widest evidence, so nothing is cut that fits alone. */
290
+ const batched = (items, budget) => {
291
+ const batches = [];
292
+ let batch = [];
293
+ let widest = 0;
294
+ for (const item of items) {
295
+ const size = bytes(item.evidence);
296
+ if (batch.length > 0 && Math.floor(budget / (batch.length + 1)) < Math.max(widest, size)) {
297
+ batches.push(batch);
298
+ batch = [];
299
+ widest = 0;
300
+ }
301
+ batch.push(item);
302
+ widest = Math.max(widest, size);
303
+ }
304
+ return batch.length === 0 ? batches : [...batches, batch];
305
+ };
306
+ /**
307
+ * Rates every chunk the reviewer wrote — a walkthrough chunk or a carried
308
+ * finding — and sorts \`full\` by it, each chunk led by its tier and reason.
309
+ * gtd's summary chunks are not rated and stay last.
310
+ */
311
+ const ranked = async (review, full) => {
312
+ const texts = chunkTexts(full);
313
+ const chunks = reviewChunks(review).map((c, i) => ({
314
+ id: `C${i + 1}`,
315
+ title: c.title,
316
+ evidence: chunkEvidence(texts.get(c.title) ?? "", c.files)
317
+ }));
318
+ if (chunks.length === 0) return full;
319
+ const minP = numeric(vars.reviewSeverityMinP, Infinity);
320
+ const batches = batched(chunks, numeric(vars.judgeBudgetBytes, 32768));
321
+ const severities = /* @__PURE__ */ new Map();
322
+ for (const [i, batch] of batches.entries()) {
323
+ const judgment = await judge("review.ranking", {
324
+ questions: batch.flatMap((c) => severityQuestions(c.id, `the change review chunk "${c.id}" ("${c.title}") walks through`, `the change in review chunk "${c.id}"`)),
325
+ evidence: Object.fromEntries(batch.map((c) => [c.id, c.evidence])),
326
+ message: reviewRankingMessage(),
327
+ label: batches.length === 1 ? "Ranking the review" : `Ranking the review (${i + 1}/${batches.length})`
328
+ });
329
+ for (const c of batch) severities.set(c.id, severityOf(c, judgment, minP));
330
+ }
331
+ return rankedReview(full, chunks.map((c, i) => ({
332
+ c,
333
+ i,
334
+ s: severities.get(c.id)
335
+ })).sort((x, y) => chunkKey(x.s) - chunkKey(y.s) || x.i - y.i).map(({ c, s }) => ({
336
+ title: c.title,
337
+ label: s.reason
338
+ })));
339
+ };
277
340
  const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
278
341
  const composedOf = (base, report, nitFix, answered) => ({
279
342
  ...report,
@@ -325,12 +388,13 @@ const requirePointers = (review, notes, codeChanged) => {
325
388
  const walkthrough = async (base, report, lap, carry) => {
326
389
  const notes = titledNotes(report);
327
390
  await reviewing(base, carry, lap, walkthroughBlock(notes, report.nitFix));
328
- const review = read(REVIEW);
329
- if (review === void 0) return;
330
- requirePointers(review, notes, changesSince(base).some((c) => isCode(c.path)));
331
- const full = withWalkthrough(review, report);
332
- if (full !== review) await run("review.carrying", writeScript(REVIEW, full), {
333
- label: "Adding the quality lap's findings",
391
+ const written = read(REVIEW);
392
+ if (written === void 0) return;
393
+ const review = unranked(written);
394
+ requirePointers(written, notes, changesSince(base).some((c) => isCode(c.path)));
395
+ const full = await ranked(review, withWalkthrough(review, report));
396
+ if (full !== written) await run("review.carrying", writeScript(REVIEW, full), {
397
+ label: "Ordering the review and adding the quality lap's findings",
334
398
  file: REVIEW,
335
399
  base
336
400
  });
@@ -15089,6 +15089,9 @@ const qaDescriptor = {
15089
15089
  const ANSWER_LEAD = "Tick exactly one:";
15090
15090
  const ANSWER_FIX = "fix as suggested";
15091
15091
  const ANSWER_WONT_FIX = "won't fix";
15092
+ /** A chunk's leading `_critical: security_` paragraph: the tier gtd ranked it with, and why. */
15093
+ const SEVERITY_LINE_RE = /^_((?:critical|should|could)(?::[^_\n]*)?)_$/;
15094
+ const severityLine = (label) => `_${label}_`;
15092
15095
  /**
15093
15096
  * The canonical sample. Every detail here is load-bearing for the formatter
15094
15097
  * round-trip: both footnote bodies exceed 80 characters so a reflow actually
@@ -15379,12 +15382,26 @@ const answerListOf = (content, body) => {
15379
15382
  })
15380
15383
  };
15381
15384
  };
15385
+ /** A leading severity line is the chunk's tier, never its prose. */
15386
+ const splitSeverity = (content, run) => {
15387
+ const first = run[0];
15388
+ const label = first?.type === "paragraph" ? SEVERITY_LINE_RE.exec(sourceText(content, first).trim())?.[1] : void 0;
15389
+ if (label === void 0) return { leadingRun: run };
15390
+ return {
15391
+ severity: {
15392
+ label,
15393
+ line: toLspPosition(first.position.start).line
15394
+ },
15395
+ leadingRun: run.slice(1)
15396
+ };
15397
+ };
15382
15398
  /** Splits one chunk's body nodes into its file pointers (hunk pointers are task items, collected recursively at ANY nesting depth via `taskItems` — a nested hunk is the same kind of hunk as a top-level one) and description prose. The description is the chunk's own leading run and never a node that contains a hunk pointer: it stops at the first node whose `taskItems` include a real pointer (at any depth — a blockquote wrapping a pointer list stops it just as a top-level list would). Every node kind in what's left before that point contributes — a `footnoteDefinition` is the one exception, excluded by `blockNodesOfRun` itself (it's a note, never document content). */
15383
15399
  const parseChunkBody = (content, lines, title, body) => {
15384
15400
  const answer = answerListOf(content, body);
15385
15401
  const isAnswer = (n) => answer !== void 0 && (n === answer.list || n === body[answer.lead]);
15386
15402
  const firstPointerIndex = body.findIndex((n) => !isAnswer(n) && taskItems(n).some((item) => hasPointerToken(content, item)));
15387
- const leadingRun = (firstPointerIndex === -1 ? body : body.slice(0, firstPointerIndex)).filter((n) => !isAnswer(n));
15403
+ const run = (firstPointerIndex === -1 ? body : body.slice(0, firstPointerIndex)).filter((n) => !isAnswer(n));
15404
+ const { severity, leadingRun } = splitSeverity(content, run);
15388
15405
  const descriptionNodes = blockNodesOfRun(content, leadingRun);
15389
15406
  const descriptionInline = collapseImages(blockRunInline(content, leadingRun));
15390
15407
  const description = leadingRun.filter((n) => n.type !== "footnoteDefinition").map((n) => sourceText(content, n)).join(" ").trim();
@@ -15403,6 +15420,7 @@ const parseChunkBody = (content, lines, title, body) => {
15403
15420
  files,
15404
15421
  errors,
15405
15422
  ...answer === void 0 ? {} : { answers: answer.options },
15423
+ ...severity === void 0 ? {} : { severity },
15406
15424
  boxed: body.some((n) => !isAnswer(n) && taskItems(n).length > 0)
15407
15425
  };
15408
15426
  };
@@ -15439,7 +15457,7 @@ const parseChangesets = (tree, content, lines) => {
15439
15457
  const errors = [];
15440
15458
  for (const { heading, headingLine, body } of splitChunks(tree)) {
15441
15459
  const title = headingText$1(content, heading);
15442
- const { description, descriptionNodes, descriptionInline, files, answers, boxed, errors: bodyErrors } = parseChunkBody(content, lines, title, body);
15460
+ const { description, descriptionNodes, descriptionInline, files, answers, severity, boxed, errors: bodyErrors } = parseChunkBody(content, lines, title, body);
15443
15461
  errors.push(...bodyErrors);
15444
15462
  if (files.length === 0 && boxed) errors.push({
15445
15463
  message: `Chunk "${title}" has no file pointers`,
@@ -15453,7 +15471,8 @@ const parseChangesets = (tree, content, lines) => {
15453
15471
  descriptionInline,
15454
15472
  files,
15455
15473
  headingLine,
15456
- ...answers === void 0 ? {} : { answers }
15474
+ ...answers === void 0 ? {} : { answers },
15475
+ ...severity === void 0 ? {} : { severity }
15457
15476
  });
15458
15477
  }
15459
15478
  if (changesets.length === 0) errors.push({ message: "REVIEW.md has no '##' chunks" });
@@ -15754,6 +15773,7 @@ const reviewView = (content) => {
15754
15773
  detail: chunk.description,
15755
15774
  detailInline: chunk.descriptionInline,
15756
15775
  body: chunk.descriptionNodes,
15776
+ ...chunk.severity === void 0 ? {} : { status: chunk.severity.label },
15757
15777
  anchor: {
15758
15778
  kind: "chunk",
15759
15779
  index: chunkIndex
@@ -15901,15 +15921,19 @@ const reviewDescriptor = {
15901
15921
  openThreads: openThreadsOf
15902
15922
  };
15903
15923
  const pointerLabel = (f) => `${f.path}${f.line === void 0 ? "" : `#${f.line}${f.rangeEnd === void 0 ? "" : `-${f.rangeEnd}`}`}`;
15904
- const chunkNote = (chunk, prior) => chunk.description === (prior?.description ?? "") || prior === void 0 && chunk.answers !== void 0 ? [] : [{
15905
- line: chunk.headingLine,
15906
- note: {
15907
- kind: "chunk",
15908
- anchor: chunk.title,
15909
- before: prior?.description ?? "",
15910
- text: chunk.description
15911
- }
15912
- }];
15924
+ const chunkProse = (chunk, retiered) => [chunk?.description ?? "", retiered && chunk?.severity !== void 0 ? severityLine(chunk.severity.label) : ""].filter((p) => p !== "").join("\n\n");
15925
+ const chunkNote = (chunk, prior) => {
15926
+ const retiered = chunk.severity !== void 0 && chunk.severity.label !== prior?.severity?.label;
15927
+ return chunkProse(chunk, retiered) === chunkProse(prior, retiered) || prior === void 0 && chunk.answers !== void 0 ? [] : [{
15928
+ line: chunk.headingLine,
15929
+ note: {
15930
+ kind: "chunk",
15931
+ anchor: chunk.title,
15932
+ before: chunkProse(prior, retiered),
15933
+ text: chunkProse(chunk, retiered)
15934
+ }
15935
+ }];
15936
+ };
15913
15937
  const pointerNotes = (chunk, prior) => chunk.files.flatMap((file) => {
15914
15938
  const label = pointerLabel(file);
15915
15939
  const was = prior?.files.find((f) => pointerLabel(f) === label);
@@ -16023,7 +16047,13 @@ const reviewDecisions = (before, after) => {
16023
16047
  const reviewChunks = (content) => parseReviewDoc(content).changesets.map((c) => ({
16024
16048
  title: c.title,
16025
16049
  line: c.headingLine,
16026
- pointers: c.files.length
16050
+ pointers: c.files.length,
16051
+ ...c.severity === void 0 ? {} : { severityLine: c.severity.line },
16052
+ files: c.files.map((f) => ({
16053
+ path: f.path,
16054
+ ...f.line === void 0 ? {} : { line: f.line },
16055
+ ...f.rangeEnd === void 0 ? {} : { rangeEnd: f.rangeEnd }
16056
+ }))
16027
16057
  }));
16028
16058
  //#endregion
16029
16059
  //#region src/steering/Edits.ts
@@ -16393,4 +16423,4 @@ const viewOf = (format, content) => ({
16393
16423
  /** Every OPEN thread — last entry is the agent's — as `{ name, line, firstMe }` (0-based line). `[]` for a format with no thread concept. */
16394
16424
  const openThreads = (format, content) => format.openThreads?.(content) ?? [];
16395
16425
  //#endregion
16396
- export { formatFinding as A, openThreadFindings as C, getParseCount as D, parseThreadsWithFindings as E, headingSectionBodies as O, THREAD_REPLY_ACTION_TITLE as S, parseThreads as T, reviewRisks as _, unansweredQuestions as a, isAnswered as b, stripCodeThreads as c, ANSWER_FIX as d, ANSWER_LEAD as f, reviewNotes as g, reviewDecisions as h, steeringFormatOrFreeForm as i, headingSections as k, freeFormFormat as l, reviewChunks as m, openThreads as n, viewOf as o, ANSWER_WONT_FIX as p, steeringFormatFor as r, parseCodeThreads as s, BUILT_IN_MODE_NAMES as t, applySteeringEdits as u, unansweredNotes as v, parseFootnotes as w, FOOTNOTE_ACTION_TITLE as x, FREE_TEXT_PLACEHOLDER as y };
16426
+ export { headingSections as A, THREAD_REPLY_ACTION_TITLE as C, parseThreadsWithFindings as D, parseThreads as E, getParseCount as O, FOOTNOTE_ACTION_TITLE as S, parseFootnotes as T, reviewRisks as _, unansweredQuestions as a, FREE_TEXT_PLACEHOLDER as b, stripCodeThreads as c, ANSWER_FIX as d, ANSWER_LEAD as f, reviewNotes as g, reviewDecisions as h, steeringFormatOrFreeForm as i, formatFinding as j, headingSectionBodies as k, freeFormFormat as l, reviewChunks as m, openThreads as n, viewOf as o, ANSWER_WONT_FIX as p, steeringFormatFor as r, parseCodeThreads as s, BUILT_IN_MODE_NAMES as t, applySteeringEdits as u, severityLine as v, openThreadFindings as w, isAnswered as x, unansweredNotes as y };
@@ -1,6 +1,6 @@
1
1
  import { answered, head, judge, mark, numeric, read, refuse, removeScript, run, scope, sectionBodies, showScript, vars, writeScript } from "../flows.mjs";
2
2
  import { lensEntriesOf, lensOf, qualityLap, reviewToHandOff } from "../workflow-kit.mjs";
3
- import { A as reviewQuality, D as fixRisks, E as fixQuality, J as handOffAwaitReviewMessage, d as healthy, j as reviewing, k as reviewMissing, m as QUALITY, nt as qualityFindingsFile, rt as qualityTriageMessage, u as guarded, v as REVIEW, w as fixCheck, wt as withLapFindings, x as awaitReview } from "./reserved-CYZ1bSBy.mjs";
3
+ import { A as reviewMissing, D as fixQuality, M as reviewing, O as fixRisks, Pt as withLapFindings, S as awaitReview, T as fixCheck, X as handOffAwaitReviewMessage, ct as qualityFindingsFile, d as healthy, h as QUALITY, j as reviewQuality, lt as qualityTriageMessage, u as guarded, y as REVIEW } from "./reserved-CUkk9XgL.mjs";
4
4
  //#region src/workflows/shared/lenses.ts
5
5
  const NO_FINDINGS = {
6
6
  notes: [],