@pmelab/gtd 43.0.0 → 45.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +33 -13
- package/dist/chunks/{reserved-COEMFEpy.mjs → reserved-ty1mtlnS.mjs} +155 -3
- package/dist/chunks/review-BmoH-9im.mjs +408 -0
- package/dist/chunks/{tail-C_xn8U7U.mjs → tail-CM3sVoAb.mjs} +3 -3
- package/dist/workflow-kit.mjs +22 -10
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +6 -278
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +27 -7
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +379 -66
- package/package.json +1 -1
- package/src/steering/FORMATS.md +4 -4
- package/src/workflow-kit/review.test.ts +80 -2
- package/src/workflow-kit/review.ts +45 -11
- package/src/workflows/feature/coverage.ts +1 -1
- package/src/workflows/feature/feature.test.ts +8 -0
- package/src/workflows/feature/feature.ts +2 -2
- package/src/workflows/feature/index.ts +1 -1
- package/src/workflows/feature/refactor.ts +1 -1
- package/src/workflows/feature/review-answers.feature +12 -3
- package/src/workflows/feature/seam.ts +1 -1
- package/src/workflows/feature/standards.feature +286 -0
- package/src/workflows/feature/steps.test.ts +2 -64
- package/src/workflows/feature/steps.ts +5 -41
- package/src/workflows/fix/fix.feature +20 -21
- package/src/workflows/index.ts +1 -1
- package/src/workflows/review/review-door.feature +158 -0
- package/src/workflows/review/review.test.ts +1 -73
- package/src/workflows/review/review.ts +34 -6
- package/src/workflows/shared/learn.test.ts +154 -0
- package/src/workflows/shared/learn.ts +206 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/{feature → shared}/review.test.ts +4 -8
- package/src/workflows/{feature → shared}/review.ts +23 -10
- package/src/workflows/shared/skills.test.ts +2 -0
- package/src/workflows/shared/skills.ts +2 -0
- package/src/workflows/shared/standards.test.ts +90 -0
- package/src/workflows/shared/standards.ts +156 -0
- package/src/workflows/shared/steps.test.ts +64 -2
- package/src/workflows/shared/steps.ts +40 -1
- package/src/workflows/shared/tail.ts +2 -2
- package/src/workflows/shared/text.test.ts +58 -1
- package/src/workflows/shared/text.ts +39 -1
- package/src/workflows/shared/vars.ts +2 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { agent, answered, applyScript, changes, changesSince, check, codeChanges, env, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, matches,
|
|
2
|
-
import { lensEntriesOf
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as refusePendingHandOff,
|
|
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
|
+
import { lensEntriesOf } from "../workflow-kit.mjs";
|
|
3
|
+
import { $ as naiveApplyFailure, B as coverageMessage, C as coder, Ct as unwindFailure, E as fixNits, Et as testFileGlobs, G as designSystem, H as coveringPrompt, I as builderSystem, K as designTriagePrompt, L as clearCriteriaMessage, N as agentWithSkills, P as buildReviewCollectingPrompt, Q as implementFixPrompt, R as clearCriteriaQuestion, St as unwindFailedMessage, T as fixCheck, Tt as envDefaults, U as criterionLine, V as coverageQuestion, W as designGateAnswerMessage, X as idleMessage, Z as implementBuildingPrompt, _ as REFACTOR_NOTE, _t as seamByHandMessage, a as flagModeOn, at as qualityUnresolvedMessage, b as SLICES, bt as startGateBlockedMessage, ct as refactorFixPrompt, et as naivePassPrompt, f as healthy, g as REFACTOR, gt as reviewerSystem, h as QUALITY, i as skills, k as planner, lt as refactorNotePrompt, m as HANDOFF, n as summary, nt as oversizedMessage, ot as refactorBuildingPrompt, p as FEEDBACK, r as access, s as shipped, st as refactorDroppedRefusal, t as steering, tt as normaliseCriteriaScript, u as gate, ut as refactorRules, v as REQUIREMENTS, vt as seamSteerMessage, w as collecting, wt as defaults, x as answerReviewQuestions, xt as uncoveredMessage, yt as slicingPrompt } from "../chunks/reserved-ty1mtlnS.mjs";
|
|
4
|
+
import { a as refusePendingHandOff, o as tail } from "../chunks/tail-CM3sVoAb.mjs";
|
|
5
|
+
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-BmoH-9im.mjs";
|
|
5
6
|
import { posix } from "node:path";
|
|
6
7
|
//#region src/workflows/shared/fences.ts
|
|
7
8
|
const markFenced = (text) => {
|
|
@@ -219,36 +220,6 @@ const answerProductQuestions = () => human("gate.answer", {
|
|
|
219
220
|
mode: "qa",
|
|
220
221
|
acceptClean: true
|
|
221
222
|
});
|
|
222
|
-
/** Answer every `question` note inline in `.gtd/REVIEW.md`. */
|
|
223
|
-
const answerReviewQuestions = (notes) => agentWithSkills("review.answer-review-questions", buildReviewAnswerQuestionsPrompt(notes), {
|
|
224
|
-
label: "Answering your questions",
|
|
225
|
-
file: REVIEW,
|
|
226
|
-
mode: "review",
|
|
227
|
-
model: planner(),
|
|
228
|
-
system: reviewerSystem()
|
|
229
|
-
});
|
|
230
|
-
/** Fix every `nit` note in one turn. */
|
|
231
|
-
const fixNits = (notes, access, rules) => {
|
|
232
|
-
const fixing = (name) => agentWithSkills(name, buildReviewFixNitsPrompt(notes, rules), {
|
|
233
|
-
label: "Fixing your nits",
|
|
234
|
-
file: REVIEW,
|
|
235
|
-
model: planner(),
|
|
236
|
-
system: reviewerSystem()
|
|
237
|
-
});
|
|
238
|
-
return access === void 0 ? fixing("review.fix.nits.fixing") : scope({
|
|
239
|
-
name: "review.fix.nits",
|
|
240
|
-
access
|
|
241
|
-
}, () => fixing("fixing"));
|
|
242
|
-
};
|
|
243
|
-
/** Turn the review round `capture` describes into `.gtd/REQUIREMENTS.md`, or change nothing. */
|
|
244
|
-
const collecting = (capture, round) => agentWithSkills("review.collecting", buildReviewCollectingPrompt(capture, void 0, round), {
|
|
245
|
-
label: "Collecting your feedback",
|
|
246
|
-
file: REQUIREMENTS,
|
|
247
|
-
mode: "qa",
|
|
248
|
-
model: planner(),
|
|
249
|
-
system: reviewerSystem(),
|
|
250
|
-
allowEmpty: true
|
|
251
|
-
});
|
|
252
223
|
/** Turn the refactor review round `capture` describes into `.gtd/REFACTOR.md`, or change nothing. */
|
|
253
224
|
const refactorCollecting = (capture, round, strays) => agentWithSkills("review.collecting", buildReviewCollectingPrompt(capture, {
|
|
254
225
|
strays,
|
|
@@ -286,249 +257,6 @@ const design = (base, guard) => scope("design", async () => {
|
|
|
286
257
|
} while (await noteGate(REQUIREMENTS, answerProductQuestions));
|
|
287
258
|
});
|
|
288
259
|
//#endregion
|
|
289
|
-
//#region src/workflows/feature/review.ts
|
|
290
|
-
const VERDICTS = [
|
|
291
|
-
"edit",
|
|
292
|
-
"question",
|
|
293
|
-
"nit",
|
|
294
|
-
"praise"
|
|
295
|
-
];
|
|
296
|
-
/** One four-way choice per note the human added to `.gtd/REVIEW.md`. */
|
|
297
|
-
const verdictQuestion = (note) => ({
|
|
298
|
-
id: note.id,
|
|
299
|
-
primitive: "choice",
|
|
300
|
-
instructions: `Classify the human's note on review item "${note.anchor}" (in .gtd/REVIEW.md). Its evidence holds the anchor, the reviewer's text and the human's text.`,
|
|
301
|
-
criteria: "edit: a request to change behaviour, design or scope — anything needing a plan, or any note you are unsure about. question: asks something and wants an answer, with no change requested. nit: a small, local, unambiguous fix (naming, typo, formatting, comment wording) needing no re-plan. praise: an approving remark with nothing to do."
|
|
302
|
-
});
|
|
303
|
-
const TIER_ORDER = {
|
|
304
|
-
critical: 0,
|
|
305
|
-
should: 1,
|
|
306
|
-
could: 2
|
|
307
|
-
};
|
|
308
|
-
/** Capture order: critical, should, could; within a tier one-way doors, then new concepts. */
|
|
309
|
-
const captureKey = (s) => s === void 0 ? 2 : TIER_ORDER[s.tier] * 3 + s.rank;
|
|
310
|
-
const SEVERITY_ANSWERS = {
|
|
311
|
-
security: ["yes", "no"],
|
|
312
|
-
reversibility: ["one-way", "two-way"],
|
|
313
|
-
blastRadius: [
|
|
314
|
-
"local",
|
|
315
|
-
"module",
|
|
316
|
-
"contract"
|
|
317
|
-
],
|
|
318
|
-
newConcept: ["yes", "no"]
|
|
319
|
-
};
|
|
320
|
-
/** Anything unanswered, unrecognised, cut or below `minP` is `critical`, never `could`. */
|
|
321
|
-
const severityOf = (note, judgment, minP) => {
|
|
322
|
-
const ans = (q) => judgment.answers[`${note.id}.${q}`];
|
|
323
|
-
const a = (q) => ans(q)?.answer;
|
|
324
|
-
const rank = () => a("reversibility") === "one-way" ? 0 : a("newConcept") === "yes" ? 1 : 2;
|
|
325
|
-
if (note.forced) return {
|
|
326
|
-
tier: "critical",
|
|
327
|
-
reason: "critical: forced by `!`",
|
|
328
|
-
rank: 2
|
|
329
|
-
};
|
|
330
|
-
if (judgment.truncated.includes(note.id) || Object.entries(SEVERITY_ANSWERS).some(([q, allowed]) => !allowed.includes(a(q) ?? "") || !((ans(q)?.p ?? NaN) >= minP))) return {
|
|
331
|
-
tier: "critical",
|
|
332
|
-
reason: "critical: judge unsure",
|
|
333
|
-
rank: 2
|
|
334
|
-
};
|
|
335
|
-
const critical = [...a("security") === "yes" ? ["security"] : [], ...a("reversibility") === "one-way" ? ["one-way door"] : []];
|
|
336
|
-
if (critical.length > 0) return {
|
|
337
|
-
tier: "critical",
|
|
338
|
-
reason: `critical: ${critical.join(", ")}`,
|
|
339
|
-
rank: rank()
|
|
340
|
-
};
|
|
341
|
-
const should = [...a("blastRadius") === "contract" ? ["contract"] : [], ...a("newConcept") === "yes" ? ["new concept"] : []];
|
|
342
|
-
if (should.length > 0) return {
|
|
343
|
-
tier: "should",
|
|
344
|
-
reason: `should: ${should.join(", ")}`,
|
|
345
|
-
rank: rank()
|
|
346
|
-
};
|
|
347
|
-
return {
|
|
348
|
-
tier: "could",
|
|
349
|
-
reason: "could",
|
|
350
|
-
rank: 2
|
|
351
|
-
};
|
|
352
|
-
};
|
|
353
|
-
const severityQuestions = (note) => [
|
|
354
|
-
{
|
|
355
|
-
id: `${note.id}.security`,
|
|
356
|
-
primitive: "noul",
|
|
357
|
-
instructions: `Does the change the human asks for in note "${note.id}" (on "${note.anchor}") touch security — authentication, authorisation, secrets, untrusted input or data exposure?`,
|
|
358
|
-
criteria: "yes: it changes or could weaken a security property. no: it does not."
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
id: `${note.id}.reversibility`,
|
|
362
|
-
primitive: "choice",
|
|
363
|
-
instructions: `Once the change in note "${note.id}" ships, can it be undone without breaking anyone who already depends on it?`,
|
|
364
|
-
criteria: "one-way: a published API, file format, persisted data, protocol or other contract that cannot be taken back. two-way: can be reverted freely."
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
id: `${note.id}.blastRadius`,
|
|
368
|
-
primitive: "choice",
|
|
369
|
-
instructions: `How far does the change in note "${note.id}" reach?`,
|
|
370
|
-
criteria: "local: one function or file. module: a whole module or several files. contract: a public interface other code or users depend on."
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
id: `${note.id}.newConcept`,
|
|
374
|
-
primitive: "noul",
|
|
375
|
-
instructions: `Does the change in note "${note.id}" introduce a concept new to this codebase — a new abstraction, entity or term? Judge from the evidence, never a glossary; reusing or extending an existing concept is not new.`,
|
|
376
|
-
criteria: "yes: a genuinely new concept. no: reuses or extends an existing one."
|
|
377
|
-
}
|
|
378
|
-
];
|
|
379
|
-
/**
|
|
380
|
-
* One judge rest: a verdict per unforced note (a forced note is asked
|
|
381
|
-
* nothing) plus four severity answers. Dismissing a note is the risky
|
|
382
|
-
* direction, so only a confident non-`edit` verdict on uncut evidence counts;
|
|
383
|
-
* a cut, unanswered or below-floor note is an `edit`. Severity answers on a
|
|
384
|
-
* non-`edit` note are ignored; on an `edit` they order the collecting turn.
|
|
385
|
-
*/
|
|
386
|
-
const triage$1 = (severities) => async (notes) => {
|
|
387
|
-
const asked = notes.filter((n) => !n.forced);
|
|
388
|
-
const judgment = asked.length === 0 ? {
|
|
389
|
-
answers: {},
|
|
390
|
-
truncated: []
|
|
391
|
-
} : await judge("review.triage", {
|
|
392
|
-
questions: asked.flatMap((n) => [verdictQuestion(n), ...severityQuestions(n)]),
|
|
393
|
-
evidence: Object.fromEntries(asked.map((n) => [n.id, `Anchor: ${n.anchor}\nReviewer's text: ${n.before}\nHuman's text: ${n.text}`])),
|
|
394
|
-
message: buildReviewTriageMessage(),
|
|
395
|
-
label: "Judging each note"
|
|
396
|
-
});
|
|
397
|
-
const { answers, truncated } = judgment;
|
|
398
|
-
const minP = numeric(vars.reviewNoteActionable, Infinity);
|
|
399
|
-
const severityMinP = numeric(vars.reviewSeverityMinP, Infinity);
|
|
400
|
-
severities.clear();
|
|
401
|
-
return new Map(notes.map((n) => {
|
|
402
|
-
const verdict = n.forced ? void 0 : VERDICTS.find((v) => v !== "edit" && answered(answers[n.id], v, minP));
|
|
403
|
-
if (verdict !== void 0 && !truncated.includes(n.id)) return [n.id, verdict];
|
|
404
|
-
severities.set(n.id, severityOf(n, judgment, severityMinP));
|
|
405
|
-
return [n.id, "edit"];
|
|
406
|
-
}));
|
|
407
|
-
};
|
|
408
|
-
const isCode$1 = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
409
|
-
/**
|
|
410
|
-
* gtd writes `.gtd/REVIEW.md` from the quality lap's `report` — no reviewer
|
|
411
|
-
* turn — and removes what the fixer left in `.gtd/QUALITY.md`. A re-review
|
|
412
|
-
* after fixed nits restores the doc as committed at `carry`, answers kept;
|
|
413
|
-
* a fresh one leaves out the notes the human already answered.
|
|
414
|
-
*/
|
|
415
|
-
const composing = (base, report, carry, nitFix, answered = /* @__PURE__ */ new Set()) => run("review.composing", carry === void 0 ? writeScript(REVIEW, composedReview(base, {
|
|
416
|
-
...report,
|
|
417
|
-
weakened: removedAssertions(changesSince(base)),
|
|
418
|
-
nitFix,
|
|
419
|
-
answered
|
|
420
|
-
}), [QUALITY]) : showScript(carry, REVIEW, REVIEW), {
|
|
421
|
-
label: "Composing the review",
|
|
422
|
-
file: REVIEW,
|
|
423
|
-
base
|
|
424
|
-
});
|
|
425
|
-
/**
|
|
426
|
-
* The quality lap's notes reach a human over everything since `base`, who
|
|
427
|
-
* answers each one — fix as suggested, won't fix, or their own answer — and
|
|
428
|
-
* may comment. A fix joins the nit batch, a won't-fix is dropped, and an own
|
|
429
|
-
* answer or comment is judged: edits go to another planning lap, questions
|
|
430
|
-
* are answered in place, nits fixed in one batch, praise dropped.
|
|
431
|
-
*/
|
|
432
|
-
const review = (base, escalations = { rounds: 0 }, options = {}) => {
|
|
433
|
-
const { guard, guardRests, fixAccess, rules } = options;
|
|
434
|
-
const feedback = options.feedback ?? {
|
|
435
|
-
file: ".gtd/REQUIREMENTS.md",
|
|
436
|
-
collecting,
|
|
437
|
-
unmatched: "gtd land: no declared pattern matches the pending changes — write .gtd/REQUIREMENTS.md from the feedback, or change nothing when it asks for nothing"
|
|
438
|
-
};
|
|
439
|
-
const severities = /* @__PURE__ */ new Map();
|
|
440
|
-
let nitFix;
|
|
441
|
-
const answered = /* @__PURE__ */ new Set();
|
|
442
|
-
return review$1(base, {
|
|
443
|
-
file: REVIEW,
|
|
444
|
-
feedback: feedback.file,
|
|
445
|
-
isCode: isCode$1,
|
|
446
|
-
reviewing: (b, carry) => {
|
|
447
|
-
const fixedAt = nitFix;
|
|
448
|
-
nitFix = void 0;
|
|
449
|
-
return composing(b, options.report ?? NO_FINDINGS, carry, fixedAt, answered);
|
|
450
|
-
},
|
|
451
|
-
fixRisks: guarded((notes) => fixRisks(notes, fixAccess, rules), guard),
|
|
452
|
-
keepGreen: () => healthy(() => fixCheck(rules), {
|
|
453
|
-
escalations,
|
|
454
|
-
guard,
|
|
455
|
-
guardRests,
|
|
456
|
-
rules
|
|
457
|
-
}),
|
|
458
|
-
awaitReview: (b) => awaitReview(b),
|
|
459
|
-
reviewMissing,
|
|
460
|
-
collecting: feedback.collecting,
|
|
461
|
-
triage: triage$1(severities),
|
|
462
|
-
decided: (decisions) => decisions.forEach((d) => answered.add(d.anchor)),
|
|
463
|
-
answerQuestions: answerReviewQuestions,
|
|
464
|
-
fixNits: async (notes) => {
|
|
465
|
-
await guarded((n) => fixNits(n, fixAccess, rules), guard)(notes);
|
|
466
|
-
nitFix = head();
|
|
467
|
-
},
|
|
468
|
-
close: closeReview,
|
|
469
|
-
captures: {
|
|
470
|
-
edits: reviewEditsCapture,
|
|
471
|
-
notes: reviewNotesCapture,
|
|
472
|
-
editNotes: (round, edits, answeredAt) => reviewEditNotesCapture(round, edits.map((n, i) => ({
|
|
473
|
-
n,
|
|
474
|
-
i,
|
|
475
|
-
s: severities.get(n.id),
|
|
476
|
-
key: captureKey(severities.get(n.id))
|
|
477
|
-
})).sort((x, y) => x.key - y.key || x.i - y.i).map(({ n, s }) => ({
|
|
478
|
-
id: n.id,
|
|
479
|
-
anchor: n.anchor,
|
|
480
|
-
text: n.forced ? n.text.replace(/^!\s*/, "") : n.text,
|
|
481
|
-
...s === void 0 ? {} : { label: s.reason }
|
|
482
|
-
})), answeredAt)
|
|
483
|
-
},
|
|
484
|
-
refusals: {
|
|
485
|
-
deleted: "gtd land: review-doc: .gtd/REVIEW.md was deleted — restore it, or leave a note (or edit code) to request changes.",
|
|
486
|
-
unmatched: feedback.unmatched,
|
|
487
|
-
...feedback.dropped === void 0 ? {} : { dropped: feedback.dropped }
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
};
|
|
491
|
-
function buildTail(first, second, ...rest) {
|
|
492
|
-
const options = callForm(first, second, rest);
|
|
493
|
-
if (options === void 0) return Promise.reject(/* @__PURE__ */ new Error("buildTail takes buildTail(base, options?) or the deprecated buildTail(fixFirst, base, green?, options?); this call fits neither"));
|
|
494
|
-
const [base, form] = options;
|
|
495
|
-
return tail((escalations, report) => review(base, escalations, {
|
|
496
|
-
...form,
|
|
497
|
-
report
|
|
498
|
-
}), {
|
|
499
|
-
...form,
|
|
500
|
-
triage: true
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
const isOptionalBoolean = (value) => value == null || typeof value === "boolean";
|
|
504
|
-
const legacyForm = (fixFirst, base, green, options) => isOptionalBoolean(fixFirst) && isOptionalBoolean(green) && isOptionalObject(options) ? [base, {
|
|
505
|
-
...options,
|
|
506
|
-
fixFirst: fixFirst ?? void 0,
|
|
507
|
-
green: green ?? void 0
|
|
508
|
-
}] : void 0;
|
|
509
|
-
const currentForm = (base, options, third, fourth) => isOptionalObject(options) && third == null && fourth == null ? [base, options ?? {}] : void 0;
|
|
510
|
-
const callForm = (first, second, [third, fourth, ...extra]) => {
|
|
511
|
-
if (extra.some((value) => value != null)) return void 0;
|
|
512
|
-
if (typeof second === "string") return legacyForm(first, second, third, fourth);
|
|
513
|
-
if (typeof first === "string") return currentForm(first, second, third, fourth);
|
|
514
|
-
};
|
|
515
|
-
/**
|
|
516
|
-
* Undo the human's review-round code edits. A path changed since the human
|
|
517
|
-
* left it is not overwritten; requireRevert names it instead.
|
|
518
|
-
*/
|
|
519
|
-
const reUnwind = async (feedback) => {
|
|
520
|
-
const { base, restoreFrom, edited } = feedback;
|
|
521
|
-
await run("re-unwind", restoreScript(base, {
|
|
522
|
-
restore: edited.filter((c) => c.status !== "added").map((c) => c.path),
|
|
523
|
-
remove: edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
524
|
-
}, restoreFrom), {
|
|
525
|
-
label: "Re-unwinding your review edit",
|
|
526
|
-
file: REVIEW,
|
|
527
|
-
base
|
|
528
|
-
});
|
|
529
|
-
requireRevert(edited, base);
|
|
530
|
-
};
|
|
531
|
-
//#endregion
|
|
532
260
|
//#region src/workflows/shared/continuation.ts
|
|
533
261
|
const phase = (name, steered) => ({
|
|
534
262
|
name,
|
|
@@ -1445,7 +1173,7 @@ const settleQuality = async () => {
|
|
|
1445
1173
|
if (rested) await healthy(() => fixCheck());
|
|
1446
1174
|
};
|
|
1447
1175
|
/** The triaged quality lap, then the human review since `base`. */
|
|
1448
|
-
const reviewedTail = (base, lensBase) => tail((escalations, report) =>
|
|
1176
|
+
const reviewedTail = (base, lensBase) => tail((escalations, report) => humanReview(base, escalations, { report }), {
|
|
1449
1177
|
green: true,
|
|
1450
1178
|
lensBase,
|
|
1451
1179
|
triage: true
|
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 { g as reviewRisks, h as reviewNotes } from "../chunks/steering-D4LUQ-av.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { C as coder, J as fixReviewNotesPrompt, N as agentWithSkills, Tt as envDefaults, c as baseline, i as skills, n as summary, q as finisherSystem, r as access, s as shipped, t as steering, wt as defaults, y as REVIEW } from "../chunks/reserved-ty1mtlnS.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-CM3sVoAb.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,16 +1,36 @@
|
|
|
1
|
-
import { start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as refusePendingHandOff
|
|
1
|
+
import { read, refuse, removeScript, run, start } from "../flows.mjs";
|
|
2
|
+
import { C as coder, N as agentWithSkills, Tt as envDefaults, i as skills, mt as reviewGateBlockedMessage, n as summary, pt as reviewFeedbackFixPrompt, q as finisherSystem, r as access, s as shipped, t as steering, u as gate, v as REQUIREMENTS, wt as defaults } from "../chunks/reserved-ty1mtlnS.mjs";
|
|
3
|
+
import { a as refusePendingHandOff } from "../chunks/tail-CM3sVoAb.mjs";
|
|
4
|
+
import { i as reUnwind, t as buildTail } from "../chunks/review-BmoH-9im.mjs";
|
|
4
5
|
//#region src/workflows/review/review.ts
|
|
6
|
+
/** Build the concerns a review round collected into `.gtd/REQUIREMENTS.md`, then remove it. */
|
|
7
|
+
const fixFeedback = async () => {
|
|
8
|
+
await agentWithSkills("feedback.fixing", reviewFeedbackFixPrompt(), {
|
|
9
|
+
label: "Fixing your review feedback",
|
|
10
|
+
file: REQUIREMENTS,
|
|
11
|
+
model: coder(),
|
|
12
|
+
system: finisherSystem(),
|
|
13
|
+
allowEmpty: true
|
|
14
|
+
});
|
|
15
|
+
await run("feedback.closing", removeScript([REQUIREMENTS]), { label: "Closing the feedback" });
|
|
16
|
+
};
|
|
5
17
|
/**
|
|
6
|
-
*
|
|
7
|
-
* lap, then the human review
|
|
8
|
-
*
|
|
18
|
+
* The feature's review of everything since `reviewBase` — a green gate, the
|
|
19
|
+
* triaged quality lap, then the human review — with no design phase: a
|
|
20
|
+
* review that asks for changes is fixed in place and reviewed again, until
|
|
21
|
+
* sign-off.
|
|
9
22
|
*/
|
|
10
23
|
const reviewFlow = async () => {
|
|
11
24
|
refusePendingHandOff();
|
|
25
|
+
if (read(".gtd/REQUIREMENTS.md") !== void 0) refuse(`gtd: ${REQUIREMENTS} is left over — finish or remove it before a review.`);
|
|
12
26
|
await gate("review-gate", reviewGateBlockedMessage());
|
|
13
|
-
|
|
27
|
+
const base = start();
|
|
28
|
+
let outcome = await buildTail(base, { green: true });
|
|
29
|
+
while (outcome.verdict === "feedback") {
|
|
30
|
+
await reUnwind(outcome);
|
|
31
|
+
await fixFeedback();
|
|
32
|
+
outcome = await buildTail(base, { lensBase: outcome.base });
|
|
33
|
+
}
|
|
14
34
|
};
|
|
15
35
|
const review = shipped(reviewFlow, { review: {
|
|
16
36
|
args: [{
|
|
@@ -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 { Bt as requireFlag, C as coder, It as flagMentions, Rt as flipProblems, Tt 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 defaults } from "../chunks/reserved-ty1mtlnS.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.";
|