@pmelab/gtd 37.0.0 → 38.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 +9 -0
- package/dist/chunks/{reserved-zAfoJtR7.mjs → reserved-sfMdy7v7.mjs} +33 -9
- package/dist/chunks/{tail-BRC4DGdV.mjs → tail-Cgca5Gp-.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +168 -42
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +198 -48
- package/package.json +1 -1
- package/src/testkit/steps.ts +35 -0
- package/src/workflows/feature/coverage.ts +14 -6
- package/src/workflows/feature/criteria.test.ts +17 -1
- package/src/workflows/feature/criteria.ts +26 -3
- package/src/workflows/feature/feature.ts +64 -15
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/seam.ts +63 -26
- package/src/workflows/feature/slicing-cap.feature +14 -0
- package/src/workflows/feature/slicing.feature +225 -0
- package/src/workflows/feature/slicing.test.ts +49 -0
- package/src/workflows/feature/slicing.ts +105 -0
- package/src/workflows/index.ts +1 -0
- package/src/workflows/shared/fences.ts +5 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.ts +50 -15
- package/src/workflows/shared/vars.ts +1 -0
- package/src/workflows/shared/workflows.steps.ts +13 -0
package/README.md
CHANGED
|
@@ -638,6 +638,15 @@ out is `gtd abandon`. A process from 27.5.0 or later whose judge ruled
|
|
|
638
638
|
that slice. Details in
|
|
639
639
|
[the naive-pass path](docs/configuration.md#the-naive-pass-path).
|
|
640
640
|
|
|
641
|
+
**Coarse slices (`sliceMaxCriteria`, default 8).** A change with more acceptance
|
|
642
|
+
criteria than the cap is built in slices: after design an agent groups related
|
|
643
|
+
criteria (same flow or subsystem) into `.gtd/SLICES.md`, as few slices as the
|
|
644
|
+
cap allows, and each slice — naive pass, refactor and rebuild when judged so,
|
|
645
|
+
health loop, quality lap — runs in order from where the previous one landed.
|
|
646
|
+
`gtd slices` lists them as `feature-1`, `feature-2`, … (`refactor-<n>` ahead of
|
|
647
|
+
a refactored one). A change within the cap is one `feature` slice, as before.
|
|
648
|
+
See [Coarse slices](docs/configuration.md#coarse-slices).
|
|
649
|
+
|
|
641
650
|
**Flag mode (`flagMode`, on by default).** On, the naive pass's judge runs as
|
|
642
651
|
above and a rebuilt implementation is asked for a new release flag (none at a
|
|
643
652
|
`low` risk tier); a pass the judge lets land is not asked for one. A project
|
|
@@ -133,6 +133,7 @@ const defaults = {
|
|
|
133
133
|
refactorFlagPattern: "\\bGTD_FLAG_\\w+",
|
|
134
134
|
seamAllow: [...testFileGlobs, ".gtd/**"].join(", "),
|
|
135
135
|
flagMode: "true",
|
|
136
|
+
sliceMaxCriteria: "8",
|
|
136
137
|
flag_risk_tier: "0",
|
|
137
138
|
qualityReviews: "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
|
|
138
139
|
refactorQualityReviews: "correctness, ponytail-review, conventions"
|
|
@@ -592,6 +593,24 @@ printf '%s\\n' ${[
|
|
|
592
593
|
].map(quote).join(" ")} > ${quote(".gtd/REQUIREMENTS.md")}
|
|
593
594
|
`;
|
|
594
595
|
};
|
|
596
|
+
const slicingPrompt = (cap) => `${stateFileRules}
|
|
597
|
+
- Read \`.gtd/REQUIREMENTS.md\`: its \`## Acceptance Criteria\` are settled
|
|
598
|
+
and more than ${cap}, too many to build in one slice
|
|
599
|
+
- Group them into slices in \`.gtd/SLICES.md\`, one \`## <title>\` heading per
|
|
600
|
+
slice over a \`- AC-<n>\` list item per criterion it builds. Put criteria of
|
|
601
|
+
the same flow or subsystem together; every criterion in exactly one slice,
|
|
602
|
+
at most ${cap} per slice
|
|
603
|
+
- Keep it coarse: as few slices as the cap allows, never one per criterion
|
|
604
|
+
- Order the slices so each builds on the ones before: they run in that order,
|
|
605
|
+
each from where the previous one landed
|
|
606
|
+
- Change nothing but \`.gtd/SLICES.md\` — leave the criteria as they are
|
|
607
|
+
- Leave everything uncommitted and finish your turn — do not commit
|
|
608
|
+
`;
|
|
609
|
+
/** The line telling a slice's turn which criteria are its own; empty for a single slice. */
|
|
610
|
+
const sliceLine = (ids) => ids === void 0 ? "" : `- This slice builds ${ids.join(", ")} only: every rule here about the criteria
|
|
611
|
+
means these. The others are for other slices, before or after it — leave
|
|
612
|
+
them unbuilt and untested
|
|
613
|
+
`;
|
|
595
614
|
const designSystem = () => `${designPersona}
|
|
596
615
|
|
|
597
616
|
${agentConduct}`;
|
|
@@ -1238,9 +1257,9 @@ re-implement. It changed these files outside the expected set:
|
|
|
1238
1257
|
${listed(retry.strays)}
|
|
1239
1258
|
Start over and choose a different approach; do not rebuild the same diff.
|
|
1240
1259
|
`;
|
|
1241
|
-
const naivePassPrompt = (retry) => `${stateFileRules}
|
|
1260
|
+
const naivePassPrompt = (retry, slice) => `${stateFileRules}
|
|
1242
1261
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1243
|
-
${criteriaBinding(false)}
|
|
1262
|
+
${sliceLine(slice)}${criteriaBinding(false)}
|
|
1244
1263
|
- Implement them the quickest way that works — the naive version. Do not
|
|
1245
1264
|
restructure, abstract or refactor around the change; it may land exactly as
|
|
1246
1265
|
you leave it, and a reviewer makes it fit
|
|
@@ -1290,9 +1309,13 @@ const afterRefactorLine = (rebuild) => `- A reviewed refactor has just landed an
|
|
|
1290
1309
|
though never a release flag. Build only what is still missing, held to every
|
|
1291
1310
|
rule below; if nothing is, ${carries(rebuild) ? "carry over the acceptance tests below and change nothing else" : "change nothing and finish your turn"}
|
|
1292
1311
|
`;
|
|
1293
|
-
const
|
|
1312
|
+
const flagLine = (flagMode, tier, amend, sliced) => !flagMode ? "- Add no release flag — this project ships without release flags" : tier === "low" ? "- Add no release flag — this change is low risk tier" : amend === true ? "- Keep the release flag the earlier lap added, if it added one; otherwise put\n the behaviour behind a new release flag" : sliced ? `- Put the new behaviour behind the release flag an earlier slice of this
|
|
1313
|
+
change added; only when none did, behind a new one, following the
|
|
1314
|
+
repository's flag conventions` : `- Put the new behaviour behind a new release flag, following the
|
|
1315
|
+
repository's flag conventions`;
|
|
1316
|
+
const implementBuildingPrompt = (reference, tier, amend, afterRefactor, flagMode = true, rebuild = {}, slice) => `${stateFileRules}
|
|
1294
1317
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1295
|
-
${criteriaBinding(true)}
|
|
1318
|
+
${sliceLine(slice)}${criteriaBinding(true)}
|
|
1296
1319
|
${afterRefactor === true ? afterRefactorLine(rebuild) : ""}${amend === true ? `- The change already exists in the tree from an earlier lap; amend it to meet
|
|
1297
1320
|
the re-derived requirements instead of rewriting it
|
|
1298
1321
|
` : flagMode ? `- Implement them properly, fitting the codebase's seams and conventions
|
|
@@ -1301,8 +1324,7 @@ ${afterRefactor === true ? afterRefactorLine(rebuild) : ""}${amend === true ? `-
|
|
|
1301
1324
|
`}${reference === void 0 || amend === true ? "" : `- Reference only: \`git show ${reference}\` shows an earlier naive attempt.
|
|
1302
1325
|
Read it for ideas; never apply, cherry-pick or check it out — write
|
|
1303
1326
|
the change fresh${carries(rebuild) ? ", its acceptance tests excepted (below)" : ""}
|
|
1304
|
-
`}${rebuildLines(rebuild)}${
|
|
1305
|
-
repository's flag conventions`}
|
|
1327
|
+
`}${rebuildLines(rebuild)}${flagLine(flagMode, tier, amend, slice !== void 0)}
|
|
1306
1328
|
- Write the tests first, then the code that makes them pass. Name each
|
|
1307
1329
|
criterion's ID in the test that covers it (\`[AC-3] …\`): once the suite is
|
|
1308
1330
|
green a judge checks every ID has a passing test, and sends uncovered ones back
|
|
@@ -1383,9 +1405,9 @@ const coverageMessage = () => `Judging acceptance-criteria coverage. Run \`gtd j
|
|
|
1383
1405
|
\`yes\`/\`no\` verdict per criterion ID — or land without a verdict, which
|
|
1384
1406
|
counts every criterion as uncovered.
|
|
1385
1407
|
`;
|
|
1386
|
-
const coveringPrompt = (uncovered) => `${stateFileRules}
|
|
1408
|
+
const coveringPrompt = (uncovered, slice) => `${stateFileRules}
|
|
1387
1409
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1388
|
-
${criteriaBinding(true)}
|
|
1410
|
+
${sliceLine(slice)}${criteriaBinding(true)}
|
|
1389
1411
|
- A judge found no passing test in this change that covers ${uncovered.join(", ")}.
|
|
1390
1412
|
Write a behaviour-level test for each, naming its ID in the test or scenario
|
|
1391
1413
|
name (\`[${uncovered[0]}] …\`), and fix the code if the test shows it falls short
|
|
@@ -1412,6 +1434,7 @@ const REVIEW = ".gtd/REVIEW.md";
|
|
|
1412
1434
|
const REFACTOR = ".gtd/REFACTOR.md";
|
|
1413
1435
|
const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md";
|
|
1414
1436
|
const QUALITY = ".gtd/QUALITY.md";
|
|
1437
|
+
const SLICES = ".gtd/SLICES.md";
|
|
1415
1438
|
const planner = () => env.plannerModel ?? "";
|
|
1416
1439
|
const coder = () => env.coderModel ?? "";
|
|
1417
1440
|
const fixCheck = (rules) => agentWithSkills("fix", buildFixPrompt(rules), {
|
|
@@ -1605,6 +1628,7 @@ const ShippedOptions = Struct({
|
|
|
1605
1628
|
reviewSeverityMinP: setting,
|
|
1606
1629
|
refactorFlagPattern: setting,
|
|
1607
1630
|
seamAllow: setting,
|
|
1631
|
+
sliceMaxCriteria: setting,
|
|
1608
1632
|
qualityReviews: setting,
|
|
1609
1633
|
refactorQualityReviews: setting,
|
|
1610
1634
|
testCommand: setting,
|
|
@@ -1710,4 +1734,4 @@ const steering = {
|
|
|
1710
1734
|
[REVIEW]: "review"
|
|
1711
1735
|
};
|
|
1712
1736
|
//#endregion
|
|
1713
|
-
export {
|
|
1737
|
+
export { naivePassPrompt as $, buildReviewAnswerQuestionsPrompt as A, coveringPrompt as B, fixQuality as C, flagReferences as Ct, reviewQuality as D, reviewMissing as E, requireFlag as Et, builderSystem as F, designTriagePrompt as G, deferNotesScript as H, clearCriteriaMessage as I, handOffAwaitReviewMessage as J, finisherSystem as K, clearCriteriaQuestion as L, buildReviewCollectingPrompt as M, buildReviewFixNitsPrompt as N, reviewing as O, buildReviewTriageMessage as P, naiveApplyFailure as Q, coverageMessage as R, fixCheck as S, flagMentions as St, planner as T, registryEntry as Tt, designGateAnswerMessage as U, criterionLine as V, designSystem as W, implementBuildingPrompt as X, idleMessage as Y, implementFixPrompt as Z, REQUIREMENTS as _, unwindFailedMessage as _t, flagModeOn as a, refactorNotePrompt as at, awaitReview as b, envDefaults as bt, baseline as c, reviewEditsCapture as ct, guarded as d, reviewerSystem as dt, normaliseCriteriaScript as et, healthy as f, seamByHandMessage as ft, REFACTOR_NOTE as g, uncoveredMessage as gt, REFACTOR as h, startGateBlockedMessage as ht, skills as i, refactorFixPrompt as it, buildReviewAwaitAfterDeferMessage as j, agentWithSkills as k, escalation as l, reviewGateBlockedMessage as lt, QUALITY as m, slicingPrompt as mt, summary as n, refactorBuildingPrompt as nt, flagShipped as o, refactorRules as ot, FEEDBACK as p, seamSteerMessage as pt, fixReviewNotesPrompt as q, access as r, refactorDroppedRefusal as rt, shipped as s, reviewEditNotesCapture as st, steering as t, qualityUnresolvedMessage as tt, gate as u, reviewNotesCapture as ut, REVIEW as v, unwindFailure as vt, fixRisks as w, flipProblems as wt, coder as x, testFileGlobs as xt, SLICES as y, defaults as yt, coverageQuestion as z };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, showScript, vars } from "../flows.mjs";
|
|
2
2
|
import { fixFindings, lensEntriesOf, lensOf, qualityLap as qualityLap$1, reviewToHandOff } from "../workflow-kit.mjs";
|
|
3
|
-
import { C as
|
|
3
|
+
import { C as fixQuality, D as reviewQuality, E as reviewMissing, J as handOffAwaitReviewMessage, O as reviewing, S as fixCheck, b as awaitReview, d as guarded, f as healthy, l as escalation, m as QUALITY, v as REVIEW, w as fixRisks } from "./reserved-sfMdy7v7.mjs";
|
|
4
4
|
//#region src/workflows/shared/tail.ts
|
|
5
5
|
/** The reviewer's own `.gtd/REVIEW.md`, kept beside the human's notes so `fix` can tell them apart. */
|
|
6
6
|
const REVIEW_ORIG = ".gtd/REVIEW.orig.md";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
|
|
2
|
-
import { Ct as
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { Ct as flagReferences, Et as requireFlag, Tt as registryEntry, bt as envDefaults, i as skills, n as summary, o as flagShipped, r as access, t as steering, x as coder, yt as defaults } from "../chunks/reserved-sfMdy7v7.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-Cgca5Gp-.mjs";
|
|
4
4
|
//#region src/workflows/cleanup/cleanup.ts
|
|
5
5
|
const removingPrompt = (name, registry) => `Clean up the release flag "${name}", which is default-on: delete its entry in ${registry}, and remove every \`GTD_FLAG_${name.toUpperCase()}\` check from the shipped sources. Keep the flag-on path; delete code only the off path reached, including tests that pin the off path.`;
|
|
6
6
|
const cleanupFlow = async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { agent, answered, applyScript, changes, changesSince, check, codeChanges, env, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, matches, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, slice, start, vars, wrote } from "../flows.mjs";
|
|
2
2
|
import { lensEntriesOf, review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-
|
|
3
|
+
import { $ as naivePassPrompt, A as buildReviewAnswerQuestionsPrompt, B as coveringPrompt, E as reviewMissing, F as builderSystem, G as designTriagePrompt, H as deferNotesScript, I as clearCriteriaMessage, L as clearCriteriaQuestion, M as buildReviewCollectingPrompt, N as buildReviewFixNitsPrompt, O as reviewing, P as buildReviewTriageMessage, Q as naiveApplyFailure, R as coverageMessage, S as fixCheck, T as planner, U as designGateAnswerMessage, V as criterionLine, W as designSystem, X as implementBuildingPrompt, Y as idleMessage, Z as implementFixPrompt, _ as REQUIREMENTS, _t as unwindFailedMessage, a as flagModeOn, at as refactorNotePrompt, b as awaitReview, bt as envDefaults, ct as reviewEditsCapture, d as guarded, dt as reviewerSystem, et as normaliseCriteriaScript, f as healthy, ft as seamByHandMessage, g as REFACTOR_NOTE, gt as uncoveredMessage, h as REFACTOR, ht as startGateBlockedMessage, i as skills, it as refactorFixPrompt, j as buildReviewAwaitAfterDeferMessage, k as agentWithSkills, m as QUALITY, mt as slicingPrompt, n as summary, nt as refactorBuildingPrompt, ot as refactorRules, p as FEEDBACK, pt as seamSteerMessage, r as access, rt as refactorDroppedRefusal, s as shipped, st as reviewEditNotesCapture, t as steering, tt as qualityUnresolvedMessage, u as gate, ut as reviewNotesCapture, v as REVIEW, vt as unwindFailure, w as fixRisks, x as coder, xt as testFileGlobs, y as SLICES, yt as defaults, z as coverageQuestion } from "../chunks/reserved-sfMdy7v7.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-Cgca5Gp-.mjs";
|
|
5
5
|
import { posix } from "node:path";
|
|
6
6
|
//#region src/workflows/shared/fences.ts
|
|
7
7
|
const markFenced = (text) => {
|
|
@@ -17,13 +17,15 @@ const markFenced = (text) => {
|
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
|
+
/** A markdown heading: its `#` run, then its text. */
|
|
21
|
+
const HEADING = /^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/;
|
|
22
|
+
/** A list item, its checkbox dropped: its text. */
|
|
23
|
+
const ITEM = /^ ?(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(.*)$/;
|
|
20
24
|
//#endregion
|
|
21
25
|
//#region src/workflows/feature/criteria.ts
|
|
22
26
|
/** Below this the judge's `yes` is not trusted: a wrong skip builds against criteria nobody can check. */
|
|
23
27
|
const CLEAR_MIN_P = .8;
|
|
24
|
-
const
|
|
25
|
-
const ITEM = /^ ?(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(.*)$/;
|
|
26
|
-
const ID = /^(AC-\d+):\s*(.*)$/;
|
|
28
|
+
const ID$1 = /^(AC-\d+):\s*(.*)$/;
|
|
27
29
|
/** The list items under the first heading `title` matches (at `level` when given), up to the next heading; wrapped lines joined, fenced lines ignored. */
|
|
28
30
|
const listUnder = (text, title, level) => {
|
|
29
31
|
const lines = markFenced(text);
|
|
@@ -51,7 +53,7 @@ const listUnder = (text, title, level) => {
|
|
|
51
53
|
};
|
|
52
54
|
/** The `- AC-<n>: …` list of `## Acceptance Criteria`, or `undefined` when the section is missing. */
|
|
53
55
|
const criteriaOf = (text) => listUnder(text, /^acceptance criteria$/i, 2)?.map((item) => {
|
|
54
|
-
const id = ID.exec(item);
|
|
56
|
+
const id = ID$1.exec(item);
|
|
55
57
|
return id === null ? {
|
|
56
58
|
id: "",
|
|
57
59
|
text: item
|
|
@@ -60,16 +62,20 @@ const criteriaOf = (text) => listUnder(text, /^acceptance criteria$/i, 2)?.map((
|
|
|
60
62
|
text: id[2]
|
|
61
63
|
};
|
|
62
64
|
});
|
|
65
|
+
/** The ID'd criteria of `text`, only `slice`'s when given. */
|
|
66
|
+
const criterionIds = (text, slice) => (criteriaOf(text) ?? []).map((c) => c.id).filter((id) => id !== "" && (slice?.only === void 0 || slice.only.includes(id)) && !(slice?.except ?? []).includes(id));
|
|
67
|
+
/** `slice`'s IDs in `.gtd/REQUIREMENTS.md` as it is now; `undefined` for an unsliced change. */
|
|
68
|
+
const sliceIds = (slice) => slice === void 0 ? void 0 : criterionIds(read(".gtd/REQUIREMENTS.md") ?? "", slice);
|
|
63
69
|
/** Items numbered `AC-1`…, unless every one already carries its own distinct ID. */
|
|
64
70
|
const numbered = (items) => {
|
|
65
|
-
const ids = items.map((item) => ID.exec(item));
|
|
71
|
+
const ids = items.map((item) => ID$1.exec(item));
|
|
66
72
|
const own = ids.every((id) => id !== null) && new Set(ids.map((id) => id[1])).size === ids.length;
|
|
67
73
|
return items.map((item, i) => own ? {
|
|
68
74
|
id: ids[i][1],
|
|
69
75
|
text: ids[i][2]
|
|
70
76
|
} : {
|
|
71
77
|
id: `AC-${i + 1}`,
|
|
72
|
-
text: item.replace(ID, "$2")
|
|
78
|
+
text: item.replace(ID$1, "$2")
|
|
73
79
|
});
|
|
74
80
|
};
|
|
75
81
|
const STATED = /^acceptance criteria:?$/i;
|
|
@@ -759,10 +765,12 @@ const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", imple
|
|
|
759
765
|
const testsSince = (base) => changesSince(base).filter((c) => c.after !== void 0 && isCode$1(c.path) && isTestFile(c.path)).sort((a, b) => Number(/\bAC-\d/.test(a.after)) - Number(/\bAC-\d/.test(b.after))).map((c) => `=== ${c.path} (${c.status})\n${c.after}`).join("\n");
|
|
760
766
|
/**
|
|
761
767
|
* The criterion IDs of `.gtd/REQUIREMENTS.md` — read afresh, never carried
|
|
762
|
-
* over — no passing test since `base` covers, by the judge.
|
|
768
|
+
* over, only `slice`'s when given — no passing test since `base` covers, by the judge.
|
|
763
769
|
*/
|
|
764
|
-
const uncoveredSince = async (base) => {
|
|
765
|
-
const
|
|
770
|
+
const uncoveredSince = async (base, slice) => {
|
|
771
|
+
const requirements = read(".gtd/REQUIREMENTS.md") ?? "";
|
|
772
|
+
const own = criterionIds(requirements, slice);
|
|
773
|
+
const criteria = (criteriaOf(requirements) ?? []).filter((c) => own.includes(c.id));
|
|
766
774
|
if (criteria.length === 0) return {
|
|
767
775
|
covered: [],
|
|
768
776
|
uncovered: []
|
|
@@ -789,14 +797,14 @@ const record = (covered, uncovered = []) => {
|
|
|
789
797
|
* sending uncovered IDs back to a covering turn; marks the IDs covered on the
|
|
790
798
|
* landing that ends it. A human may overrule once the retries are spent.
|
|
791
799
|
*/
|
|
792
|
-
const requireCoverage = async (base = start()) => {
|
|
800
|
+
const requireCoverage = async (base = start(), slice) => {
|
|
793
801
|
let retries = 0;
|
|
794
802
|
for (;;) {
|
|
795
|
-
const { covered, uncovered } = await uncoveredSince(base);
|
|
803
|
+
const { covered, uncovered } = await uncoveredSince(base, slice);
|
|
796
804
|
if (uncovered.length === 0) return record(covered);
|
|
797
805
|
if (retries < COVERING_RETRIES) {
|
|
798
806
|
retries++;
|
|
799
|
-
await agentWithSkills("covering", coveringPrompt(uncovered), {
|
|
807
|
+
await agentWithSkills("covering", coveringPrompt(uncovered, sliceIds(slice)), {
|
|
800
808
|
label: "Covering the acceptance criteria",
|
|
801
809
|
model: coder(),
|
|
802
810
|
system: builderSystem(),
|
|
@@ -827,7 +835,7 @@ const judgeSeam = (tip) => judgeDiffShape("judge", {
|
|
|
827
835
|
allow: (vars.seamAllow ?? defaults.seamAllow).split(",").map((glob) => glob.trim()).filter((glob) => glob !== ""),
|
|
828
836
|
exempt: tip.changes.filter((c) => c.status === "added").map((c) => c.path)
|
|
829
837
|
});
|
|
830
|
-
const naivePass = (retry) => agentWithSkills("naive", naivePassPrompt(retry), {
|
|
838
|
+
const naivePass = (retry, slice) => agentWithSkills("naive", naivePassPrompt(retry, sliceIds(slice)), {
|
|
831
839
|
label: retry === void 0 ? "Writing the naive pass" : "Rewriting the naive pass",
|
|
832
840
|
keep: true,
|
|
833
841
|
model: coder(),
|
|
@@ -868,7 +876,7 @@ const addedLines = (change) => {
|
|
|
868
876
|
return change.after.split("\n").filter((line) => !before.has(line)).join("\n");
|
|
869
877
|
};
|
|
870
878
|
const acceptanceTests = (changes) => changes.filter((c) => c.after !== void 0 && isCode$1(c.path) && isTestFile(c.path));
|
|
871
|
-
const idsOf = (file) => (
|
|
879
|
+
const idsOf = (file, slice) => criterionIds(read(file) ?? "", slice);
|
|
872
880
|
const unnamed = (ids, tests) => {
|
|
873
881
|
const lines = tests.map(addedLines);
|
|
874
882
|
return ids.filter((id) => !lines.some((text) => named(id, text)));
|
|
@@ -878,9 +886,9 @@ const unnamed = (ids, tests) => {
|
|
|
878
886
|
* test line of the pass names, or a test importing a file the pass added —
|
|
879
887
|
* naive structure a reset and refactor replaces, which the tests must survive.
|
|
880
888
|
*/
|
|
881
|
-
const acceptanceFault = (file, tip) => {
|
|
889
|
+
const acceptanceFault = (file, tip, slice) => {
|
|
882
890
|
const tests = acceptanceTests(tip.changes);
|
|
883
|
-
const untested = unnamed(idsOf(file), tests);
|
|
891
|
+
const untested = unnamed(idsOf(file, slice), tests);
|
|
884
892
|
if (untested.length > 0) return `acceptance-tests: no test of the naive pass names ${untested.join(", ")} — write a behaviour-level test for each criterion with its ID in the test or scenario name, e.g. "[${untested[0]}] …"`;
|
|
885
893
|
const added = tip.changes.filter((c) => c.status === "added" && isCode$1(c.path) && !isTestFile(c.path)).map((c) => c.path);
|
|
886
894
|
const reaching = tests.filter((test) => holdsCallSites(test.path)).flatMap((test) => {
|
|
@@ -890,8 +898,8 @@ const acceptanceFault = (file, tip) => {
|
|
|
890
898
|
if (reaching.length > 0) return `acceptance-internals: these tests import files the naive pass added — test through code that existed before it (a public entry point, an existing module's exports):\n${reaching.join("\n")}`;
|
|
891
899
|
};
|
|
892
900
|
/** What a rebuild carries over from the naive pass `tip`: the IDs its tests name, and those test files. */
|
|
893
|
-
const naiveAcceptance = (tip) => {
|
|
894
|
-
const ids = idsOf(REQUIREMENTS);
|
|
901
|
+
const naiveAcceptance = (tip, slice) => {
|
|
902
|
+
const ids = idsOf(REQUIREMENTS, slice);
|
|
895
903
|
const perTest = acceptanceTests(tip.changes).map((c) => {
|
|
896
904
|
const lines = addedLines(c);
|
|
897
905
|
return {
|
|
@@ -970,7 +978,7 @@ function implement(first, tier, amend) {
|
|
|
970
978
|
const since = head();
|
|
971
979
|
const acceptance = options.rebuild?.acceptance;
|
|
972
980
|
const built = options.naive === void 0 || !await applied(options.naive);
|
|
973
|
-
if (built) await agentWithSkills("building", implementBuildingPrompt(options.reference, options.tier, options.amend, options.afterRefactor, flagModeOn(), options.rebuild), {
|
|
981
|
+
if (built) await agentWithSkills("building", implementBuildingPrompt(options.reference, options.tier, options.amend, options.afterRefactor, flagModeOn(), options.rebuild, sliceIds(options.slice)), {
|
|
974
982
|
label: "Implementing",
|
|
975
983
|
model: coder(),
|
|
976
984
|
system: builderSystem(),
|
|
@@ -979,10 +987,18 @@ function implement(first, tier, amend) {
|
|
|
979
987
|
requireCriteriaFrozen(REQUIREMENTS);
|
|
980
988
|
if (built && acceptance !== void 0) requireAcceptance(acceptance, since);
|
|
981
989
|
await green(built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0);
|
|
982
|
-
await requireCoverage();
|
|
983
|
-
await
|
|
990
|
+
await requireCoverage(options.base, options.slice);
|
|
991
|
+
await closing(options.more === true);
|
|
984
992
|
});
|
|
985
993
|
}
|
|
994
|
+
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
995
|
+
const closeOut = () => scope("implement", () => closing(false));
|
|
996
|
+
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
997
|
+
const closing = (more) => run("closing", removeScript(more ? [REFACTOR_NOTE] : [
|
|
998
|
+
REQUIREMENTS,
|
|
999
|
+
REFACTOR_NOTE,
|
|
1000
|
+
SLICES
|
|
1001
|
+
]), { label: "Closing out the implementation" });
|
|
986
1002
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
987
1003
|
const steer = async (retry) => {
|
|
988
1004
|
await human("steer", {
|
|
@@ -1013,8 +1029,8 @@ const budget = () => {
|
|
|
1013
1029
|
};
|
|
1014
1030
|
};
|
|
1015
1031
|
/** Sends a pass whose tests fall short or run red back (`undefined`: it goes on, an overruled one as it is). */
|
|
1016
|
-
const vetted = async (tip, vetting) => {
|
|
1017
|
-
const fault = vetting.waived ? void 0 : acceptanceFault(REQUIREMENTS, tip);
|
|
1032
|
+
const vetted = async (tip, vetting, slice) => {
|
|
1033
|
+
const fault = vetting.waived ? void 0 : acceptanceFault(REQUIREMENTS, tip, slice);
|
|
1018
1034
|
if (fault !== void 0) {
|
|
1019
1035
|
const verdict = await vetting.again({
|
|
1020
1036
|
fault,
|
|
@@ -1048,16 +1064,16 @@ const ruled = async (tip, again) => {
|
|
|
1048
1064
|
tip
|
|
1049
1065
|
} : next;
|
|
1050
1066
|
};
|
|
1051
|
-
const decide = () => scope("seam", async () => {
|
|
1067
|
+
const decide = (slice) => scope("seam", async () => {
|
|
1052
1068
|
const vetting = {
|
|
1053
1069
|
again: budget(),
|
|
1054
1070
|
waived: false
|
|
1055
1071
|
};
|
|
1056
1072
|
let retry;
|
|
1057
1073
|
for (;;) {
|
|
1058
|
-
await naivePass(retry);
|
|
1074
|
+
await naivePass(retry, slice);
|
|
1059
1075
|
const tip = kept("naive");
|
|
1060
|
-
const outcome = await vetted(tip, vetting) ?? (flagModeOn() ? await ruled(tip, vetting.again) : {
|
|
1076
|
+
const outcome = await vetted(tip, vetting, slice) ?? (flagModeOn() ? await ruled(tip, vetting.again) : {
|
|
1061
1077
|
reference: tip.hash,
|
|
1062
1078
|
tip
|
|
1063
1079
|
});
|
|
@@ -1078,8 +1094,8 @@ const refactorNote = async (reference, strays) => {
|
|
|
1078
1094
|
if (others.length > 0 || note.trim() === "") refuse(`gtd land: refactor-note: write ${REFACTOR_NOTE} and change nothing else${others.length > 0 ? ` — revert these:\n${others.map((path) => ` - ${path}`).join("\n")}` : ""}`);
|
|
1079
1095
|
return note;
|
|
1080
1096
|
};
|
|
1081
|
-
const seamBuild = async (base) => {
|
|
1082
|
-
const { reference, tip, shape } = await decide();
|
|
1097
|
+
const seamBuild = async (base, slice$1 = {}) => {
|
|
1098
|
+
const { reference, tip, shape } = await decide(slice$1.criteria);
|
|
1083
1099
|
if (shape === void 0) return {
|
|
1084
1100
|
reference,
|
|
1085
1101
|
base,
|
|
@@ -1094,17 +1110,17 @@ const seamBuild = async (base) => {
|
|
|
1094
1110
|
return {
|
|
1095
1111
|
reference,
|
|
1096
1112
|
base,
|
|
1097
|
-
rebuild: { acceptance: naiveAcceptance(tip) }
|
|
1113
|
+
rebuild: { acceptance: naiveAcceptance(tip, slice$1.criteria) }
|
|
1098
1114
|
};
|
|
1099
1115
|
}
|
|
1100
1116
|
const strays = refactorStrays(shape);
|
|
1101
1117
|
if (strays.length === 0) return {
|
|
1102
1118
|
reference,
|
|
1103
1119
|
base,
|
|
1104
|
-
rebuild: { acceptance: naiveAcceptance(tip) }
|
|
1120
|
+
rebuild: { acceptance: naiveAcceptance(tip, slice$1.criteria) }
|
|
1105
1121
|
};
|
|
1106
1122
|
const note = await scope("seam", () => refactorNote(reference, strays));
|
|
1107
|
-
const refactored = await slice("refactor", () => reviewedRefactor(shape, {
|
|
1123
|
+
const refactored = await slice(slice$1.refactor ?? "refactor", () => reviewedRefactor(shape, {
|
|
1108
1124
|
reference,
|
|
1109
1125
|
note
|
|
1110
1126
|
}));
|
|
@@ -1114,34 +1130,143 @@ const seamBuild = async (base) => {
|
|
|
1114
1130
|
refactored,
|
|
1115
1131
|
rebuild: {
|
|
1116
1132
|
note,
|
|
1117
|
-
acceptance: naiveAcceptance(tip)
|
|
1133
|
+
acceptance: naiveAcceptance(tip, slice$1.criteria)
|
|
1118
1134
|
}
|
|
1119
1135
|
};
|
|
1120
1136
|
};
|
|
1121
1137
|
//#endregion
|
|
1138
|
+
//#region src/workflows/feature/slicing.ts
|
|
1139
|
+
const ID = /^(AC-\d+)(?![\w-])/;
|
|
1140
|
+
/** The `sliceMaxCriteria` setting. Anything but a positive integer fails rather than guess a cap. */
|
|
1141
|
+
const sliceCap = () => {
|
|
1142
|
+
const value = vars.sliceMaxCriteria ?? defaults.sliceMaxCriteria;
|
|
1143
|
+
if (!/^[1-9]\d*$/.test(value)) refuse(`gtd: sliceMaxCriteria must be a positive integer, got ${JSON.stringify(value)}`);
|
|
1144
|
+
return Number(value);
|
|
1145
|
+
};
|
|
1146
|
+
/** Each `## ` heading of `text` with the list items under it; an item not led by an ID keeps an empty one. */
|
|
1147
|
+
const slicesOf = (text) => {
|
|
1148
|
+
const slices = [];
|
|
1149
|
+
let open = false;
|
|
1150
|
+
for (const { line, fenced } of markFenced(text)) {
|
|
1151
|
+
if (fenced) continue;
|
|
1152
|
+
const heading = HEADING.exec(line);
|
|
1153
|
+
if (heading !== null) {
|
|
1154
|
+
open = heading[1] === "##";
|
|
1155
|
+
if (open) slices.push({
|
|
1156
|
+
title: heading[2],
|
|
1157
|
+
ids: []
|
|
1158
|
+
});
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
const item = ITEM.exec(line);
|
|
1162
|
+
if (item !== null && open) slices.at(-1).ids.push(ID.exec(item[1])?.[1] ?? "");
|
|
1163
|
+
}
|
|
1164
|
+
return slices;
|
|
1165
|
+
};
|
|
1166
|
+
const twice = (list) => [...new Set(list.filter((id, i) => list.indexOf(id) !== i))];
|
|
1167
|
+
/** Why `slices` fail to put each of `ids` in exactly one slice of at most `cap`, or `undefined`. */
|
|
1168
|
+
const sliceFault = (slices, ids, cap) => {
|
|
1169
|
+
const listed = slices.flatMap((s) => s.ids);
|
|
1170
|
+
if (slices.length === 0) return `no "## " slice`;
|
|
1171
|
+
const empty = slices.filter((s) => s.ids.length === 0).map((s) => s.title);
|
|
1172
|
+
if (empty.length > 0) return `a slice with no criterion: ${empty.join(", ")}`;
|
|
1173
|
+
if (listed.includes("")) return "a list item not led by a criterion ID";
|
|
1174
|
+
const over = slices.filter((s) => s.ids.length > cap).map((s) => s.title);
|
|
1175
|
+
if (over.length > 0) return `more than ${cap} criteria in: ${over.join(", ")}`;
|
|
1176
|
+
const repeated = twice(listed);
|
|
1177
|
+
if (repeated.length > 0) return `an ID in more than one slice: ${repeated.join(", ")}`;
|
|
1178
|
+
const unknown = listed.filter((id) => !ids.includes(id));
|
|
1179
|
+
if (unknown.length > 0) return `an ID ${REQUIREMENTS} does not list: ${unknown.join(", ")}`;
|
|
1180
|
+
const missing = ids.filter((id) => !listed.includes(id));
|
|
1181
|
+
if (missing.length > 0) return `no slice for: ${missing.join(", ")}`;
|
|
1182
|
+
};
|
|
1183
|
+
/** Refuse a turn whose `.gtd/SLICES.md` does not put each of `ids` in exactly one slice of at most `cap`. */
|
|
1184
|
+
const requireSlices = (ids, cap) => {
|
|
1185
|
+
const slices = slicesOf(read(".gtd/SLICES.md") ?? "");
|
|
1186
|
+
const why = sliceFault(slices, ids, cap);
|
|
1187
|
+
if (why !== void 0) refuse(`gtd land: slices: ${SLICES} has ${why} — list each slice as a "## <title>" heading over "- AC-<n>" items, every criterion of ${REQUIREMENTS} in exactly one slice, at most ${cap} per slice`);
|
|
1188
|
+
return slices;
|
|
1189
|
+
};
|
|
1190
|
+
/**
|
|
1191
|
+
* The slices to build, in order. Criteria within `sliceMaxCriteria` are one
|
|
1192
|
+
* slice and no turn; above it `design.slicing` groups them.
|
|
1193
|
+
*/
|
|
1194
|
+
const slicing = async () => {
|
|
1195
|
+
const all = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
1196
|
+
const cap = sliceCap();
|
|
1197
|
+
if (all.length <= cap) return [{
|
|
1198
|
+
title: "",
|
|
1199
|
+
ids: all
|
|
1200
|
+
}];
|
|
1201
|
+
return scope("design", async () => {
|
|
1202
|
+
await agentWithSkills("slicing", slicingPrompt(cap), {
|
|
1203
|
+
label: "Slicing the change",
|
|
1204
|
+
file: SLICES,
|
|
1205
|
+
model: planner(),
|
|
1206
|
+
system: designSystem()
|
|
1207
|
+
});
|
|
1208
|
+
return requireSlices(all, cap);
|
|
1209
|
+
});
|
|
1210
|
+
};
|
|
1211
|
+
/** Slice `i`'s criteria; `undefined` when `slices` is the one slice of an unsliced change. */
|
|
1212
|
+
const sliceCriteria = (slices, i) => slices.length === 1 ? void 0 : i < slices.length - 1 ? { only: slices[i].ids } : { except: slices.slice(0, -1).flatMap((s) => s.ids) };
|
|
1213
|
+
//#endregion
|
|
1122
1214
|
//#region src/workflows/feature/feature.ts
|
|
1123
1215
|
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
1124
1216
|
const unwind = () => {
|
|
1125
1217
|
const commit = head();
|
|
1126
1218
|
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
1127
1219
|
};
|
|
1128
|
-
/**
|
|
1220
|
+
/**
|
|
1221
|
+
* The slices `design.slicing` grouped the criteria into, each built in turn
|
|
1222
|
+
* from where the previous one landed; design must have run since `firstBase`.
|
|
1223
|
+
*/
|
|
1129
1224
|
const buildFeature = async (firstBase) => {
|
|
1130
1225
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1226
|
+
const slices = await slicing();
|
|
1227
|
+
const one = slices.length === 1;
|
|
1228
|
+
for (const i of slices.keys()) {
|
|
1229
|
+
const more = i < slices.length - 1;
|
|
1230
|
+
const criteria = sliceCriteria(slices, i);
|
|
1231
|
+
if (sliceIds(criteria)?.length === 0) {
|
|
1232
|
+
if (!more) await closeOut();
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
const n = one ? "" : `-${i + 1}`;
|
|
1236
|
+
await buildSlice(i === 0 ? firstBase : head(), {
|
|
1237
|
+
tier,
|
|
1238
|
+
criteria,
|
|
1239
|
+
refactor: `refactor${n}`,
|
|
1240
|
+
feature: `feature${n}`,
|
|
1241
|
+
more
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
/** A naive pass (judged only with `flagMode` on), then implement and the quality lap. */
|
|
1246
|
+
const buildSlice = async (sliceBase, build) => {
|
|
1247
|
+
const seam = await seamBuild(sliceBase, {
|
|
1248
|
+
criteria: build.criteria,
|
|
1249
|
+
refactor: build.refactor
|
|
1250
|
+
});
|
|
1251
|
+
await slice(build.feature, async () => {
|
|
1133
1252
|
await implement({
|
|
1134
1253
|
reference: seam.reference,
|
|
1135
|
-
tier,
|
|
1254
|
+
tier: build.tier,
|
|
1136
1255
|
afterRefactor: seam.refactored,
|
|
1137
1256
|
naive: seam.land === true ? seam.reference : void 0,
|
|
1138
|
-
rebuild: seam.rebuild
|
|
1257
|
+
rebuild: seam.rebuild,
|
|
1258
|
+
slice: build.criteria,
|
|
1259
|
+
base: build.criteria === void 0 ? void 0 : sliceBase,
|
|
1260
|
+
more: build.more
|
|
1139
1261
|
});
|
|
1140
1262
|
const carried = seam.rebuild?.acceptance?.tests ?? [];
|
|
1141
|
-
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path) && !carried.includes(c.path)) ?
|
|
1263
|
+
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path) && !carried.includes(c.path)) ? sliceBase : seam.base;
|
|
1264
|
+
const guard = build.more ? () => requireCriteriaFrozen(REQUIREMENTS) : void 0;
|
|
1142
1265
|
await tail(settleQuality, {
|
|
1143
1266
|
green: true,
|
|
1144
|
-
lensBase: base
|
|
1267
|
+
lensBase: base,
|
|
1268
|
+
guard,
|
|
1269
|
+
guardRests: false
|
|
1145
1270
|
});
|
|
1146
1271
|
});
|
|
1147
1272
|
};
|
|
@@ -1187,6 +1312,7 @@ const ordinaryStart = async () => {
|
|
|
1187
1312
|
});
|
|
1188
1313
|
refusePendingHandOff();
|
|
1189
1314
|
flagModeOn();
|
|
1315
|
+
sliceCap();
|
|
1190
1316
|
const stated = statedCriteria(changes(), head());
|
|
1191
1317
|
await unwind();
|
|
1192
1318
|
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
@@ -1202,4 +1328,4 @@ const feature = shipped(ordinaryStart);
|
|
|
1202
1328
|
//#region src/workflows/feature/index.ts
|
|
1203
1329
|
var feature_default = feature;
|
|
1204
1330
|
//#endregion
|
|
1205
|
-
export { COMMENT, REFACTOR_PACKAGE, acceptanceFault, access, afterTail, answerProductQuestions, answerReviewQuestions, buildFeature, buildTail, collecting, criteriaOf, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, holdsCallSites, implement, isTestFile, judgeSeam, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, reUnwind, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireNoNewFlags, requireNoTestFiles, requireTestCallSitesOnly, requireWithin, reviewedRefactor, seamBuild, skills, skipDesign, statedCriteria, steering, summary, triage, unwind };
|
|
1331
|
+
export { COMMENT, REFACTOR_PACKAGE, acceptanceFault, access, afterTail, answerProductQuestions, answerReviewQuestions, buildFeature, buildTail, closeOut, collecting, criteriaOf, criterionIds, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, holdsCallSites, implement, isTestFile, judgeSeam, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, reUnwind, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireNoNewFlags, requireNoTestFiles, requireSlices, requireTestCallSitesOnly, requireWithin, reviewedRefactor, seamBuild, skills, skipDesign, sliceCap, sliceCriteria, sliceIds, slicesOf, slicing, statedCriteria, steering, summary, triage, unwind };
|
package/dist/workflows/fix.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, start } from "../flows.mjs";
|
|
2
2
|
import { d as reviewNotes, f as reviewRisks } from "../chunks/steering-DVILanVf.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { K as finisherSystem, bt as envDefaults, c as baseline, i as skills, k as agentWithSkills, n as summary, q as fixReviewNotesPrompt, r as access, s as shipped, t as steering, v as REVIEW, x as coder, yt as defaults } from "../chunks/reserved-sfMdy7v7.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-Cgca5Gp-.mjs";
|
|
5
5
|
//#region src/workflows/fix/fix.ts
|
|
6
6
|
/**
|
|
7
7
|
* Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { bt as envDefaults, i as skills, lt as reviewGateBlockedMessage, n as summary, r as access, s as shipped, t as steering, u as gate, yt as defaults } from "../chunks/reserved-sfMdy7v7.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-Cgca5Gp-.mjs";
|
|
4
4
|
//#region src/workflows/review/review.ts
|
|
5
5
|
/**
|
|
6
6
|
* Pure review of everything since `reviewBase`: a green gate, the quality
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agent, changesSince, glob, head, mark, read, refuse, removeScript, run, scope, vars } from "../flows.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { Et as requireFlag, St as flagMentions, bt as envDefaults, i as skills, n as summary, o as flagShipped, p as FEEDBACK, r as access, t as steering, u as gate, wt as flipProblems, x as coder, yt as defaults } from "../chunks/reserved-sfMdy7v7.mjs";
|
|
3
3
|
//#region src/workflows/rollout/rollout.ts
|
|
4
4
|
const flippingPrompt = (name, registry) => `Roll out the release flag "${name}": in ${registry}, set \`defaultOn: true\` on its entry. Change nothing else — no other entry, no other field, no other file, no comments.`;
|
|
5
5
|
const suiteBlockedMessage = () => "The suite is red after the rollout. Fix it by hand — a rollout may only touch the flag's registry entry — then land.";
|