@pmelab/gtd 29.1.0 → 29.2.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 +17 -9
- package/dist/chunks/{reserved-DoHUII2D.mjs → reserved-BzeofpoC.mjs} +25 -9
- package/dist/chunks/{tail-C6YBG5zh.mjs → tail-eS_ZSImR.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +128 -41
- 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 +151 -48
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +13 -5
- package/src/workflows/feature/feature.test.ts +1 -1
- package/src/workflows/feature/feature.ts +6 -5
- package/src/workflows/feature/index.ts +1 -1
- package/src/workflows/feature/packages.test.ts +299 -5
- package/src/workflows/feature/packages.ts +109 -26
- package/src/workflows/feature/refactor.ts +9 -2
- package/src/workflows/feature/review.test.ts +103 -3
- package/src/workflows/feature/review.ts +34 -8
- package/src/workflows/feature/seam.test.ts +15 -1
- package/src/workflows/feature/seam.ts +28 -8
- package/src/workflows/index.ts +1 -1
- package/src/workflows/shared/options.test.ts +9 -3
- package/src/workflows/shared/text.test.ts +47 -3
- package/src/workflows/shared/text.ts +36 -14
package/dist/workflows.mjs
CHANGED
|
@@ -17475,7 +17475,12 @@ ${stateFileRules}
|
|
|
17475
17475
|
- Every package with tests has a \`## Tests\` section, one line per
|
|
17476
17476
|
declared test, copied from the architecture entries:
|
|
17477
17477
|
\`- unit: \\\`<path>\\\`\` or \`- e2e: \\\`<path>\\\`\`. Chore packages have
|
|
17478
|
-
none
|
|
17478
|
+
none. List every test the package writes or changes; also list an
|
|
17479
|
+
existing test a criterion relies on, marked \`(unchanged)\` —
|
|
17480
|
+
\`- unit: \\\`<path>\\\` (unchanged)\` or
|
|
17481
|
+
\`- e2e: \\\`<path>\\\` (unchanged)\`; list no other untouched test;
|
|
17482
|
+
never mark a file the package also writes or changes — list it once,
|
|
17483
|
+
unmarked; only decompose places the marker
|
|
17479
17484
|
- Leave everything uncommitted and finish
|
|
17480
17485
|
`;
|
|
17481
17486
|
const architectureGateAnswerMessage = () => `This gate stops on every process, even with no open question, so
|
|
@@ -17508,15 +17513,26 @@ const packagesItemBuildingPrompt = (pkg) => `${stateFileRules}
|
|
|
17508
17513
|
never delete the package file (the declared-tests guard reads it
|
|
17509
17514
|
after you)
|
|
17510
17515
|
- The package to implement is \`${pkg}\`
|
|
17511
|
-
- First check
|
|
17512
|
-
an earlier fix turn may already
|
|
17513
|
-
|
|
17514
|
-
with
|
|
17515
|
-
symbol
|
|
17516
|
-
|
|
17516
|
+
- First check the acceptance criteria against the current tree —
|
|
17517
|
+
an earlier fix turn may already meet them
|
|
17518
|
+
- **Every criterion already met in the tree**: write
|
|
17519
|
+
\`.gtd/SATISFIED.md\` with evidence per criterion — commit, file,
|
|
17520
|
+
symbol, issue URL, or command output — and change nothing else
|
|
17521
|
+
- **No \`## Tests\` and work outside the tree**: first check it was
|
|
17522
|
+
not already done by an earlier attempt (e.g. an existing issue
|
|
17523
|
+
with the same title via \`gh\`) and reuse that as evidence, or do
|
|
17524
|
+
the work; then write \`.gtd/SATISFIED.md\` with that evidence and
|
|
17525
|
+
change nothing else in the tree
|
|
17526
|
+
- **Any other package**: implement normally, write no
|
|
17527
|
+
\`.gtd/SATISFIED.md\`
|
|
17517
17528
|
- Write the tests the package declares under \`## Tests\` first,
|
|
17518
17529
|
then the code that makes them pass; the turn is refused unless
|
|
17519
|
-
every declared test is in your diff
|
|
17530
|
+
every declared test is in your diff — except a line marked
|
|
17531
|
+
\`(unchanged)\`, which must exist before the turn and stay
|
|
17532
|
+
untouched — creating, editing or deleting it refuses the turn. If
|
|
17533
|
+
it does not exist yet, or the marker is wrong because the package
|
|
17534
|
+
must change that test, stop without creating it and leave it to
|
|
17535
|
+
a person
|
|
17520
17536
|
- A declared e2e test is a scenario in the repository's own e2e
|
|
17521
17537
|
framework for the behaviour its \`## E2E Scenarios\` entry names.
|
|
17522
17538
|
Word it to fit the existing steps — reuse and generalise them
|
|
@@ -17544,7 +17560,7 @@ ${strays.map((path) => ` - \`${displayPath(path)}\``).join("\n")}
|
|
|
17544
17560
|
- Introduce no new flag read and add no second read of an existing one:
|
|
17545
17561
|
every match of \`${pattern}\` may move, never increase in number`;
|
|
17546
17562
|
const withRules = (rules) => rules === void 0 ? "" : `${rules}\n`;
|
|
17547
|
-
const refactorBuildingPrompt = (pkg, strays, pattern, reference, feedback = false) => `${stateFileRules}
|
|
17563
|
+
const refactorBuildingPrompt = ({ pkg, strays, pattern, reference, feedback = false }) => `${stateFileRules}
|
|
17548
17564
|
- The package to implement is \`${pkg}\`; never delete it
|
|
17549
17565
|
${feedback ? `- The package carries a \`## Review feedback\` section: a human's review of the
|
|
17550
17566
|
last refactor. Act on it, within the rules below
|
|
@@ -37955,34 +37971,86 @@ const deferNotes = (notes, round) => run("review.defer", deferNotesScript(notes,
|
|
|
37955
37971
|
});
|
|
37956
37972
|
//#endregion
|
|
37957
37973
|
//#region src/workflows/feature/packages.ts
|
|
37958
|
-
const TEST_LINE =
|
|
37959
|
-
|
|
37974
|
+
const TEST_LINE = /^-\s+(unit|e2e):\s+`([^`]+)`(.*)$/;
|
|
37975
|
+
const MARKER = /^[ \t]+\(unchanged\)(?=\s|$)/i;
|
|
37976
|
+
const ITEM = /^\s*-\s/;
|
|
37977
|
+
/** The `## Tests` entries of a package file; malformed entries are ignored. */
|
|
37960
37978
|
const declaredTests = (packageText) => {
|
|
37961
37979
|
const found = [];
|
|
37962
37980
|
let inTests = false;
|
|
37963
|
-
|
|
37964
|
-
|
|
37965
|
-
const match = TEST_LINE.exec(
|
|
37981
|
+
let entry;
|
|
37982
|
+
const flush = () => {
|
|
37983
|
+
const match = entry === void 0 ? null : TEST_LINE.exec(entry);
|
|
37966
37984
|
if (match) found.push({
|
|
37967
37985
|
level: match[1],
|
|
37968
|
-
path: match[2]
|
|
37986
|
+
path: match[2],
|
|
37987
|
+
unchanged: MARKER.test(match[3])
|
|
37969
37988
|
});
|
|
37970
|
-
|
|
37989
|
+
entry = void 0;
|
|
37990
|
+
};
|
|
37991
|
+
for (const line of packageText.replace(/\r\n?/g, "\n").split("\n")) if (/^##\s/.test(line)) {
|
|
37992
|
+
flush();
|
|
37993
|
+
inTests = /^##\s+Tests\s*$/.test(line);
|
|
37994
|
+
} else if (!inTests) continue;
|
|
37995
|
+
else if (ITEM.test(line)) {
|
|
37996
|
+
flush();
|
|
37997
|
+
entry = line.trim();
|
|
37998
|
+
} else if (entry !== void 0 && /^\s/.test(line) && line.trim() !== "") entry = `${entry} ${line.trim()}`;
|
|
37999
|
+
else flush();
|
|
38000
|
+
flush();
|
|
37971
38001
|
return found;
|
|
37972
38002
|
};
|
|
38003
|
+
const diffBucket = (test, status, turn, satisfied) => {
|
|
38004
|
+
if (test.unchanged) {
|
|
38005
|
+
if (turn !== "build") return void 0;
|
|
38006
|
+
if (status === "modified") return "modifiedMarked";
|
|
38007
|
+
return status === "added" ? "addedMarked" : void 0;
|
|
38008
|
+
}
|
|
38009
|
+
return !satisfied && (status === void 0 || status === "deleted") ? "notInDiff" : void 0;
|
|
38010
|
+
};
|
|
38011
|
+
const treeBucket = (test, status) => {
|
|
38012
|
+
if (read(test.path) !== void 0) return void 0;
|
|
38013
|
+
return test.unchanged && status === "deleted" ? "deletedMarked" : "notInTree";
|
|
38014
|
+
};
|
|
38015
|
+
const unmetBucket = (test, change, turn, satisfied) => diffBucket(test, change?.status, turn, satisfied) ?? treeBucket(test, change?.status);
|
|
37973
38016
|
/**
|
|
37974
38017
|
* Refuse a build or fix turn unless every test `pkg` declares exists and is in the
|
|
37975
38018
|
* diff since `since`. The package text is read as of `since`, so a builder
|
|
37976
|
-
* editing its own `## Tests` cannot dodge the guard;
|
|
37977
|
-
* written,
|
|
38019
|
+
* editing its own `## Tests` (or adding `(unchanged)`) cannot dodge the guard;
|
|
38020
|
+
* with `.gtd/SATISFIED.md` written, or the line marked, existing suffices. The
|
|
38021
|
+
* build turn must neither modify nor add a marked test; a fix turn may edit it.
|
|
37978
38022
|
*/
|
|
37979
|
-
const requireDeclaredTests = (pkg, since) => {
|
|
38023
|
+
const requireDeclaredTests = (pkg, since, turn = "fix") => {
|
|
37980
38024
|
const range = changesSince(since);
|
|
37981
38025
|
const text = range.get(pkg)?.before ?? read(pkg) ?? "";
|
|
37982
38026
|
const satisfied = read(".gtd/SATISFIED.md") !== void 0;
|
|
37983
|
-
const
|
|
37984
|
-
|
|
37985
|
-
|
|
38027
|
+
const buckets = {
|
|
38028
|
+
notInDiff: [],
|
|
38029
|
+
modifiedMarked: [],
|
|
38030
|
+
addedMarked: [],
|
|
38031
|
+
deletedMarked: [],
|
|
38032
|
+
notInTree: []
|
|
38033
|
+
};
|
|
38034
|
+
const entries = declaredTests(text);
|
|
38035
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38036
|
+
for (const test of entries) {
|
|
38037
|
+
if (test.unchanged && entries.some((e) => !e.unchanged && e.path === test.path)) continue;
|
|
38038
|
+
const bucket = unmetBucket(test, range.get(test.path), turn, satisfied);
|
|
38039
|
+
if (bucket && !seen.has(test.path)) {
|
|
38040
|
+
seen.add(test.path);
|
|
38041
|
+
buckets[bucket].push(test.path);
|
|
38042
|
+
}
|
|
38043
|
+
}
|
|
38044
|
+
const { notInDiff, modifiedMarked, addedMarked, deletedMarked, notInTree } = buckets;
|
|
38045
|
+
const reasons = [
|
|
38046
|
+
["not in the package's diff — write them", notInDiff],
|
|
38047
|
+
["marked (unchanged) but modified — revert them", modifiedMarked],
|
|
38048
|
+
["marked (unchanged) but added by this turn — the marker promises an existing test; stop and leave it to a person", addedMarked],
|
|
38049
|
+
["marked (unchanged) but deleted — restore them", deletedMarked],
|
|
38050
|
+
["missing from the tree", notInTree]
|
|
38051
|
+
].filter(([, paths]) => paths.length > 0);
|
|
38052
|
+
if (reasons.length === 0) return;
|
|
38053
|
+
refuse(`gtd land: declared-tests: ${pkg} declares tests this turn left unmet:\n${reasons.map(([reason, paths]) => `${reason}:\n${paths.map((path) => ` - ${path}`).join("\n")}`).join("\n")}`);
|
|
37986
38054
|
};
|
|
37987
38055
|
/**
|
|
37988
38056
|
* Build `pkg`, check its declared tests, keep the full suite green, and close
|
|
@@ -37990,12 +38058,12 @@ const requireDeclaredTests = (pkg, since) => {
|
|
|
37990
38058
|
*/
|
|
37991
38059
|
const packageItem = async (pkg) => {
|
|
37992
38060
|
const from = head$1();
|
|
37993
|
-
const declared = (turn) => async () => {
|
|
38061
|
+
const declared = (turn, kind) => async () => {
|
|
37994
38062
|
await turn();
|
|
37995
|
-
requireDeclaredTests(pkg, from);
|
|
38063
|
+
requireDeclaredTests(pkg, from, kind);
|
|
37996
38064
|
};
|
|
37997
|
-
await declared(() => build(pkg))();
|
|
37998
|
-
await healthy(declared(fixSuite));
|
|
38065
|
+
await declared(() => build(pkg), "build")();
|
|
38066
|
+
await healthy(declared(fixSuite, "fix"));
|
|
37999
38067
|
await run("closing", removeScript([
|
|
38000
38068
|
pkg,
|
|
38001
38069
|
".gtd/SATISFIED.md",
|
|
@@ -38230,8 +38298,16 @@ const review$1 = (base, escalations = { rounds: 0 }, options = {}) => {
|
|
|
38230
38298
|
}
|
|
38231
38299
|
});
|
|
38232
38300
|
};
|
|
38233
|
-
|
|
38234
|
-
|
|
38301
|
+
function buildTail(first, second, third = false, fourth = {}) {
|
|
38302
|
+
if (typeof first !== "string" && typeof first !== "boolean" && typeof second !== "string") return Promise.reject(/* @__PURE__ */ new Error("buildTail takes buildTail(base, options?) or the deprecated buildTail(fixFirst, base, green?, options?); this call fits neither"));
|
|
38303
|
+
const [base, options] = typeof first === "string" ? [first, second ?? {}] : [second, {
|
|
38304
|
+
...fourth,
|
|
38305
|
+
fixFirst: first,
|
|
38306
|
+
green: third
|
|
38307
|
+
}];
|
|
38308
|
+
const { fixFirst, green, ...reviewOptions } = options;
|
|
38309
|
+
return tail(fixFirst ?? false, (escalations) => review$1(base, escalations, reviewOptions), green ?? false, options.guard, options.lenses, options.guardRests, options.lensBase, options.rules);
|
|
38310
|
+
}
|
|
38235
38311
|
/**
|
|
38236
38312
|
* Undo the human's review-round code edits. A path changed since the human
|
|
38237
38313
|
* left it is not overwritten; requireRevert names it instead.
|
|
@@ -38372,7 +38448,13 @@ const refactorLap = async (p, guard, feedback) => {
|
|
|
38372
38448
|
label: "Deriving the refactor package",
|
|
38373
38449
|
file: REFACTOR_PACKAGE
|
|
38374
38450
|
});
|
|
38375
|
-
await agentWithSkills("building", refactorBuildingPrompt(
|
|
38451
|
+
await agentWithSkills("building", refactorBuildingPrompt({
|
|
38452
|
+
pkg: REFACTOR_PACKAGE,
|
|
38453
|
+
strays,
|
|
38454
|
+
pattern,
|
|
38455
|
+
reference,
|
|
38456
|
+
feedback: feedback !== void 0
|
|
38457
|
+
}), {
|
|
38376
38458
|
label: "Refactoring",
|
|
38377
38459
|
model: coder(),
|
|
38378
38460
|
system: builderSystem()
|
|
@@ -38428,7 +38510,8 @@ const reviewedRefactor = async (shape, options = {}) => {
|
|
|
38428
38510
|
let collected;
|
|
38429
38511
|
for (;;) {
|
|
38430
38512
|
await refactorLap(p, guard, collected);
|
|
38431
|
-
const outcome = await buildTail(
|
|
38513
|
+
const outcome = await buildTail(entry, {
|
|
38514
|
+
green: true,
|
|
38432
38515
|
guard,
|
|
38433
38516
|
guardRests: false,
|
|
38434
38517
|
lenses: lensesOf(vars.refactorQualityReviews ?? ""),
|
|
@@ -38461,20 +38544,27 @@ const naivePass = (rejection) => agentWithSkills("naive", naivePassPrompt(reject
|
|
|
38461
38544
|
model: coder(),
|
|
38462
38545
|
system: builderSystem()
|
|
38463
38546
|
});
|
|
38464
|
-
|
|
38465
|
-
|
|
38466
|
-
|
|
38467
|
-
|
|
38468
|
-
|
|
38547
|
+
function implement(first, tier, amend) {
|
|
38548
|
+
const options = typeof first === "object" && first !== null ? first : {
|
|
38549
|
+
reference: first,
|
|
38550
|
+
tier,
|
|
38551
|
+
amend
|
|
38552
|
+
};
|
|
38553
|
+
return scope("implement", async () => {
|
|
38554
|
+
await agentWithSkills("building", implementBuildingPrompt(options.reference, options.tier, options.amend), {
|
|
38555
|
+
label: "Implementing",
|
|
38556
|
+
model: coder(),
|
|
38557
|
+
system: builderSystem()
|
|
38558
|
+
});
|
|
38559
|
+
await healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
38560
|
+
label: "Fixing the check",
|
|
38561
|
+
file: FEEDBACK,
|
|
38562
|
+
model: coder(),
|
|
38563
|
+
system: builderSystem()
|
|
38564
|
+
}));
|
|
38565
|
+
await run("closing", removeScript([REQUIREMENTS]), { label: "Closing out the implementation" });
|
|
38469
38566
|
});
|
|
38470
|
-
|
|
38471
|
-
label: "Fixing the check",
|
|
38472
|
-
file: FEEDBACK,
|
|
38473
|
-
model: coder(),
|
|
38474
|
-
system: builderSystem()
|
|
38475
|
-
}));
|
|
38476
|
-
await run("closing", removeScript([REQUIREMENTS]), { label: "Closing out the implementation" });
|
|
38477
|
-
});
|
|
38567
|
+
}
|
|
38478
38568
|
const decide = () => scope("seam", async () => {
|
|
38479
38569
|
let rejection;
|
|
38480
38570
|
let retries = 0;
|
|
@@ -38549,7 +38639,10 @@ const planAndBuild = async (firstBase, firstLap = "first") => {
|
|
|
38549
38639
|
await design(base);
|
|
38550
38640
|
await architecture();
|
|
38551
38641
|
await packages();
|
|
38552
|
-
const outcome = await buildTail(
|
|
38642
|
+
const outcome = await buildTail(base, {
|
|
38643
|
+
green: true,
|
|
38644
|
+
...lap === "first" ? {} : { lap }
|
|
38645
|
+
});
|
|
38553
38646
|
if (outcome.verdict === "signoff") return;
|
|
38554
38647
|
await reUnwind(outcome);
|
|
38555
38648
|
base = outcome.base;
|
|
@@ -38561,8 +38654,14 @@ const naivePlanAndBuild = async (firstBase) => {
|
|
|
38561
38654
|
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
38562
38655
|
const seam = await seamBuild(firstBase);
|
|
38563
38656
|
await slice("feature", async () => {
|
|
38564
|
-
await implement(
|
|
38565
|
-
|
|
38657
|
+
await implement({
|
|
38658
|
+
reference: seam.reference,
|
|
38659
|
+
tier
|
|
38660
|
+
});
|
|
38661
|
+
await featureFeedback(await buildTail(seam.base, {
|
|
38662
|
+
green: true,
|
|
38663
|
+
lensBase: seam.base
|
|
38664
|
+
}), seam.base);
|
|
38566
38665
|
});
|
|
38567
38666
|
};
|
|
38568
38667
|
const featureFeedback = async (first, lensBase) => {
|
|
@@ -38570,8 +38669,12 @@ const featureFeedback = async (first, lensBase) => {
|
|
|
38570
38669
|
while (outcome.verdict === "feedback") {
|
|
38571
38670
|
await reUnwind(outcome);
|
|
38572
38671
|
await design(outcome.base);
|
|
38573
|
-
await implement(
|
|
38574
|
-
|
|
38672
|
+
await implement({
|
|
38673
|
+
tier: riskTierOn() ? recordRisk() : void 0,
|
|
38674
|
+
amend: true
|
|
38675
|
+
});
|
|
38676
|
+
outcome = await buildTail(outcome.base, {
|
|
38677
|
+
green: true,
|
|
38575
38678
|
lap: "feedback",
|
|
38576
38679
|
...lensBase === void 0 ? {} : { lensBase }
|
|
38577
38680
|
});
|
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ export {
|
|
|
56
56
|
export default feature
|
|
57
57
|
|
|
58
58
|
export async function hotfix() {
|
|
59
|
-
return afterTail(await buildTail(
|
|
59
|
+
return afterTail(await buildTail(start(), { fixFirst: true }))
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export const doors = { hotfix: { workflow: "hotfix" } }
|
|
@@ -227,9 +227,11 @@ pass (which writes `.gtd/packages/`), then builds the packages. Add a human
|
|
|
227
227
|
sign-off between the two:
|
|
228
228
|
|
|
229
229
|
```ts
|
|
230
|
-
const planAndBuild = async (): Promise<void> => {
|
|
230
|
+
const planAndBuild = async (firstBase: string): Promise<void> => {
|
|
231
|
+
let base = firstBase
|
|
232
|
+
let lap: "first" | "feedback" = "first"
|
|
231
233
|
for (;;) {
|
|
232
|
-
await design()
|
|
234
|
+
await design(base)
|
|
233
235
|
await architecture()
|
|
234
236
|
await human("approve-plan", {
|
|
235
237
|
message:
|
|
@@ -238,8 +240,14 @@ const planAndBuild = async (): Promise<void> => {
|
|
|
238
240
|
acceptClean: true,
|
|
239
241
|
})
|
|
240
242
|
await packages()
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
const outcome = await buildTail(base, {
|
|
244
|
+
green: true,
|
|
245
|
+
...(lap === "first" ? {} : { lap }),
|
|
246
|
+
})
|
|
247
|
+
if (outcome.verdict === "signoff") return
|
|
248
|
+
await reUnwind(outcome)
|
|
249
|
+
base = outcome.base
|
|
250
|
+
lap = "feedback"
|
|
243
251
|
}
|
|
244
252
|
}
|
|
245
253
|
```
|
|
@@ -131,7 +131,7 @@ describe("afterTail feedback lap", () => {
|
|
|
131
131
|
`export { feature as default, defaults, envDefaults, summary, base, steering, skills, access } from "@pmelab/gtd/workflows"`,
|
|
132
132
|
`export async function tail() {`,
|
|
133
133
|
` await gate("review-gate", "The baseline is red.")`,
|
|
134
|
-
` return afterTail(await buildTail(
|
|
134
|
+
` return afterTail(await buildTail(start()))`,
|
|
135
135
|
`}`,
|
|
136
136
|
].join("\n")
|
|
137
137
|
|
|
@@ -39,7 +39,7 @@ export const planAndBuild = async (
|
|
|
39
39
|
await design(base)
|
|
40
40
|
await architecture()
|
|
41
41
|
await packages()
|
|
42
|
-
const outcome = await buildTail(
|
|
42
|
+
const outcome = await buildTail(base, { green: true, ...(lap === "first" ? {} : { lap }) })
|
|
43
43
|
if (outcome.verdict === "signoff") return
|
|
44
44
|
await reUnwind(outcome)
|
|
45
45
|
base = outcome.base
|
|
@@ -54,9 +54,9 @@ const naivePlanAndBuild = async (firstBase: string): Promise<void> => {
|
|
|
54
54
|
const tier = riskTierOn() ? recordRisk() : undefined
|
|
55
55
|
const seam = await seamBuild(firstBase)
|
|
56
56
|
await slice("feature", async () => {
|
|
57
|
-
await implement(seam.reference, tier)
|
|
57
|
+
await implement({ reference: seam.reference, tier })
|
|
58
58
|
await featureFeedback(
|
|
59
|
-
await buildTail(
|
|
59
|
+
await buildTail(seam.base, { green: true, lensBase: seam.base }),
|
|
60
60
|
seam.base,
|
|
61
61
|
)
|
|
62
62
|
})
|
|
@@ -68,8 +68,9 @@ const featureFeedback = async (first: ReviewOutcome, lensBase?: string): Promise
|
|
|
68
68
|
await reUnwind(outcome)
|
|
69
69
|
await design(outcome.base)
|
|
70
70
|
const tier = riskTierOn() ? recordRisk() : undefined
|
|
71
|
-
await implement(
|
|
72
|
-
outcome = await buildTail(
|
|
71
|
+
await implement({ tier, amend: true })
|
|
72
|
+
outcome = await buildTail(outcome.base, {
|
|
73
|
+
green: true,
|
|
73
74
|
lap: "feedback",
|
|
74
75
|
...(lensBase === undefined ? {} : { lensBase }),
|
|
75
76
|
})
|
|
@@ -5,7 +5,7 @@ export * from "./planning.js"
|
|
|
5
5
|
export * from "./packages.js"
|
|
6
6
|
export * from "./refactor.js"
|
|
7
7
|
export * from "./seam.js"
|
|
8
|
-
export { buildTail, type ReviewOutcome } from "./review.js"
|
|
8
|
+
export { buildTail, type BuildTailOptions, type ReviewOutcome } from "./review.js"
|
|
9
9
|
export * from "./feature.js"
|
|
10
10
|
export * from "../shared/reserved.js"
|
|
11
11
|
|