@pmelab/gtd 50.0.0 → 51.0.1
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 +32 -24
- package/dist/chunks/{reserved-CdOzKFVX.mjs → reserved-CUkk9XgL.mjs} +93 -4
- package/dist/chunks/{review-BChXEl2Y.mjs → review-BaCXlIT2.mjs} +2 -2
- package/dist/chunks/{tail-DRM2eC1z.mjs → tail-CX6Pdsql.mjs} +1 -1
- package/dist/gtd.bundle.mjs +26 -25
- package/dist/testkit.mjs +26 -25
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +144 -11
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +234 -12
- package/package.json +1 -1
- package/src/edge/Edge.ts +1 -30
- package/src/replay/Replay.test.ts +4 -8
- package/src/replay/Replay.ts +35 -3
- package/src/replay/index.ts +7 -1
- 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 +1 -0
- 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 +1 -0
- package/src/workflows/feature/review-gate.feature +1 -0
- package/src/workflows/feature/reviewer-walkthrough.feature +1 -0
- 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 +1 -0
- package/src/workflows/feature/severity-sorted-review.feature +1 -0
- package/src/workflows/feature/slice-findings.feature +1 -0
- package/src/workflows/feature/slicing-cap.feature +9 -0
- package/src/workflows/feature/slicing.feature +1 -0
- package/src/workflows/feature/standards.feature +1 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/prose.ts +6 -0
- 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.ts +104 -3
- package/src/workflows/shared/vars.ts +3 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { agent, answered, applyScript, changes, changesSince, check, codeChanges, env, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, matches, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireThreadsClosed, revertScript, run, scope, slice, start, vars, wrote } from "../flows.mjs";
|
|
2
2
|
import { lensEntriesOf } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as refusePendingHandOff, c as joinReports, o as tail } from "../chunks/tail-
|
|
5
|
-
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-
|
|
3
|
+
import { $ as implementFixPrompt, C as coder, Ct as seamByHandMessage, Dt as testerSystem, E as fixNits, Et as startGateBlockedMessage, G as designGateAnswerMessage, H as coverageQuestion, I as builderSystem, It as defaults, K as designSystem, Lt as envDefaults, Mt as unwindFailure, N as agentWithSkills, P as buildReviewCollectingPrompt, Q as implementBuildingPrompt, R as clearCriteriaMessage, Rt as testFileGlobs, St as reviewerSystem, Tt as slicingPrompt, U as coveringPrompt, V as coverageMessage, W as criterionLine, Z as idleMessage, _ as REFACTOR_NOTE, a as flagModeOn, at as prototypeOpenMessage, b as SLICES, d as healthy, dt as refactorBuildingPrompt, et as naiveApplyFailure, f as FEEDBACK, ft as refactorDroppedRefusal, g as REFACTOR, ht as refactorRules, i as skills, it as prototypeFixingPrompt, jt as unwindFailedMessage, k as planner, kt as uncoveredMessage, l as gate, m as PROTOTYPE, mt as refactorNotePrompt, n as summary, nt as normaliseCriteriaScript, ot as prototypeTestingPrompt, p as HANDOFF, pt as refactorFixPrompt, q as designTriagePrompt, r as access, rt as oversizedMessage, s as shipped, st as prototypeUnguidedMessage, t as steering, tt as naivePassPrompt, v as REQUIREMENTS, w as collecting, wt as seamSteerMessage, x as answerReviewQuestions, z as clearCriteriaQuestion } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, c as joinReports, o as tail } from "../chunks/tail-CX6Pdsql.mjs";
|
|
5
|
+
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-BaCXlIT2.mjs";
|
|
6
6
|
import { posix } from "node:path";
|
|
7
7
|
//#region src/workflows/shared/fences.ts
|
|
8
8
|
const markFenced = (text) => {
|
|
@@ -561,7 +561,9 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
561
561
|
feedback,
|
|
562
562
|
fixAccess: strayAccess(p.strays),
|
|
563
563
|
lensBase: entry,
|
|
564
|
-
rules: refactorRules(p.strays, p.pattern)
|
|
564
|
+
rules: refactorRules(p.strays, p.pattern),
|
|
565
|
+
walkthrough: true,
|
|
566
|
+
lap: "refactor"
|
|
565
567
|
});
|
|
566
568
|
if (outcome.verdict === "signoff") return true;
|
|
567
569
|
await reUnwind(outcome);
|
|
@@ -576,7 +578,7 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
576
578
|
const COVERED_MIN_P = .7;
|
|
577
579
|
/** Covering turns before the process rests at `uncovered`; a human edit there refills them. */
|
|
578
580
|
const COVERING_RETRIES = 2;
|
|
579
|
-
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
581
|
+
const frozen$1 = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
580
582
|
const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
581
583
|
label: "Fixing the check",
|
|
582
584
|
file: FEEDBACK,
|
|
@@ -584,7 +586,7 @@ const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", imple
|
|
|
584
586
|
system: builderSystem()
|
|
585
587
|
}), {
|
|
586
588
|
guard: () => {
|
|
587
|
-
frozen();
|
|
589
|
+
frozen$1();
|
|
588
590
|
guard?.();
|
|
589
591
|
},
|
|
590
592
|
guardRests: false
|
|
@@ -637,7 +639,7 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
637
639
|
system: builderSystem(),
|
|
638
640
|
allowEmpty: true
|
|
639
641
|
});
|
|
640
|
-
frozen();
|
|
642
|
+
frozen$1();
|
|
641
643
|
} else {
|
|
642
644
|
await human("uncovered", {
|
|
643
645
|
message: uncoveredMessage(uncovered),
|
|
@@ -646,13 +648,136 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
646
648
|
acceptClean: true
|
|
647
649
|
});
|
|
648
650
|
if (!changes().some((c) => isCode$1(c.path))) return record(covered, uncovered);
|
|
649
|
-
frozen();
|
|
651
|
+
frozen$1();
|
|
650
652
|
retries = 0;
|
|
651
653
|
}
|
|
652
654
|
await green();
|
|
653
655
|
}
|
|
654
656
|
};
|
|
655
657
|
//#endregion
|
|
658
|
+
//#region src/workflows/feature/prototype.ts
|
|
659
|
+
/** The `prototypeRounds` setting. Anything but a non-negative integer fails rather than guess a cap. */
|
|
660
|
+
const prototypeRounds = () => {
|
|
661
|
+
const value = vars.prototypeRounds ?? defaults.prototypeRounds;
|
|
662
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) refuse(`gtd: prototypeRounds must be a non-negative integer, got ${JSON.stringify(value)}`);
|
|
663
|
+
return Number(value);
|
|
664
|
+
};
|
|
665
|
+
const CHUNK = /^(AC-\d+|Problem\b.*?)\s*:\s*(pass|fail|blocked)$/i;
|
|
666
|
+
const UNGUIDED = /^no testing instructions$/i;
|
|
667
|
+
/** The `## ` chunks of a prototype report; a heading in neither shape is prose, not an item. */
|
|
668
|
+
const reportOf = (text) => {
|
|
669
|
+
let unguided = false;
|
|
670
|
+
const items = [];
|
|
671
|
+
const malformed = [];
|
|
672
|
+
for (const { line, fenced } of markFenced(text)) {
|
|
673
|
+
const heading = fenced ? null : HEADING.exec(line);
|
|
674
|
+
if (heading?.[1] !== "##") continue;
|
|
675
|
+
if (UNGUIDED.test(heading[2])) unguided = true;
|
|
676
|
+
const chunk = CHUNK.exec(heading[2]);
|
|
677
|
+
if (chunk === null && /^(ac-\d|problem)/i.test(heading[2])) malformed.push(heading[2]);
|
|
678
|
+
if (chunk !== null) items.push({
|
|
679
|
+
subject: /^ac-/i.test(chunk[1]) ? chunk[1].toUpperCase() : chunk[1],
|
|
680
|
+
verdict: chunk[2].toLowerCase()
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
unguided,
|
|
685
|
+
items,
|
|
686
|
+
malformed
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
/**
|
|
690
|
+
* The report the testing turn left, refused unless it is the only change and
|
|
691
|
+
* names every criterion once, with its open items: another slice's criteria
|
|
692
|
+
* are not this slice's to fix.
|
|
693
|
+
*/
|
|
694
|
+
const requireReport = (slice, retest) => {
|
|
695
|
+
const others = changes().map((c) => c.path).filter((path) => path !== PROTOTYPE);
|
|
696
|
+
if (others.length > 0) refuse(`gtd land: prototype-report: test in a disposable place and change nothing but ${PROTOTYPE} — revert these:\n${others.map((path) => ` - ${path}`).join("\n")}`);
|
|
697
|
+
const text = read(PROTOTYPE);
|
|
698
|
+
if (text === void 0) return refuse(`gtd land: prototype-report: write ${PROTOTYPE}`);
|
|
699
|
+
const report = reportOf(text);
|
|
700
|
+
if (report.malformed.length > 0) refuse(`gtd land: prototype-report: head each chunk \`## <ID>: pass|fail|blocked\` or \`## Problem <title>: pass|fail|blocked\` — these miss it:\n${report.malformed.map((h) => ` - ${h}`).join("\n")}`);
|
|
701
|
+
if (report.unguided && report.items.length === 0 && !retest) return {
|
|
702
|
+
unguided: true,
|
|
703
|
+
open: []
|
|
704
|
+
};
|
|
705
|
+
const requirements = read(".gtd/REQUIREMENTS.md") ?? "";
|
|
706
|
+
const ids = criterionIds(requirements, slice);
|
|
707
|
+
const elsewhere = criterionIds(requirements).filter((id) => !ids.includes(id));
|
|
708
|
+
const count = (id) => report.items.filter((i) => i.subject === id).length;
|
|
709
|
+
const wrong = ids.filter((id) => count(id) !== 1);
|
|
710
|
+
if (wrong.length > 0) refuse(`gtd land: prototype-report: ${PROTOTYPE} needs exactly one chunk headed \`## <ID>: pass|fail|blocked\` for ${wrong.join(", ")}`);
|
|
711
|
+
return {
|
|
712
|
+
unguided: false,
|
|
713
|
+
open: report.items.filter((i) => i.verdict !== "pass" && !elsewhere.includes(i.subject))
|
|
714
|
+
};
|
|
715
|
+
};
|
|
716
|
+
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
717
|
+
const testing = async (slice, retest) => {
|
|
718
|
+
await agentWithSkills("testing", prototypeTestingPrompt(sliceIds(slice), retest), {
|
|
719
|
+
label: retest ? "Re-testing the slice by hand" : "Testing the slice by hand",
|
|
720
|
+
file: PROTOTYPE,
|
|
721
|
+
model: coder(),
|
|
722
|
+
system: testerSystem(),
|
|
723
|
+
allowEmpty: true
|
|
724
|
+
});
|
|
725
|
+
return requireReport(slice, retest);
|
|
726
|
+
};
|
|
727
|
+
const fixing = async (slice, guard) => {
|
|
728
|
+
await scope("fix", () => agentWithSkills("fixing", prototypeFixingPrompt(sliceIds(slice)), {
|
|
729
|
+
label: "Fixing what the hands-on test found",
|
|
730
|
+
file: PROTOTYPE,
|
|
731
|
+
model: coder(),
|
|
732
|
+
system: builderSystem(),
|
|
733
|
+
allowEmpty: true
|
|
734
|
+
}));
|
|
735
|
+
if (changes().some((c) => c.path === ".gtd/PROTOTYPE.md")) refuse(`gtd land: prototype-fix: leave ${PROTOTYPE} as it is — the next round re-tests from it`);
|
|
736
|
+
frozen();
|
|
737
|
+
guard?.();
|
|
738
|
+
};
|
|
739
|
+
/** A person's rest; resolves `true` when they changed something to test again, `false` on a clean overrule. */
|
|
740
|
+
const askPerson = async (name, message, label) => {
|
|
741
|
+
await human(name, {
|
|
742
|
+
message,
|
|
743
|
+
label,
|
|
744
|
+
file: PROTOTYPE,
|
|
745
|
+
acceptClean: true
|
|
746
|
+
});
|
|
747
|
+
if (!changes().some((c) => c.path !== ".gtd/PROTOTYPE.md")) return false;
|
|
748
|
+
frozen();
|
|
749
|
+
return true;
|
|
750
|
+
};
|
|
751
|
+
/**
|
|
752
|
+
* Use the slice by hand: a testing turn reports on every criterion, a fix turn
|
|
753
|
+
* takes each failure, and the slice is tested again — until the report is
|
|
754
|
+
* clean, or `prototypeRounds` fixes are spent and a person decides. `guard`
|
|
755
|
+
* holds every fix turn, as it holds the implementation's.
|
|
756
|
+
*/
|
|
757
|
+
const prototypeTest = async (slice, guard) => {
|
|
758
|
+
const cap = prototypeRounds();
|
|
759
|
+
if (cap === 0) return;
|
|
760
|
+
await scope("prototype", async () => {
|
|
761
|
+
let fixes = 0;
|
|
762
|
+
let retest = false;
|
|
763
|
+
for (;;) {
|
|
764
|
+
const { unguided, open } = await testing(slice, retest);
|
|
765
|
+
retest = !unguided;
|
|
766
|
+
if (unguided) {
|
|
767
|
+
if (!await askPerson("unguided", prototypeUnguidedMessage(), "No way to test by hand")) return;
|
|
768
|
+
} else if (open.length === 0) return;
|
|
769
|
+
else if (fixes < cap && open.some((i) => i.verdict === "fail")) {
|
|
770
|
+
fixes++;
|
|
771
|
+
await fixing(slice, guard);
|
|
772
|
+
} else {
|
|
773
|
+
if (!await askPerson("open", prototypeOpenMessage(open, fixes), "Hands-on test still failing")) return;
|
|
774
|
+
fixes = 0;
|
|
775
|
+
}
|
|
776
|
+
await green(guard);
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
//#endregion
|
|
656
781
|
//#region src/workflows/feature/seam.ts
|
|
657
782
|
/** Naive-pass retries after a `reject` or a red suite; the next one rests at `seam.steer`. */
|
|
658
783
|
const NAIVE_RETRIES = 2;
|
|
@@ -859,8 +984,10 @@ const implementing = (options) => scope("implement", async () => {
|
|
|
859
984
|
...acceptance,
|
|
860
985
|
ids: settled(acceptance.ids, options.slice)
|
|
861
986
|
}, since);
|
|
862
|
-
|
|
987
|
+
const keepsTests = built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0;
|
|
988
|
+
await green(keepsTests);
|
|
863
989
|
await requireCoverage(options.base, options.slice);
|
|
990
|
+
await prototypeTest(options.slice, keepsTests);
|
|
864
991
|
const ids = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
865
992
|
await closing(moreToBuild(options.more));
|
|
866
993
|
return ids;
|
|
@@ -869,11 +996,16 @@ const moreToBuild = (more) => typeof more === "function" ? more() : more === tru
|
|
|
869
996
|
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
870
997
|
const closeOut = () => scope("implement", () => closing(false));
|
|
871
998
|
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
872
|
-
const closing = (more) => run("closing", removeScript(more ? [
|
|
999
|
+
const closing = (more) => run("closing", removeScript(more ? [
|
|
1000
|
+
REFACTOR_NOTE,
|
|
1001
|
+
HANDOFF,
|
|
1002
|
+
PROTOTYPE
|
|
1003
|
+
] : [
|
|
873
1004
|
REQUIREMENTS,
|
|
874
1005
|
REFACTOR_NOTE,
|
|
875
1006
|
SLICES,
|
|
876
|
-
HANDOFF
|
|
1007
|
+
HANDOFF,
|
|
1008
|
+
PROTOTYPE
|
|
877
1009
|
]), { label: "Closing out the implementation" });
|
|
878
1010
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
879
1011
|
const steer = async (retry) => {
|
|
@@ -1238,6 +1370,7 @@ const ordinaryStart = async () => {
|
|
|
1238
1370
|
refusePendingHandOff();
|
|
1239
1371
|
flagModeOn();
|
|
1240
1372
|
sliceCap();
|
|
1373
|
+
prototypeRounds();
|
|
1241
1374
|
const stated = statedCriteria(changes(), head());
|
|
1242
1375
|
await unwind();
|
|
1243
1376
|
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
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 { _ as reviewRisks, g as reviewNotes } from "../chunks/steering-DtUVUz9l.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { C as coder, It as defaults, J as finisherSystem, Lt as envDefaults, N as agentWithSkills, Y as fixReviewNotesPrompt, c as baseline, i as skills, n as summary, r as access, s as shipped, t as steering, y as REVIEW } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-CX6Pdsql.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,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as refusePendingHandOff } from "../chunks/tail-
|
|
4
|
-
import { i as reUnwind, t as buildTail } from "../chunks/review-
|
|
2
|
+
import { C as coder, It as defaults, J as finisherSystem, Lt as envDefaults, N as agentWithSkills, i as skills, l as gate, n as summary, r as access, s as shipped, t as steering, v as REQUIREMENTS, vt as reviewFeedbackFixPrompt, yt as reviewGateBlockedMessage } from "../chunks/reserved-CUkk9XgL.mjs";
|
|
3
|
+
import { a as refusePendingHandOff } from "../chunks/tail-CX6Pdsql.mjs";
|
|
4
|
+
import { i as reUnwind, t as buildTail } from "../chunks/review-BaCXlIT2.mjs";
|
|
5
5
|
//#region src/workflows/review/review.ts
|
|
6
6
|
const fixFeedback = async () => {
|
|
7
7
|
await agentWithSkills("feedback.fixing", reviewFeedbackFixPrompt(), {
|
|
@@ -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 { C as coder, It as defaults, Jt as flagMentions, Lt as envDefaults, Qt as requireFlag, Xt as flipProblems, f as FEEDBACK, i as skills, l as gate, n as summary, o as flagShipped, r as access, t as steering } from "../chunks/reserved-CUkk9XgL.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.";
|
package/dist/workflows.mjs
CHANGED
|
@@ -17241,6 +17241,7 @@ const defaults = {
|
|
|
17241
17241
|
qualityReviews: "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
|
|
17242
17242
|
refactorQualityReviews: "correctness, ponytail-review, conventions",
|
|
17243
17243
|
continuations: "3",
|
|
17244
|
+
prototypeRounds: "3",
|
|
17244
17245
|
continuationStall: "2"
|
|
17245
17246
|
};
|
|
17246
17247
|
/** Environment settings. */
|
|
@@ -17318,6 +17319,11 @@ task in front of you end to end, tests first, and come back to fix
|
|
|
17318
17319
|
things when redirected — a failing check, or reviewer feedback. Stay
|
|
17319
17320
|
strictly inside that task; never touch code outside it. Treat feedback as the
|
|
17320
17321
|
diagnosis to act on; discard it, with reason, only when simply wrong.`;
|
|
17322
|
+
const testerPersona = `You are the hands-on tester in gtd's build pipeline: use what was just
|
|
17323
|
+
built the way a person would — run the real program, open the real
|
|
17324
|
+
page, call the real endpoint — and report exactly what you did and
|
|
17325
|
+
what you saw. Never fix anything yourself, and never pass a criterion
|
|
17326
|
+
you did not watch hold.`;
|
|
17321
17327
|
const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
|
|
17322
17328
|
before it closes. Fix a late-breaking failing check after sign-off,
|
|
17323
17329
|
focused and minimal. Every turn lands its own commit — nothing here
|
|
@@ -17743,8 +17749,11 @@ const acceptanceCriteriaBlock = `
|
|
|
17743
17749
|
Whichever lap this is, \`.gtd/REQUIREMENTS.md\` carries a
|
|
17744
17750
|
\`## Acceptance Criteria\` section: one \`- AC-<n>: <criterion>\` line
|
|
17745
17751
|
per criterion, numbered from \`AC-1\`. They bind every later step —
|
|
17746
|
-
the build is done when each one holds — so each must be
|
|
17747
|
-
test
|
|
17752
|
+
the build is done when each one holds — so each must be behaviour a
|
|
17753
|
+
test can pin: one that fails when it does not hold. A green suite, a
|
|
17754
|
+
passing typecheck or lint, a generator or build that runs — the health
|
|
17755
|
+
check enforces those on every lap, so none is a criterion; fold what it
|
|
17756
|
+
must produce into a behaviour a test can observe. A later lap keeps every ID:
|
|
17748
17757
|
reword a criterion in place, append a new one with the next unused
|
|
17749
17758
|
number, delete a dropped one — never renumber, never reuse an ID. When
|
|
17750
17759
|
the file holds no list (a review loop-back rewrites it), recover the
|
|
@@ -17852,7 +17861,7 @@ const clearCriteriaQuestion = {
|
|
|
17852
17861
|
id: "clear",
|
|
17853
17862
|
primitive: "noul",
|
|
17854
17863
|
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.",
|
|
17855
|
-
criteria: "yes: every criterion is clear
|
|
17864
|
+
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."
|
|
17856
17865
|
};
|
|
17857
17866
|
const clearCriteriaMessage = () => `The sketch stated its own acceptance criteria. Run \`gtd judge answer\` and
|
|
17858
17867
|
pipe \`yes\` when every one is clear and checkable, so design is skipped and
|
|
@@ -18771,6 +18780,80 @@ Two ways forward (then run \`gtd land\`):
|
|
|
18771
18780
|
- **Overrule** — land a clean tree to finish without them. The slice records
|
|
18772
18781
|
the IDs it covers and, as \`uncovered\`, the ones it does not
|
|
18773
18782
|
`;
|
|
18783
|
+
const testerSystem = () => `${testerPersona}
|
|
18784
|
+
|
|
18785
|
+
${agentConduct}`;
|
|
18786
|
+
const PROTOTYPE_FORMAT = `- Write \`.gtd/PROTOTYPE.md\`, replacing any earlier report: one \`## \` chunk
|
|
18787
|
+
per criterion, headed \`## AC-<n>: pass\`, \`## AC-<n>: fail\` or
|
|
18788
|
+
\`## AC-<n>: blocked\`, then one chunk per problem you found outside any
|
|
18789
|
+
criterion, headed \`## Problem <short title>: fail\` (\`: pass\` once a
|
|
18790
|
+
re-test shows it fixed). Under each: the exact steps you ran, what you
|
|
18791
|
+
observed (output, a screenshot path, the error), and for a failure what
|
|
18792
|
+
you expected instead
|
|
18793
|
+
- \`blocked\` is for a criterion you could not exercise, or one whose
|
|
18794
|
+
expected behaviour itself looks wrong — say which and why: a person
|
|
18795
|
+
decides it, never a fixer`;
|
|
18796
|
+
const prototypeTestingPrompt = (slice, retest) => `${stateFileRules}
|
|
18797
|
+
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
18798
|
+
${sliceLine(slice)}- The change is built and its suite is green. Now test it by hand: use
|
|
18799
|
+
every acceptance criterion the way a person would and watch what
|
|
18800
|
+
happens. Running the test suite is not this — it already ran
|
|
18801
|
+
- Find how this project is exercised for real, in this order: a project
|
|
18802
|
+
skill that launches or drives it (e.g. \`.claude/skills/*\`); a manual
|
|
18803
|
+
testing or running section in \`AGENTS.md\`, \`CLAUDE.md\` or \`README.md\`;
|
|
18804
|
+
failing those, the project's shape — a browser or Playwright for a web
|
|
18805
|
+
project, the built CLI in a scratch directory for a CLI, a request client
|
|
18806
|
+
for an API
|
|
18807
|
+
- Found none and recognised no shape: write only a chunk headed
|
|
18808
|
+
\`## No testing instructions\` saying what you looked for, and finish —
|
|
18809
|
+
a person is asked; never guess
|
|
18810
|
+
- Test in a disposable place only — a scratch directory outside this
|
|
18811
|
+
repository, a throwaway repository, a dev server on a free port — never
|
|
18812
|
+
against the user's real data or this working tree. Point every tool's
|
|
18813
|
+
output (screenshots, traces, test results) outside the repository too
|
|
18814
|
+
${retest ? `- This is a re-test: \`.gtd/PROTOTYPE.md\` holds the last round's report and
|
|
18815
|
+
a fix turn or a person has changed the code since. Re-test every failed or blocked criterion and
|
|
18816
|
+
every problem it lists in full, then re-run the passed ones quickly
|
|
18817
|
+
` : ""}${PROTOTYPE_FORMAT}
|
|
18818
|
+
- Change nothing but \`.gtd/PROTOTYPE.md\` — a turn that does is refused
|
|
18819
|
+
- Leave everything uncommitted and finish your turn — do not commit
|
|
18820
|
+
`;
|
|
18821
|
+
const prototypeFixingPrompt = (slice) => `${stateFileRules}
|
|
18822
|
+
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
18823
|
+
${sliceLine(slice)}${criteriaBinding(true)}
|
|
18824
|
+
- Read \`.gtd/PROTOTYPE.md\`: a hands-on test of the change. Fix every
|
|
18825
|
+
\`fail\` chunk of this change's criteria${slice === void 0 ? "" : " (this slice's only)"} or a problem found
|
|
18826
|
+
beside them, at its cause, with a test that shows it; leave \`blocked\`
|
|
18827
|
+
ones to the person
|
|
18828
|
+
- A \`fail\` the code does not explain (the tester misread what it saw) has
|
|
18829
|
+
nothing to fix: leave it and change nothing for it — the next round re-tests
|
|
18830
|
+
- Leave \`.gtd/PROTOTYPE.md\` as it is: the next round re-tests from it
|
|
18831
|
+
- Run \`${env.testCommand}\` and finish only once it is green
|
|
18832
|
+
- Leave everything uncommitted and finish your turn — do not commit
|
|
18833
|
+
`;
|
|
18834
|
+
const openItems = (items) => items.map((item) => `- ${item.subject}: ${item.verdict}`).join("\n");
|
|
18835
|
+
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"}:
|
|
18836
|
+
${openItems(items)}
|
|
18837
|
+
|
|
18838
|
+
\`.gtd/PROTOTYPE.md\` holds the last report: the steps run and what was seen.
|
|
18839
|
+
|
|
18840
|
+
Two ways forward (then run \`gtd land\`):
|
|
18841
|
+
- **Fix** — change the code, or the testing instructions, yourself (the
|
|
18842
|
+
criteria are settled: an edit to them is refused); the suite runs and the
|
|
18843
|
+
slice is tested again, with a fresh round budget
|
|
18844
|
+
- **Overrule** — land a clean tree to go on to the quality lap with these
|
|
18845
|
+
items open
|
|
18846
|
+
`;
|
|
18847
|
+
const prototypeUnguidedMessage = () => `The hands-on test found no way to exercise this project for real — no
|
|
18848
|
+
project skill, no manual testing section in \`AGENTS.md\`, \`CLAUDE.md\` or
|
|
18849
|
+
\`README.md\`, and no project shape it recognised. \`.gtd/PROTOTYPE.md\` says
|
|
18850
|
+
what it looked for.
|
|
18851
|
+
|
|
18852
|
+
Two ways forward (then run \`gtd land\`):
|
|
18853
|
+
- **Instruct** — add testing instructions (a project skill, or a manual
|
|
18854
|
+
testing section); the slice is tested again
|
|
18855
|
+
- **Skip** — land a clean tree to go on to the quality lap untested
|
|
18856
|
+
`;
|
|
18774
18857
|
//#endregion
|
|
18775
18858
|
//#region src/workflows/shared/steps.ts
|
|
18776
18859
|
const FEEDBACK = ".gtd/FEEDBACK.md";
|
|
@@ -18782,6 +18865,7 @@ const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md";
|
|
|
18782
18865
|
const QUALITY = ".gtd/QUALITY.md";
|
|
18783
18866
|
const SLICES = ".gtd/SLICES.md";
|
|
18784
18867
|
const HANDOFF = ".gtd/HANDOFF.md";
|
|
18868
|
+
const PROTOTYPE = ".gtd/PROTOTYPE.md";
|
|
18785
18869
|
const planner = () => env.plannerModel ?? "";
|
|
18786
18870
|
const coder = () => env.coderModel ?? "";
|
|
18787
18871
|
const fixCheck = (rules) => agentWithSkills("fix", buildFixPrompt(rules), {
|
|
@@ -38499,6 +38583,7 @@ const ShippedOptions = Struct({
|
|
|
38499
38583
|
qualityReviews: setting$1,
|
|
38500
38584
|
refactorQualityReviews: setting$1,
|
|
38501
38585
|
continuations: setting$1,
|
|
38586
|
+
prototypeRounds: setting$1,
|
|
38502
38587
|
continuationStall: setting$1,
|
|
38503
38588
|
testCommand: setting$1,
|
|
38504
38589
|
plannerModel: setting$1,
|
|
@@ -39748,7 +39833,9 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
39748
39833
|
feedback,
|
|
39749
39834
|
fixAccess: strayAccess(p.strays),
|
|
39750
39835
|
lensBase: entry,
|
|
39751
|
-
rules: refactorRules(p.strays, p.pattern)
|
|
39836
|
+
rules: refactorRules(p.strays, p.pattern),
|
|
39837
|
+
walkthrough: true,
|
|
39838
|
+
lap: "refactor"
|
|
39752
39839
|
});
|
|
39753
39840
|
if (outcome.verdict === "signoff") return true;
|
|
39754
39841
|
await reUnwind(outcome);
|
|
@@ -39763,7 +39850,7 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
39763
39850
|
const COVERED_MIN_P = .7;
|
|
39764
39851
|
/** Covering turns before the process rests at `uncovered`; a human edit there refills them. */
|
|
39765
39852
|
const COVERING_RETRIES = 2;
|
|
39766
|
-
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
39853
|
+
const frozen$1 = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
39767
39854
|
const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
39768
39855
|
label: "Fixing the check",
|
|
39769
39856
|
file: FEEDBACK,
|
|
@@ -39771,7 +39858,7 @@ const green = (guard) => healthy(() => agentWithSkills("fix.suite.fixing", imple
|
|
|
39771
39858
|
system: builderSystem()
|
|
39772
39859
|
}), {
|
|
39773
39860
|
guard: () => {
|
|
39774
|
-
frozen();
|
|
39861
|
+
frozen$1();
|
|
39775
39862
|
guard?.();
|
|
39776
39863
|
},
|
|
39777
39864
|
guardRests: false
|
|
@@ -39824,7 +39911,7 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
39824
39911
|
system: builderSystem(),
|
|
39825
39912
|
allowEmpty: true
|
|
39826
39913
|
});
|
|
39827
|
-
frozen();
|
|
39914
|
+
frozen$1();
|
|
39828
39915
|
} else {
|
|
39829
39916
|
await human("uncovered", {
|
|
39830
39917
|
message: uncoveredMessage(uncovered),
|
|
@@ -39833,13 +39920,136 @@ const requireCoverage = async (base = start(), slice) => {
|
|
|
39833
39920
|
acceptClean: true
|
|
39834
39921
|
});
|
|
39835
39922
|
if (!changes().some((c) => isCode$1(c.path))) return record(covered, uncovered);
|
|
39836
|
-
frozen();
|
|
39923
|
+
frozen$1();
|
|
39837
39924
|
retries = 0;
|
|
39838
39925
|
}
|
|
39839
39926
|
await green();
|
|
39840
39927
|
}
|
|
39841
39928
|
};
|
|
39842
39929
|
//#endregion
|
|
39930
|
+
//#region src/workflows/feature/prototype.ts
|
|
39931
|
+
/** The `prototypeRounds` setting. Anything but a non-negative integer fails rather than guess a cap. */
|
|
39932
|
+
const prototypeRounds = () => {
|
|
39933
|
+
const value = vars.prototypeRounds ?? defaults.prototypeRounds;
|
|
39934
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) refuse(`gtd: prototypeRounds must be a non-negative integer, got ${JSON.stringify(value)}`);
|
|
39935
|
+
return Number(value);
|
|
39936
|
+
};
|
|
39937
|
+
const CHUNK = /^(AC-\d+|Problem\b.*?)\s*:\s*(pass|fail|blocked)$/i;
|
|
39938
|
+
const UNGUIDED = /^no testing instructions$/i;
|
|
39939
|
+
/** The `## ` chunks of a prototype report; a heading in neither shape is prose, not an item. */
|
|
39940
|
+
const reportOf = (text) => {
|
|
39941
|
+
let unguided = false;
|
|
39942
|
+
const items = [];
|
|
39943
|
+
const malformed = [];
|
|
39944
|
+
for (const { line, fenced } of markFenced(text)) {
|
|
39945
|
+
const heading = fenced ? null : HEADING.exec(line);
|
|
39946
|
+
if (heading?.[1] !== "##") continue;
|
|
39947
|
+
if (UNGUIDED.test(heading[2])) unguided = true;
|
|
39948
|
+
const chunk = CHUNK.exec(heading[2]);
|
|
39949
|
+
if (chunk === null && /^(ac-\d|problem)/i.test(heading[2])) malformed.push(heading[2]);
|
|
39950
|
+
if (chunk !== null) items.push({
|
|
39951
|
+
subject: /^ac-/i.test(chunk[1]) ? chunk[1].toUpperCase() : chunk[1],
|
|
39952
|
+
verdict: chunk[2].toLowerCase()
|
|
39953
|
+
});
|
|
39954
|
+
}
|
|
39955
|
+
return {
|
|
39956
|
+
unguided,
|
|
39957
|
+
items,
|
|
39958
|
+
malformed
|
|
39959
|
+
};
|
|
39960
|
+
};
|
|
39961
|
+
/**
|
|
39962
|
+
* The report the testing turn left, refused unless it is the only change and
|
|
39963
|
+
* names every criterion once, with its open items: another slice's criteria
|
|
39964
|
+
* are not this slice's to fix.
|
|
39965
|
+
*/
|
|
39966
|
+
const requireReport = (slice, retest) => {
|
|
39967
|
+
const others = changes().map((c) => c.path).filter((path) => path !== PROTOTYPE);
|
|
39968
|
+
if (others.length > 0) refuse(`gtd land: prototype-report: test in a disposable place and change nothing but ${PROTOTYPE} — revert these:\n${others.map((path) => ` - ${path}`).join("\n")}`);
|
|
39969
|
+
const text = read(PROTOTYPE);
|
|
39970
|
+
if (text === void 0) return refuse(`gtd land: prototype-report: write ${PROTOTYPE}`);
|
|
39971
|
+
const report = reportOf(text);
|
|
39972
|
+
if (report.malformed.length > 0) refuse(`gtd land: prototype-report: head each chunk \`## <ID>: pass|fail|blocked\` or \`## Problem <title>: pass|fail|blocked\` — these miss it:\n${report.malformed.map((h) => ` - ${h}`).join("\n")}`);
|
|
39973
|
+
if (report.unguided && report.items.length === 0 && !retest) return {
|
|
39974
|
+
unguided: true,
|
|
39975
|
+
open: []
|
|
39976
|
+
};
|
|
39977
|
+
const requirements = read(".gtd/REQUIREMENTS.md") ?? "";
|
|
39978
|
+
const ids = criterionIds(requirements, slice);
|
|
39979
|
+
const elsewhere = criterionIds(requirements).filter((id) => !ids.includes(id));
|
|
39980
|
+
const count = (id) => report.items.filter((i) => i.subject === id).length;
|
|
39981
|
+
const wrong = ids.filter((id) => count(id) !== 1);
|
|
39982
|
+
if (wrong.length > 0) refuse(`gtd land: prototype-report: ${PROTOTYPE} needs exactly one chunk headed \`## <ID>: pass|fail|blocked\` for ${wrong.join(", ")}`);
|
|
39983
|
+
return {
|
|
39984
|
+
unguided: false,
|
|
39985
|
+
open: report.items.filter((i) => i.verdict !== "pass" && !elsewhere.includes(i.subject))
|
|
39986
|
+
};
|
|
39987
|
+
};
|
|
39988
|
+
const frozen = () => requireCriteriaFrozen(REQUIREMENTS);
|
|
39989
|
+
const testing = async (slice, retest) => {
|
|
39990
|
+
await agentWithSkills("testing", prototypeTestingPrompt(sliceIds(slice), retest), {
|
|
39991
|
+
label: retest ? "Re-testing the slice by hand" : "Testing the slice by hand",
|
|
39992
|
+
file: PROTOTYPE,
|
|
39993
|
+
model: coder(),
|
|
39994
|
+
system: testerSystem(),
|
|
39995
|
+
allowEmpty: true
|
|
39996
|
+
});
|
|
39997
|
+
return requireReport(slice, retest);
|
|
39998
|
+
};
|
|
39999
|
+
const fixing = async (slice, guard) => {
|
|
40000
|
+
await scope("fix", () => agentWithSkills("fixing", prototypeFixingPrompt(sliceIds(slice)), {
|
|
40001
|
+
label: "Fixing what the hands-on test found",
|
|
40002
|
+
file: PROTOTYPE,
|
|
40003
|
+
model: coder(),
|
|
40004
|
+
system: builderSystem(),
|
|
40005
|
+
allowEmpty: true
|
|
40006
|
+
}));
|
|
40007
|
+
if (changes().some((c) => c.path === ".gtd/PROTOTYPE.md")) refuse(`gtd land: prototype-fix: leave ${PROTOTYPE} as it is — the next round re-tests from it`);
|
|
40008
|
+
frozen();
|
|
40009
|
+
guard?.();
|
|
40010
|
+
};
|
|
40011
|
+
/** A person's rest; resolves `true` when they changed something to test again, `false` on a clean overrule. */
|
|
40012
|
+
const askPerson = async (name, message, label) => {
|
|
40013
|
+
await human(name, {
|
|
40014
|
+
message,
|
|
40015
|
+
label,
|
|
40016
|
+
file: PROTOTYPE,
|
|
40017
|
+
acceptClean: true
|
|
40018
|
+
});
|
|
40019
|
+
if (!changes().some((c) => c.path !== ".gtd/PROTOTYPE.md")) return false;
|
|
40020
|
+
frozen();
|
|
40021
|
+
return true;
|
|
40022
|
+
};
|
|
40023
|
+
/**
|
|
40024
|
+
* Use the slice by hand: a testing turn reports on every criterion, a fix turn
|
|
40025
|
+
* takes each failure, and the slice is tested again — until the report is
|
|
40026
|
+
* clean, or `prototypeRounds` fixes are spent and a person decides. `guard`
|
|
40027
|
+
* holds every fix turn, as it holds the implementation's.
|
|
40028
|
+
*/
|
|
40029
|
+
const prototypeTest = async (slice, guard) => {
|
|
40030
|
+
const cap = prototypeRounds();
|
|
40031
|
+
if (cap === 0) return;
|
|
40032
|
+
await scope("prototype", async () => {
|
|
40033
|
+
let fixes = 0;
|
|
40034
|
+
let retest = false;
|
|
40035
|
+
for (;;) {
|
|
40036
|
+
const { unguided, open } = await testing(slice, retest);
|
|
40037
|
+
retest = !unguided;
|
|
40038
|
+
if (unguided) {
|
|
40039
|
+
if (!await askPerson("unguided", prototypeUnguidedMessage(), "No way to test by hand")) return;
|
|
40040
|
+
} else if (open.length === 0) return;
|
|
40041
|
+
else if (fixes < cap && open.some((i) => i.verdict === "fail")) {
|
|
40042
|
+
fixes++;
|
|
40043
|
+
await fixing(slice, guard);
|
|
40044
|
+
} else {
|
|
40045
|
+
if (!await askPerson("open", prototypeOpenMessage(open, fixes), "Hands-on test still failing")) return;
|
|
40046
|
+
fixes = 0;
|
|
40047
|
+
}
|
|
40048
|
+
await green(guard);
|
|
40049
|
+
}
|
|
40050
|
+
});
|
|
40051
|
+
};
|
|
40052
|
+
//#endregion
|
|
39843
40053
|
//#region src/workflows/feature/seam.ts
|
|
39844
40054
|
/** Naive-pass retries after a `reject` or a red suite; the next one rests at `seam.steer`. */
|
|
39845
40055
|
const NAIVE_RETRIES = 2;
|
|
@@ -40046,8 +40256,10 @@ const implementing = (options) => scope("implement", async () => {
|
|
|
40046
40256
|
...acceptance,
|
|
40047
40257
|
ids: settled(acceptance.ids, options.slice)
|
|
40048
40258
|
}, since);
|
|
40049
|
-
|
|
40259
|
+
const keepsTests = built && acceptance !== void 0 ? () => requireAcceptanceKept(acceptance) : void 0;
|
|
40260
|
+
await green(keepsTests);
|
|
40050
40261
|
await requireCoverage(options.base, options.slice);
|
|
40262
|
+
await prototypeTest(options.slice, keepsTests);
|
|
40051
40263
|
const ids = criterionIds(read(".gtd/REQUIREMENTS.md") ?? "");
|
|
40052
40264
|
await closing(moreToBuild(options.more));
|
|
40053
40265
|
return ids;
|
|
@@ -40056,11 +40268,16 @@ const moreToBuild = (more) => typeof more === "function" ? more() : more === tru
|
|
|
40056
40268
|
/** The last slice's closing, for a last slice a steer left with no criterion to build. */
|
|
40057
40269
|
const closeOut = () => scope("implement", () => closing(false));
|
|
40058
40270
|
/** A slice with `more` after it keeps the criteria and the slicing for the next one. */
|
|
40059
|
-
const closing = (more) => run("closing", removeScript(more ? [
|
|
40271
|
+
const closing = (more) => run("closing", removeScript(more ? [
|
|
40272
|
+
REFACTOR_NOTE,
|
|
40273
|
+
HANDOFF,
|
|
40274
|
+
PROTOTYPE
|
|
40275
|
+
] : [
|
|
40060
40276
|
REQUIREMENTS,
|
|
40061
40277
|
REFACTOR_NOTE,
|
|
40062
40278
|
SLICES,
|
|
40063
|
-
HANDOFF
|
|
40279
|
+
HANDOFF,
|
|
40280
|
+
PROTOTYPE
|
|
40064
40281
|
]), { label: "Closing out the implementation" });
|
|
40065
40282
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
40066
40283
|
const steer = async (retry) => {
|
|
@@ -40425,6 +40642,7 @@ const ordinaryStart = async () => {
|
|
|
40425
40642
|
refusePendingHandOff();
|
|
40426
40643
|
flagModeOn();
|
|
40427
40644
|
sliceCap();
|
|
40645
|
+
prototypeRounds();
|
|
40428
40646
|
const stated = statedCriteria(changes(), head$1());
|
|
40429
40647
|
await unwind();
|
|
40430
40648
|
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
@@ -40459,6 +40677,10 @@ const skills = (vars) => {
|
|
|
40459
40677
|
seam: ["incremental-implementation"],
|
|
40460
40678
|
implement: ["test-driven-development", "incremental-implementation"],
|
|
40461
40679
|
"implement.fix.suite": ["debugging-and-error-recovery"],
|
|
40680
|
+
"implement.prototype": [],
|
|
40681
|
+
"implement.prototype.fix": ["test-driven-development", "incremental-implementation"],
|
|
40682
|
+
"implement.prototype.fix.suite": ["debugging-and-error-recovery"],
|
|
40683
|
+
"implement.prototype.health": ["debugging-and-error-recovery"],
|
|
40462
40684
|
"implement.health": ["debugging-and-error-recovery"],
|
|
40463
40685
|
...lensScopes("build.quality", lensesOf(vars.qualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens]),
|
|
40464
40686
|
...lensScopes("refactor.build.quality", lensesOf(vars.refactorQualityReviews ?? ""), (lens) => builtInLenses[lens]?.skills ?? [lens])
|
|
@@ -40636,4 +40858,4 @@ var workflows_default = feature;
|
|
|
40636
40858
|
*/
|
|
40637
40859
|
const doors = {};
|
|
40638
40860
|
//#endregion
|
|
40639
|
-
export { COMMENT, ESCALATION, FEEDBACK, FIX_CAP, FlagOptions, HANDOFF, QUALITY, REFACTOR, REFACTOR_NOTE, REFACTOR_PACKAGE, REQUIREMENTS, REVIEW, REVIEW_ORIG, SLICES, ShippedOptions, acceptanceFault, access, afterTail, agentWithSkills, answerProductQuestions, answerReviewQuestions, awaitReview, base, baseline, buildFeature, buildTail, builtInLenses, cleanup, cleanupFlow, closeOut, closeReview, coder, collecting, criteriaOf, criterionIds, workflows_default as default, defaults, describeEscalation, design, doors, envDefaults, escalate, escalation, escalationExhausted, feature, fix, fixCheck, fixFlow, fixNits, fixQuality, fixReviewNotes, fixRisks, gate, guarded, handOffReview, handOffTail, healthy, holdsCallSites, implement, implementing, isTestFile, judgeSeam, lensEntriesOf, lensOf, lensesOf, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, planner, qualityLap, reUnwind, recheck, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireCriteriaIfEdited, requireNoNewFlags, requireNoTestFiles, requireSlices, requireStableIds, requireTestCallSitesOnly, requireWithin, retryQuestion, review, reviewFlow, reviewMissing, reviewQuality, reviewedRefactor, reviewing, rollout, rolloutFlow, seamBuild, skills, skipDesign, sliceCap, sliceCriteria, sliceIds, slicesOf, slicing, statedCriteria, steering, summary, tail, threads, triage, unstableIds, unwind };
|
|
40861
|
+
export { COMMENT, ESCALATION, FEEDBACK, FIX_CAP, FlagOptions, HANDOFF, PROTOTYPE, QUALITY, REFACTOR, REFACTOR_NOTE, REFACTOR_PACKAGE, REQUIREMENTS, REVIEW, REVIEW_ORIG, SLICES, ShippedOptions, acceptanceFault, access, afterTail, agentWithSkills, answerProductQuestions, answerReviewQuestions, awaitReview, base, baseline, buildFeature, buildTail, builtInLenses, cleanup, cleanupFlow, closeOut, closeReview, coder, collecting, criteriaOf, criterionIds, workflows_default as default, defaults, describeEscalation, design, doors, envDefaults, escalate, escalation, escalationExhausted, feature, fix, fixCheck, fixFlow, fixNits, fixQuality, fixReviewNotes, fixRisks, gate, guarded, handOffReview, handOffTail, healthy, holdsCallSites, implement, implementing, isTestFile, judgeSeam, lensEntriesOf, lensOf, lensesOf, naiveAcceptance, naivePass, ordinaryStart, planAndBuild, planner, qualityLap, reUnwind, recheck, refactorCollecting, refactorFirst, refactorNote, refactorStrays, requireAcceptance, requireAcceptanceKept, requireCriteria, requireCriteriaFrozen, requireCriteriaIfEdited, requireNoNewFlags, requireNoTestFiles, requireSlices, requireStableIds, requireTestCallSitesOnly, requireWithin, retryQuestion, review, reviewFlow, reviewMissing, reviewQuality, reviewedRefactor, reviewing, rollout, rolloutFlow, seamBuild, skills, skipDesign, sliceCap, sliceCriteria, sliceIds, slicesOf, slicing, statedCriteria, steering, summary, tail, threads, triage, unstableIds, unwind };
|