@pmelab/gtd 37.0.0 → 38.1.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 +15 -0
- package/dist/chunks/{reserved-zAfoJtR7.mjs → reserved-CfQ8eurM.mjs} +85 -17
- package/dist/chunks/{tail-BRC4DGdV.mjs → tail-C7yaWvaS.mjs} +1 -1
- package/dist/chunks/{wire-CSsa3Wxd.mjs → wire-BJuZWR3C.mjs} +1 -1
- package/dist/gtd.bundle.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/dist/wire.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +318 -76
- 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 +419 -109
- package/package.json +1 -1
- package/src/testkit/steps.ts +39 -0
- package/src/wire/ContextLimit.ts +1 -1
- package/src/workflows/feature/continuation-cap.feature +65 -0
- package/src/workflows/feature/continuation-rebuild.feature +88 -0
- package/src/workflows/feature/continuation-slices.feature +64 -0
- package/src/workflows/feature/continuation.feature +205 -0
- package/src/workflows/feature/coverage.ts +14 -6
- package/src/workflows/feature/criteria.test.ts +17 -1
- package/src/workflows/feature/criteria.ts +37 -3
- package/src/workflows/feature/feature.ts +64 -15
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/refactor.ts +64 -30
- package/src/workflows/feature/seam.ts +157 -48
- 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/continuation.ts +92 -0
- package/src/workflows/shared/fences.ts +5 -0
- package/src/workflows/shared/options.ts +3 -0
- package/src/workflows/shared/steps.ts +2 -0
- package/src/workflows/shared/text.test.ts +6 -2
- package/src/workflows/shared/text.ts +153 -22
- package/src/workflows/shared/vars.ts +6 -0
- package/src/workflows/shared/workflows.steps.ts +13 -0
|
@@ -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 naiveApplyFailure, A as agentWithSkills, B as coverageQuestion, C as fixCheck, Ct as testFileGlobs, D as reviewMissing, E as planner, F as buildReviewTriageMessage, G as designSystem, H as criterionLine, I as builderSystem, K as designTriagePrompt, L as clearCriteriaMessage, M as buildReviewAwaitAfterDeferMessage, N as buildReviewCollectingPrompt, P as buildReviewFixNitsPrompt, Q as implementFixPrompt, R as clearCriteriaQuestion, S as coder, St as envDefaults, T as fixRisks, U as deferNotesScript, V as coveringPrompt, W as designGateAnswerMessage, X as idleMessage, Z as implementBuildingPrompt, _ as REFACTOR_NOTE, _t as startGateBlockedMessage, a as flagModeOn, at as refactorDroppedRefusal, b as SLICES, bt as unwindFailure, ct as refactorRules, d as guarded, et as naivePassPrompt, f as healthy, ft as reviewNotesCapture, g as REFACTOR, gt as slicingPrompt, h as QUALITY, ht as seamSteerMessage, i as skills, it as refactorBuildingPrompt, j as buildReviewAnswerQuestionsPrompt, k as reviewing, lt as reviewEditNotesCapture, m as HANDOFF, mt as seamByHandMessage, n as summary, nt as oversizedMessage, ot as refactorFixPrompt, p as FEEDBACK, pt as reviewerSystem, r as access, rt as qualityUnresolvedMessage, s as shipped, st as refactorNotePrompt, t as steering, tt as normaliseCriteriaScript, u as gate, ut as reviewEditsCapture, v as REQUIREMENTS, vt as uncoveredMessage, x as awaitReview, xt as defaults, y as REVIEW, yt as unwindFailedMessage, z as coverageMessage } from "../chunks/reserved-CfQ8eurM.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, i as isOptionalObject, n as closeReview, o as tail } from "../chunks/tail-C7yaWvaS.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;
|
|
@@ -139,6 +145,13 @@ const skipDesign = (stated) => stated === void 0 ? Promise.resolve(false) : scop
|
|
|
139
145
|
});
|
|
140
146
|
return true;
|
|
141
147
|
});
|
|
148
|
+
/** After a gate that invites narrowing the requirements: an edit must leave valid criteria. */
|
|
149
|
+
const requireCriteriaIfEdited = () => {
|
|
150
|
+
const change = changes().get(REQUIREMENTS);
|
|
151
|
+
if (change === void 0) return;
|
|
152
|
+
if (change.status === "deleted") refuse(`gtd land: criteria-missing: ${REQUIREMENTS} must stay — narrow its criteria, never delete it`);
|
|
153
|
+
requireCriteria(REQUIREMENTS);
|
|
154
|
+
};
|
|
142
155
|
//#endregion
|
|
143
156
|
//#region src/workflows/feature/tier.ts
|
|
144
157
|
/** GTD_FLAG_RISK_TIER: pinned at process start, so a mid-process flip never moves a process. */
|
|
@@ -487,6 +500,53 @@ const reUnwind = async (feedback) => {
|
|
|
487
500
|
requireRevert(edited, base);
|
|
488
501
|
};
|
|
489
502
|
//#endregion
|
|
503
|
+
//#region src/workflows/shared/continuation.ts
|
|
504
|
+
const phase = (name, steered) => ({
|
|
505
|
+
name,
|
|
506
|
+
spent: false,
|
|
507
|
+
count: 0,
|
|
508
|
+
stalled: 0,
|
|
509
|
+
steered
|
|
510
|
+
});
|
|
511
|
+
const own = (fn) => fn();
|
|
512
|
+
const continued = (fn) => scope("continue", fn);
|
|
513
|
+
const afresh = (fn) => scope("fresh", fn);
|
|
514
|
+
const setting = (name, least) => {
|
|
515
|
+
const raw = vars[name] ?? defaults[name];
|
|
516
|
+
const n = Number(raw);
|
|
517
|
+
if (raw.trim() === "" || !Number.isInteger(n) || n < least) refuse(`gtd: ${name} must be a whole number of at least ${least}, got ${JSON.stringify(raw)}`);
|
|
518
|
+
return n;
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* Run `turn` until one ends without a handoff note: each stopped turn is picked
|
|
522
|
+
* up by a continuation in a fresh conversation. Past `continuations` of them in
|
|
523
|
+
* the phase, or after `continuationStall` in a row that close nothing still
|
|
524
|
+
* open, the phase rests at `oversized` for a person; landing there grants a new round.
|
|
525
|
+
*/
|
|
526
|
+
const continuing = async (phase, turn) => {
|
|
527
|
+
const cap = setting("continuations", 0);
|
|
528
|
+
const stall = setting("continuationStall", 1);
|
|
529
|
+
let last = await turn(void 0, phase.spent ? afresh : own);
|
|
530
|
+
while (last.handoff !== void 0) {
|
|
531
|
+
phase.spent = true;
|
|
532
|
+
const from = last.handoff;
|
|
533
|
+
if (phase.count >= cap || phase.stalled >= stall) {
|
|
534
|
+
await human("oversized", {
|
|
535
|
+
message: oversizedMessage(phase.name, from, phase.count >= cap ? { cap } : { stall }, phase.steered !== void 0),
|
|
536
|
+
label: "Too big for its context",
|
|
537
|
+
acceptClean: true
|
|
538
|
+
});
|
|
539
|
+
phase.steered?.();
|
|
540
|
+
phase.count = 0;
|
|
541
|
+
phase.stalled = 0;
|
|
542
|
+
}
|
|
543
|
+
last = await turn(from, continued);
|
|
544
|
+
phase.count++;
|
|
545
|
+
phase.stalled = from.open.length === 0 || from.open.some((item) => !(last.handoff?.open ?? []).includes(item)) ? 0 : phase.stalled + 1;
|
|
546
|
+
}
|
|
547
|
+
return last.result;
|
|
548
|
+
};
|
|
549
|
+
//#endregion
|
|
490
550
|
//#region src/workflows/feature/refactor.ts
|
|
491
551
|
const REFACTOR_PACKAGE = REFACTOR;
|
|
492
552
|
const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
@@ -632,7 +692,7 @@ const guardOf = ({ pattern, strays }, entry) => () => {
|
|
|
632
692
|
requireNoNewFlags(pattern, entry);
|
|
633
693
|
};
|
|
634
694
|
/** Derive, build, check and close one refactor lap; `feedback` is the collected review text of the lap before. */
|
|
635
|
-
const refactorLap = async (p, guard, feedback) => {
|
|
695
|
+
const refactorLap = async (p, guard, refactor, feedback) => {
|
|
636
696
|
const { pattern, strays, reference, note } = p;
|
|
637
697
|
await run("deriving", deriveScript([
|
|
638
698
|
...packageLines(strays, pattern, reference),
|
|
@@ -652,32 +712,48 @@ const refactorLap = async (p, guard, feedback) => {
|
|
|
652
712
|
label: "Deriving the refactor package",
|
|
653
713
|
file: REFACTOR_PACKAGE
|
|
654
714
|
});
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
guard();
|
|
668
|
-
await healthy(async () => {
|
|
669
|
-
await agentWithSkills("fix.suite.fixing", refactorFixPrompt(strays, pattern), {
|
|
670
|
-
label: "Fixing the check",
|
|
671
|
-
file: FEEDBACK,
|
|
715
|
+
const reached = /* @__PURE__ */ new Set();
|
|
716
|
+
await continuing(refactor, async (from, fresh) => {
|
|
717
|
+
await fresh(() => agentWithSkills("building", refactorBuildingPrompt({
|
|
718
|
+
pkg: REFACTOR_PACKAGE,
|
|
719
|
+
strays,
|
|
720
|
+
pattern,
|
|
721
|
+
reference,
|
|
722
|
+
feedback: feedback !== void 0,
|
|
723
|
+
note: note !== void 0,
|
|
724
|
+
from
|
|
725
|
+
}), {
|
|
726
|
+
label: from === void 0 ? "Refactoring" : "Continuing the refactor",
|
|
672
727
|
model: coder(),
|
|
673
728
|
system: builderSystem()
|
|
674
|
-
});
|
|
729
|
+
}));
|
|
730
|
+
const left = read(HANDOFF);
|
|
675
731
|
guard();
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
732
|
+
const snapshot = head();
|
|
733
|
+
for (const c of changes()) reached.add(c.path);
|
|
734
|
+
await healthy(async () => {
|
|
735
|
+
await agentWithSkills("fix.suite.fixing", refactorFixPrompt(strays, pattern), {
|
|
736
|
+
label: "Fixing the check",
|
|
737
|
+
file: FEEDBACK,
|
|
738
|
+
model: coder(),
|
|
739
|
+
system: builderSystem()
|
|
740
|
+
});
|
|
741
|
+
guard();
|
|
742
|
+
}, {
|
|
743
|
+
describeGuard: guard,
|
|
744
|
+
rules: refactorRules(strays, pattern)
|
|
745
|
+
});
|
|
746
|
+
if (left === void 0) return { result: void 0 };
|
|
747
|
+
return {
|
|
748
|
+
result: void 0,
|
|
749
|
+
handoff: {
|
|
750
|
+
snapshot,
|
|
751
|
+
note: left.trim(),
|
|
752
|
+
open: strays.filter((path) => !reached.has(path))
|
|
753
|
+
}
|
|
754
|
+
};
|
|
679
755
|
});
|
|
680
|
-
await run("closing", removeScript([REFACTOR_PACKAGE]), { label: "Closing out the refactor" });
|
|
756
|
+
await run("closing", removeScript([REFACTOR_PACKAGE, HANDOFF]), { label: "Closing out the refactor" });
|
|
681
757
|
};
|
|
682
758
|
const strayAccess = (strays) => ({ write: [
|
|
683
759
|
...strays,
|
|
@@ -691,7 +767,7 @@ const refactorFirst = async (shape, options = {}) => {
|
|
|
691
767
|
name: "refactor",
|
|
692
768
|
access: strayAccess(p.strays)
|
|
693
769
|
}, async () => {
|
|
694
|
-
await refactorLap(p, guardOf(p, head()));
|
|
770
|
+
await refactorLap(p, guardOf(p, head()), phase("refactor"));
|
|
695
771
|
return true;
|
|
696
772
|
});
|
|
697
773
|
};
|
|
@@ -716,9 +792,10 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
716
792
|
unmatched: "gtd land: refactor-feedback: the review feedback changed files but wrote no .gtd/REFACTOR.md — a refactor cannot act on it; revert the change, or `gtd abandon` and restart",
|
|
717
793
|
dropped: refactorDroppedRefusal
|
|
718
794
|
};
|
|
795
|
+
const refactor = phase("refactor");
|
|
719
796
|
let collected;
|
|
720
797
|
for (;;) {
|
|
721
|
-
await refactorLap(p, guard, collected);
|
|
798
|
+
await refactorLap(p, guard, refactor, collected);
|
|
722
799
|
const outcome = await buildTail(entry, {
|
|
723
800
|
green: true,
|
|
724
801
|
guard,
|
|
@@ -759,10 +836,12 @@ const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", imple
|
|
|
759
836
|
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
837
|
/**
|
|
761
838
|
* The criterion IDs of `.gtd/REQUIREMENTS.md` — read afresh, never carried
|
|
762
|
-
* over — no passing test since `base` covers, by the judge.
|
|
839
|
+
* over, only `slice`'s when given — no passing test since `base` covers, by the judge.
|
|
763
840
|
*/
|
|
764
|
-
const uncoveredSince = async (base) => {
|
|
765
|
-
const
|
|
841
|
+
const uncoveredSince = async (base, slice) => {
|
|
842
|
+
const requirements = read(".gtd/REQUIREMENTS.md") ?? "";
|
|
843
|
+
const own = criterionIds(requirements, slice);
|
|
844
|
+
const criteria = (criteriaOf(requirements) ?? []).filter((c) => own.includes(c.id));
|
|
766
845
|
if (criteria.length === 0) return {
|
|
767
846
|
covered: [],
|
|
768
847
|
uncovered: []
|
|
@@ -789,14 +868,14 @@ const record = (covered, uncovered = []) => {
|
|
|
789
868
|
* sending uncovered IDs back to a covering turn; marks the IDs covered on the
|
|
790
869
|
* landing that ends it. A human may overrule once the retries are spent.
|
|
791
870
|
*/
|
|
792
|
-
const requireCoverage = async (base = start()) => {
|
|
871
|
+
const requireCoverage = async (base = start(), slice) => {
|
|
793
872
|
let retries = 0;
|
|
794
873
|
for (;;) {
|
|
795
|
-
const { covered, uncovered } = await uncoveredSince(base);
|
|
874
|
+
const { covered, uncovered } = await uncoveredSince(base, slice);
|
|
796
875
|
if (uncovered.length === 0) return record(covered);
|
|
797
876
|
if (retries < COVERING_RETRIES) {
|
|
798
877
|
retries++;
|
|
799
|
-
await agentWithSkills("covering", coveringPrompt(uncovered), {
|
|
878
|
+
await agentWithSkills("covering", coveringPrompt(uncovered, sliceIds(slice)), {
|
|
800
879
|
label: "Covering the acceptance criteria",
|
|
801
880
|
model: coder(),
|
|
802
881
|
system: builderSystem(),
|
|
@@ -827,12 +906,32 @@ const judgeSeam = (tip) => judgeDiffShape("judge", {
|
|
|
827
906
|
allow: (vars.seamAllow ?? defaults.seamAllow).split(",").map((glob) => glob.trim()).filter((glob) => glob !== ""),
|
|
828
907
|
exempt: tip.changes.filter((c) => c.status === "added").map((c) => c.path)
|
|
829
908
|
});
|
|
830
|
-
const naivePass = (retry) => agentWithSkills("naive", naivePassPrompt(retry), {
|
|
831
|
-
label: retry === void 0 ? "Writing the naive pass" : "Rewriting the naive pass",
|
|
909
|
+
const naivePass = (retry, slice, from) => agentWithSkills("naive", naivePassPrompt(retry, sliceIds(slice), from), {
|
|
910
|
+
label: from !== void 0 ? "Continuing the naive pass" : retry === void 0 ? "Writing the naive pass" : "Rewriting the naive pass",
|
|
832
911
|
keep: true,
|
|
833
912
|
model: coder(),
|
|
834
913
|
system: builderSystem()
|
|
835
914
|
});
|
|
915
|
+
const handoffOf = (tip, slice) => {
|
|
916
|
+
const note = tip.changes.find((c) => c.path === ".gtd/HANDOFF.md" && c.after !== void 0);
|
|
917
|
+
if (note === void 0) return void 0;
|
|
918
|
+
return {
|
|
919
|
+
snapshot: tip.hash,
|
|
920
|
+
note: note.after.trim(),
|
|
921
|
+
open: unnamed(idsOf(REQUIREMENTS, slice), acceptanceTests(tip.changes))
|
|
922
|
+
};
|
|
923
|
+
};
|
|
924
|
+
/** A naive pass, continued until a pass ends without a handoff note: the snapshot is its kept tip. */
|
|
925
|
+
const naiveContinued = (retry, slice, naive) => continuing(naive, async (from, fresh) => {
|
|
926
|
+
const tip = await fresh(async () => {
|
|
927
|
+
await naivePass(retry, slice, from);
|
|
928
|
+
return kept("naive");
|
|
929
|
+
});
|
|
930
|
+
return {
|
|
931
|
+
result: tip,
|
|
932
|
+
handoff: handoffOf(tip, slice)
|
|
933
|
+
};
|
|
934
|
+
});
|
|
836
935
|
const IMPORTED = /(?:\bfrom|\bimport|\brequire\s*\(|\bmock\s*\()\s*\(?\s*(["'`])([^"'`\n]+)\1/g;
|
|
837
936
|
const EXTENSION = /\.[\w-]+$/;
|
|
838
937
|
const ALIAS = /^(?:[@~]\/|#)/;
|
|
@@ -868,7 +967,7 @@ const addedLines = (change) => {
|
|
|
868
967
|
return change.after.split("\n").filter((line) => !before.has(line)).join("\n");
|
|
869
968
|
};
|
|
870
969
|
const acceptanceTests = (changes) => changes.filter((c) => c.after !== void 0 && isCode$1(c.path) && isTestFile(c.path));
|
|
871
|
-
const idsOf = (file) => (
|
|
970
|
+
const idsOf = (file, slice) => criterionIds(read(file) ?? "", slice);
|
|
872
971
|
const unnamed = (ids, tests) => {
|
|
873
972
|
const lines = tests.map(addedLines);
|
|
874
973
|
return ids.filter((id) => !lines.some((text) => named(id, text)));
|
|
@@ -878,9 +977,9 @@ const unnamed = (ids, tests) => {
|
|
|
878
977
|
* test line of the pass names, or a test importing a file the pass added —
|
|
879
978
|
* naive structure a reset and refactor replaces, which the tests must survive.
|
|
880
979
|
*/
|
|
881
|
-
const acceptanceFault = (file, tip) => {
|
|
980
|
+
const acceptanceFault = (file, tip, slice) => {
|
|
882
981
|
const tests = acceptanceTests(tip.changes);
|
|
883
|
-
const untested = unnamed(idsOf(file), tests);
|
|
982
|
+
const untested = unnamed(idsOf(file, slice), tests);
|
|
884
983
|
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
984
|
const added = tip.changes.filter((c) => c.status === "added" && isCode$1(c.path) && !isTestFile(c.path)).map((c) => c.path);
|
|
886
985
|
const reaching = tests.filter((test) => holdsCallSites(test.path)).flatMap((test) => {
|
|
@@ -890,8 +989,8 @@ const acceptanceFault = (file, tip) => {
|
|
|
890
989
|
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
990
|
};
|
|
892
991
|
/** 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);
|
|
992
|
+
const naiveAcceptance = (tip, slice) => {
|
|
993
|
+
const ids = idsOf(REQUIREMENTS, slice);
|
|
895
994
|
const perTest = acceptanceTests(tip.changes).map((c) => {
|
|
896
995
|
const lines = addedLines(c);
|
|
897
996
|
return {
|
|
@@ -960,6 +1059,32 @@ const applied = async (tip) => {
|
|
|
960
1059
|
await run("applying", applyScript(tip, FEEDBACK, naiveApplyFailure(tip), [".gtd"]), { label: "Applying the naive pass" });
|
|
961
1060
|
return !wrote(".gtd/FEEDBACK.md") && codeChanges().length > 0;
|
|
962
1061
|
};
|
|
1062
|
+
/** `carried` limited to the criteria `.gtd/REQUIREMENTS.md` lists now; a file listing none narrows nothing. */
|
|
1063
|
+
const settled = (carried, slice) => {
|
|
1064
|
+
const listed = idsOf(REQUIREMENTS, slice);
|
|
1065
|
+
if (carried === void 0) return listed;
|
|
1066
|
+
return listed.length === 0 ? carried : carried.filter((id) => listed.includes(id));
|
|
1067
|
+
};
|
|
1068
|
+
const build = (options) => continuing(phase("build", requireCriteriaIfEdited), async (from, fresh) => {
|
|
1069
|
+
await fresh(() => agentWithSkills("building", implementBuildingPrompt(options.reference, options.tier, options.amend, options.afterRefactor, flagModeOn(), options.rebuild, sliceIds(options.slice), from), {
|
|
1070
|
+
label: from === void 0 ? "Implementing" : "Continuing the implementation",
|
|
1071
|
+
model: coder(),
|
|
1072
|
+
system: builderSystem(),
|
|
1073
|
+
...options.afterRefactor === true ? { allowEmpty: true } : {}
|
|
1074
|
+
}));
|
|
1075
|
+
requireCriteriaFrozen(REQUIREMENTS);
|
|
1076
|
+
const note = read(HANDOFF);
|
|
1077
|
+
if (note === void 0) return { result: void 0 };
|
|
1078
|
+
const ids = settled(options.rebuild?.acceptance?.ids, options.slice);
|
|
1079
|
+
return {
|
|
1080
|
+
result: void 0,
|
|
1081
|
+
handoff: {
|
|
1082
|
+
snapshot: head(),
|
|
1083
|
+
note: note.trim(),
|
|
1084
|
+
open: unnamed(ids, acceptanceTests(changesSince(start())))
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
});
|
|
963
1088
|
function implement(first, tier, amend) {
|
|
964
1089
|
const options = typeof first === "object" && first !== null ? first : {
|
|
965
1090
|
reference: first,
|
|
@@ -970,19 +1095,26 @@ function implement(first, tier, amend) {
|
|
|
970
1095
|
const since = head();
|
|
971
1096
|
const acceptance = options.rebuild?.acceptance;
|
|
972
1097
|
const built = options.naive === void 0 || !await applied(options.naive);
|
|
973
|
-
if (built) await
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
});
|
|
979
|
-
requireCriteriaFrozen(REQUIREMENTS);
|
|
980
|
-
if (built && acceptance !== void 0) requireAcceptance(acceptance, since);
|
|
1098
|
+
if (built) await build(options);
|
|
1099
|
+
else requireCriteriaFrozen(REQUIREMENTS);
|
|
1100
|
+
if (built && acceptance !== void 0) requireAcceptance({
|
|
1101
|
+
...acceptance,
|
|
1102
|
+
ids: settled(acceptance.ids, options.slice)
|
|
1103
|
+
}, since);
|
|
981
1104
|
await green(built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0);
|
|
982
|
-
await requireCoverage();
|
|
983
|
-
await
|
|
1105
|
+
await requireCoverage(options.base, options.slice);
|
|
1106
|
+
await closing(options.more === true);
|
|
984
1107
|
});
|
|
985
1108
|
}
|
|
1109
|
+
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
1110
|
+
const closeOut = () => scope("implement", () => closing(false));
|
|
1111
|
+
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
1112
|
+
const closing = (more) => run("closing", removeScript(more ? [REFACTOR_NOTE, HANDOFF] : [
|
|
1113
|
+
REQUIREMENTS,
|
|
1114
|
+
REFACTOR_NOTE,
|
|
1115
|
+
SLICES,
|
|
1116
|
+
HANDOFF
|
|
1117
|
+
]), { label: "Closing out the implementation" });
|
|
986
1118
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
987
1119
|
const steer = async (retry) => {
|
|
988
1120
|
await human("steer", {
|
|
@@ -1013,8 +1145,8 @@ const budget = () => {
|
|
|
1013
1145
|
};
|
|
1014
1146
|
};
|
|
1015
1147
|
/** 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);
|
|
1148
|
+
const vetted = async (tip, vetting, slice) => {
|
|
1149
|
+
const fault = vetting.waived ? void 0 : acceptanceFault(REQUIREMENTS, tip, slice);
|
|
1018
1150
|
if (fault !== void 0) {
|
|
1019
1151
|
const verdict = await vetting.again({
|
|
1020
1152
|
fault,
|
|
@@ -1048,16 +1180,16 @@ const ruled = async (tip, again) => {
|
|
|
1048
1180
|
tip
|
|
1049
1181
|
} : next;
|
|
1050
1182
|
};
|
|
1051
|
-
const decide = () => scope("seam", async () => {
|
|
1183
|
+
const decide = (slice) => scope("seam", async () => {
|
|
1052
1184
|
const vetting = {
|
|
1053
1185
|
again: budget(),
|
|
1054
1186
|
waived: false
|
|
1055
1187
|
};
|
|
1188
|
+
const naive = phase("naive pass", requireCriteriaIfEdited);
|
|
1056
1189
|
let retry;
|
|
1057
1190
|
for (;;) {
|
|
1058
|
-
await
|
|
1059
|
-
const
|
|
1060
|
-
const outcome = await vetted(tip, vetting) ?? (flagModeOn() ? await ruled(tip, vetting.again) : {
|
|
1191
|
+
const tip = await naiveContinued(retry, slice, naive);
|
|
1192
|
+
const outcome = await vetted(tip, vetting, slice) ?? (flagModeOn() ? await ruled(tip, vetting.again) : {
|
|
1061
1193
|
reference: tip.hash,
|
|
1062
1194
|
tip
|
|
1063
1195
|
});
|
|
@@ -1078,8 +1210,8 @@ const refactorNote = async (reference, strays) => {
|
|
|
1078
1210
|
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
1211
|
return note;
|
|
1080
1212
|
};
|
|
1081
|
-
const seamBuild = async (base) => {
|
|
1082
|
-
const { reference, tip, shape } = await decide();
|
|
1213
|
+
const seamBuild = async (base, slice$1 = {}) => {
|
|
1214
|
+
const { reference, tip, shape } = await decide(slice$1.criteria);
|
|
1083
1215
|
if (shape === void 0) return {
|
|
1084
1216
|
reference,
|
|
1085
1217
|
base,
|
|
@@ -1094,17 +1226,17 @@ const seamBuild = async (base) => {
|
|
|
1094
1226
|
return {
|
|
1095
1227
|
reference,
|
|
1096
1228
|
base,
|
|
1097
|
-
rebuild: { acceptance: naiveAcceptance(tip) }
|
|
1229
|
+
rebuild: { acceptance: naiveAcceptance(tip, slice$1.criteria) }
|
|
1098
1230
|
};
|
|
1099
1231
|
}
|
|
1100
1232
|
const strays = refactorStrays(shape);
|
|
1101
1233
|
if (strays.length === 0) return {
|
|
1102
1234
|
reference,
|
|
1103
1235
|
base,
|
|
1104
|
-
rebuild: { acceptance: naiveAcceptance(tip) }
|
|
1236
|
+
rebuild: { acceptance: naiveAcceptance(tip, slice$1.criteria) }
|
|
1105
1237
|
};
|
|
1106
1238
|
const note = await scope("seam", () => refactorNote(reference, strays));
|
|
1107
|
-
const refactored = await slice("refactor", () => reviewedRefactor(shape, {
|
|
1239
|
+
const refactored = await slice(slice$1.refactor ?? "refactor", () => reviewedRefactor(shape, {
|
|
1108
1240
|
reference,
|
|
1109
1241
|
note
|
|
1110
1242
|
}));
|
|
@@ -1114,34 +1246,143 @@ const seamBuild = async (base) => {
|
|
|
1114
1246
|
refactored,
|
|
1115
1247
|
rebuild: {
|
|
1116
1248
|
note,
|
|
1117
|
-
acceptance: naiveAcceptance(tip)
|
|
1249
|
+
acceptance: naiveAcceptance(tip, slice$1.criteria)
|
|
1118
1250
|
}
|
|
1119
1251
|
};
|
|
1120
1252
|
};
|
|
1121
1253
|
//#endregion
|
|
1254
|
+
//#region src/workflows/feature/slicing.ts
|
|
1255
|
+
const ID = /^(AC-\d+)(?![\w-])/;
|
|
1256
|
+
/** The `sliceMaxCriteria` setting. Anything but a positive integer fails rather than guess a cap. */
|
|
1257
|
+
const sliceCap = () => {
|
|
1258
|
+
const value = vars.sliceMaxCriteria ?? defaults.sliceMaxCriteria;
|
|
1259
|
+
if (!/^[1-9]\d*$/.test(value)) refuse(`gtd: sliceMaxCriteria must be a positive integer, got ${JSON.stringify(value)}`);
|
|
1260
|
+
return Number(value);
|
|
1261
|
+
};
|
|
1262
|
+
/** Each `## ` heading of `text` with the list items under it; an item not led by an ID keeps an empty one. */
|
|
1263
|
+
const slicesOf = (text) => {
|
|
1264
|
+
const slices = [];
|
|
1265
|
+
let open = false;
|
|
1266
|
+
for (const { line, fenced } of markFenced(text)) {
|
|
1267
|
+
if (fenced) continue;
|
|
1268
|
+
const heading = HEADING.exec(line);
|
|
1269
|
+
if (heading !== null) {
|
|
1270
|
+
open = heading[1] === "##";
|
|
1271
|
+
if (open) slices.push({
|
|
1272
|
+
title: heading[2],
|
|
1273
|
+
ids: []
|
|
1274
|
+
});
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
const item = ITEM.exec(line);
|
|
1278
|
+
if (item !== null && open) slices.at(-1).ids.push(ID.exec(item[1])?.[1] ?? "");
|
|
1279
|
+
}
|
|
1280
|
+
return slices;
|
|
1281
|
+
};
|
|
1282
|
+
const twice = (list) => [...new Set(list.filter((id, i) => list.indexOf(id) !== i))];
|
|
1283
|
+
/** Why `slices` fail to put each of `ids` in exactly one slice of at most `cap`, or `undefined`. */
|
|
1284
|
+
const sliceFault = (slices, ids, cap) => {
|
|
1285
|
+
const listed = slices.flatMap((s) => s.ids);
|
|
1286
|
+
if (slices.length === 0) return `no "## " slice`;
|
|
1287
|
+
const empty = slices.filter((s) => s.ids.length === 0).map((s) => s.title);
|
|
1288
|
+
if (empty.length > 0) return `a slice with no criterion: ${empty.join(", ")}`;
|
|
1289
|
+
if (listed.includes("")) return "a list item not led by a criterion ID";
|
|
1290
|
+
const over = slices.filter((s) => s.ids.length > cap).map((s) => s.title);
|
|
1291
|
+
if (over.length > 0) return `more than ${cap} criteria in: ${over.join(", ")}`;
|
|
1292
|
+
const repeated = twice(listed);
|
|
1293
|
+
if (repeated.length > 0) return `an ID in more than one slice: ${repeated.join(", ")}`;
|
|
1294
|
+
const unknown = listed.filter((id) => !ids.includes(id));
|
|
1295
|
+
if (unknown.length > 0) return `an ID ${REQUIREMENTS} does not list: ${unknown.join(", ")}`;
|
|
1296
|
+
const missing = ids.filter((id) => !listed.includes(id));
|
|
1297
|
+
if (missing.length > 0) return `no slice for: ${missing.join(", ")}`;
|
|
1298
|
+
};
|
|
1299
|
+
/** Refuse a turn whose `.gtd/SLICES.md` does not put each of `ids` in exactly one slice of at most `cap`. */
|
|
1300
|
+
const requireSlices = (ids, cap) => {
|
|
1301
|
+
const slices = slicesOf(read(".gtd/SLICES.md") ?? "");
|
|
1302
|
+
const why = sliceFault(slices, ids, cap);
|
|
1303
|
+
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`);
|
|
1304
|
+
return slices;
|
|
1305
|
+
};
|
|
1306
|
+
/**
|
|
1307
|
+
* The slices to build, in order. Criteria within `sliceMaxCriteria` are one
|
|
1308
|
+
* slice and no turn; above it `design.slicing` groups them.
|
|
1309
|
+
*/
|
|
1310
|
+
const slicing = async () => {
|
|
1311
|
+
const all = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
1312
|
+
const cap = sliceCap();
|
|
1313
|
+
if (all.length <= cap) return [{
|
|
1314
|
+
title: "",
|
|
1315
|
+
ids: all
|
|
1316
|
+
}];
|
|
1317
|
+
return scope("design", async () => {
|
|
1318
|
+
await agentWithSkills("slicing", slicingPrompt(cap), {
|
|
1319
|
+
label: "Slicing the change",
|
|
1320
|
+
file: SLICES,
|
|
1321
|
+
model: planner(),
|
|
1322
|
+
system: designSystem()
|
|
1323
|
+
});
|
|
1324
|
+
return requireSlices(all, cap);
|
|
1325
|
+
});
|
|
1326
|
+
};
|
|
1327
|
+
/** Slice `i`'s criteria; `undefined` when `slices` is the one slice of an unsliced change. */
|
|
1328
|
+
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) };
|
|
1329
|
+
//#endregion
|
|
1122
1330
|
//#region src/workflows/feature/feature.ts
|
|
1123
1331
|
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
1124
1332
|
const unwind = () => {
|
|
1125
1333
|
const commit = head();
|
|
1126
1334
|
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
1127
1335
|
};
|
|
1128
|
-
/**
|
|
1336
|
+
/**
|
|
1337
|
+
* The slices `design.slicing` grouped the criteria into, each built in turn
|
|
1338
|
+
* from where the previous one landed; design must have run since `firstBase`.
|
|
1339
|
+
*/
|
|
1129
1340
|
const buildFeature = async (firstBase) => {
|
|
1130
1341
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1342
|
+
const slices = await slicing();
|
|
1343
|
+
const one = slices.length === 1;
|
|
1344
|
+
for (const i of slices.keys()) {
|
|
1345
|
+
const more = i < slices.length - 1;
|
|
1346
|
+
const criteria = sliceCriteria(slices, i);
|
|
1347
|
+
if (sliceIds(criteria)?.length === 0) {
|
|
1348
|
+
if (!more) await closeOut();
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
const n = one ? "" : `-${i + 1}`;
|
|
1352
|
+
await buildSlice(i === 0 ? firstBase : head(), {
|
|
1353
|
+
tier,
|
|
1354
|
+
criteria,
|
|
1355
|
+
refactor: `refactor${n}`,
|
|
1356
|
+
feature: `feature${n}`,
|
|
1357
|
+
more
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
/** A naive pass (judged only with `flagMode` on), then implement and the quality lap. */
|
|
1362
|
+
const buildSlice = async (sliceBase, build) => {
|
|
1363
|
+
const seam = await seamBuild(sliceBase, {
|
|
1364
|
+
criteria: build.criteria,
|
|
1365
|
+
refactor: build.refactor
|
|
1366
|
+
});
|
|
1367
|
+
await slice(build.feature, async () => {
|
|
1133
1368
|
await implement({
|
|
1134
1369
|
reference: seam.reference,
|
|
1135
|
-
tier,
|
|
1370
|
+
tier: build.tier,
|
|
1136
1371
|
afterRefactor: seam.refactored,
|
|
1137
1372
|
naive: seam.land === true ? seam.reference : void 0,
|
|
1138
|
-
rebuild: seam.rebuild
|
|
1373
|
+
rebuild: seam.rebuild,
|
|
1374
|
+
slice: build.criteria,
|
|
1375
|
+
base: build.criteria === void 0 ? void 0 : sliceBase,
|
|
1376
|
+
more: build.more
|
|
1139
1377
|
});
|
|
1140
1378
|
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)) ?
|
|
1379
|
+
const base = seam.refactored === true && !changesSince(seam.base).some((c) => isCode$1(c.path) && !carried.includes(c.path)) ? sliceBase : seam.base;
|
|
1380
|
+
const guard = build.more ? () => requireCriteriaFrozen(REQUIREMENTS) : void 0;
|
|
1142
1381
|
await tail(settleQuality, {
|
|
1143
1382
|
green: true,
|
|
1144
|
-
lensBase: base
|
|
1383
|
+
lensBase: base,
|
|
1384
|
+
guard,
|
|
1385
|
+
guardRests: false
|
|
1145
1386
|
});
|
|
1146
1387
|
});
|
|
1147
1388
|
};
|
|
@@ -1187,6 +1428,7 @@ const ordinaryStart = async () => {
|
|
|
1187
1428
|
});
|
|
1188
1429
|
refusePendingHandOff();
|
|
1189
1430
|
flagModeOn();
|
|
1431
|
+
sliceCap();
|
|
1190
1432
|
const stated = statedCriteria(changes(), head());
|
|
1191
1433
|
await unwind();
|
|
1192
1434
|
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
@@ -1202,4 +1444,4 @@ const feature = shipped(ordinaryStart);
|
|
|
1202
1444
|
//#region src/workflows/feature/index.ts
|
|
1203
1445
|
var feature_default = feature;
|
|
1204
1446
|
//#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 };
|
|
1447
|
+
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, requireCriteriaIfEdited, 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 { A as agentWithSkills, J as fixReviewNotesPrompt, S as coder, St as envDefaults, c as baseline, i as skills, n as summary, q as finisherSystem, r as access, s as shipped, t as steering, xt as defaults, y as REVIEW } from "../chunks/reserved-CfQ8eurM.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-C7yaWvaS.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 { St as envDefaults, dt as reviewGateBlockedMessage, i as skills, n as summary, r as access, s as shipped, t as steering, u as gate, xt as defaults } from "../chunks/reserved-CfQ8eurM.mjs";
|
|
3
|
+
import { a as refusePendingHandOff, r as handOffTail } from "../chunks/tail-C7yaWvaS.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 flipProblems, Ot as requireFlag, S as coder, St 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 flagMentions, xt as defaults } from "../chunks/reserved-CfQ8eurM.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.";
|