@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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +16 -3
- package/dist/chunks/{reserved-CYZ1bSBy.mjs → reserved-CUkk9XgL.mjs} +151 -5
- package/dist/chunks/{review-BewyJflS.mjs → review-BaCXlIT2.mjs} +84 -20
- package/dist/chunks/{steering-BXwjmocB.mjs → steering-DtUVUz9l.mjs} +44 -14
- package/dist/chunks/{tail-C1Oi2YEy.mjs → tail-CX6Pdsql.mjs} +1 -1
- package/dist/gtd.bundle.mjs +22 -5
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +1 -1
- package/dist/web/main.js +27 -3
- package/dist/workflow-kit.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +144 -11
- 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 +415 -42
- package/package.json +1 -1
- package/src/steering/index.ts +1 -0
- package/src/steering/review.test.ts +60 -2
- package/src/steering/review.ts +66 -9
- package/src/workflows/feature/coverage.feature +1 -0
- package/src/workflows/feature/fast-path.feature +1 -0
- package/src/workflows/feature/feature.ts +4 -1
- package/src/workflows/feature/flag-mode.feature +1 -1
- package/src/workflows/feature/lens-models.feature +1 -0
- package/src/workflows/feature/lens-review.feature +17 -4
- package/src/workflows/feature/one-step-land.feature +1 -0
- package/src/workflows/feature/prototype-rounds.feature +152 -0
- package/src/workflows/feature/prototype.feature +419 -0
- package/src/workflows/feature/prototype.test.ts +43 -0
- package/src/workflows/feature/prototype.ts +181 -0
- package/src/workflows/feature/refactor-absorbs.feature +9 -1
- package/src/workflows/feature/refactor-first.feature +13 -3
- package/src/workflows/feature/refactor-rebuild.feature +29 -5
- package/src/workflows/feature/refactor-review.feature +111 -13
- package/src/workflows/feature/refactor.test.ts +14 -4
- package/src/workflows/feature/refactor.ts +2 -0
- package/src/workflows/feature/review-answers.feature +9 -1
- package/src/workflows/feature/review-gate.feature +21 -3
- package/src/workflows/feature/reviewer-walkthrough.feature +15 -3
- package/src/workflows/feature/seam-rejection.feature +1 -0
- package/src/workflows/feature/seam.feature +1 -0
- package/src/workflows/feature/seam.test.ts +3 -2
- package/src/workflows/feature/seam.ts +19 -5
- package/src/workflows/feature/severity-budget.feature +112 -0
- package/src/workflows/feature/severity-sorted-review.feature +198 -0
- package/src/workflows/feature/slice-findings.feature +15 -2
- package/src/workflows/feature/slicing-cap.feature +9 -0
- package/src/workflows/feature/slicing.feature +1 -0
- package/src/workflows/feature/standards.feature +5 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/prose.ts +6 -0
- package/src/workflows/shared/review.ts +124 -18
- package/src/workflows/shared/skills.test.ts +4 -0
- package/src/workflows/shared/skills.ts +5 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.test.ts +90 -0
- package/src/workflows/shared/text.ts +181 -4
- package/src/workflows/shared/vars.ts +3 -0
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.
|
|
542
|
-
|
|
543
|
-
|
|
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,
|
|
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
|
|
699
|
-
test
|
|
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
|
|
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 {
|
|
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-
|
|
2
|
+
import { _ as reviewRisks, m as reviewChunks } from "./steering-DtUVUz9l.mjs";
|
|
3
3
|
import { removedAssertions, review } from "../workflow-kit.mjs";
|
|
4
|
-
import { At as
|
|
5
|
-
import { i as isOptionalObject, n as closeReview, o as tail, s as NO_FINDINGS } from "./tail-
|
|
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
|
-
|
|
223
|
+
/** `asks` names the change in full, `change` refers back to it. */
|
|
224
|
+
const severityQuestions = (id, asks, change) => [
|
|
223
225
|
{
|
|
224
|
-
id: `${
|
|
226
|
+
id: `${id}.security`,
|
|
225
227
|
primitive: "noul",
|
|
226
|
-
instructions: `Does
|
|
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: `${
|
|
232
|
+
id: `${id}.reversibility`,
|
|
231
233
|
primitive: "choice",
|
|
232
|
-
instructions: `Once
|
|
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: `${
|
|
238
|
+
id: `${id}.blastRadius`,
|
|
237
239
|
primitive: "choice",
|
|
238
|
-
instructions: `How far does
|
|
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: `${
|
|
244
|
+
id: `${id}.newConcept`,
|
|
243
245
|
primitive: "noul",
|
|
244
|
-
instructions: `Does
|
|
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
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
|
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
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
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 {
|
|
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
|
|
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: [],
|