@pmelab/gtd 53.0.0 → 54.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/dist/chunks/{review-CNgN_V8T.mjs → review-CXzUXJHL.mjs} +10 -3
- package/dist/workflows/feature.mjs +1 -1
- package/dist/workflows/review.mjs +1 -1
- package/dist/workflows.mjs +10 -3
- package/package.json +1 -1
- package/src/testkit/steps.ts +23 -0
- package/src/workflows/feature/lens-review.feature +50 -0
- package/src/workflows/feature/refactor-review.feature +38 -0
- package/src/workflows/feature/reviewer-walkthrough.feature +0 -31
- package/src/workflows/shared/review.ts +12 -2
|
@@ -387,13 +387,14 @@ const requirePointers = (review, notes, codeChanged) => {
|
|
|
387
387
|
* pointing at its code and each of the lap's notes a chunk of its own; gtd
|
|
388
388
|
* then adds each note's text and answer boxes, and the summary chunks.
|
|
389
389
|
*/
|
|
390
|
-
const walkthrough = async (base, report, lap, carry) => {
|
|
390
|
+
const walkthrough = async (base, report, lap, carry, afterRiskFix) => {
|
|
391
391
|
const notes = titledNotes(report);
|
|
392
392
|
await reviewing(base, carry, lap, walkthroughBlock(notes, report.nitFix));
|
|
393
393
|
const written = read(REVIEW);
|
|
394
394
|
if (written === void 0) return;
|
|
395
395
|
const review = unranked(written);
|
|
396
396
|
requirePointers(written, notes, changesSince(base).some((c) => isCode(c.path)));
|
|
397
|
+
if (!afterRiskFix && reviewRisks(written).length > 0) return;
|
|
397
398
|
const full = sectionedReview(await ranked(review, withWalkthrough(review, report), new Set(notes.map((n) => n.title))), new Set(reviewChunks(review).map((c) => c.title)));
|
|
398
399
|
if (full !== written) await run("review.carrying", writeScript(REVIEW, full), {
|
|
399
400
|
label: "Ordering the review and adding the quality lap's findings",
|
|
@@ -419,16 +420,22 @@ const humanReview = (base, escalations = { rounds: 0 }, options = {}) => {
|
|
|
419
420
|
const severities = /* @__PURE__ */ new Map();
|
|
420
421
|
let nitFix;
|
|
421
422
|
const answered = /* @__PURE__ */ new Set();
|
|
423
|
+
let riskFixed = false;
|
|
422
424
|
return review(base, {
|
|
423
425
|
file: REVIEW,
|
|
424
426
|
feedback: feedback.file,
|
|
425
427
|
isCode,
|
|
426
428
|
reviewing: (b, carry) => {
|
|
427
429
|
const fixedAt = nitFix;
|
|
430
|
+
const afterRiskFix = riskFixed;
|
|
431
|
+
riskFixed = false;
|
|
428
432
|
const report = options.report ?? NO_FINDINGS;
|
|
429
|
-
return options.walkthrough === true ? guarded(() => walkthrough(b, composedOf(b, report, fixedAt, answered), options.lap ?? "first", carry), guard)() : composing(b, report, carry, fixedAt, answered);
|
|
433
|
+
return options.walkthrough === true ? guarded(() => walkthrough(b, composedOf(b, report, fixedAt, answered), options.lap ?? "first", carry, afterRiskFix), guard)() : composing(b, report, carry, fixedAt, answered);
|
|
430
434
|
},
|
|
431
|
-
fixRisks: guarded((notes) =>
|
|
435
|
+
fixRisks: guarded((notes) => {
|
|
436
|
+
riskFixed = true;
|
|
437
|
+
return fixRisks(notes, fixAccess, rules);
|
|
438
|
+
}, guard),
|
|
432
439
|
keepGreen: () => healthy(() => fixCheck(rules), {
|
|
433
440
|
escalations,
|
|
434
441
|
guard,
|
|
@@ -2,7 +2,7 @@ import { agent, answered, applyScript, changes, changesSince, check, codeChanges
|
|
|
2
2
|
import { lensEntriesOf } from "../workflow-kit.mjs";
|
|
3
3
|
import { $ as observableMessage, B as designGateAnswerMessage, Ct as testerSystem, D as planner, Dt as unwindFailure, Et as unwindFailedMessage, H as designSystem, I as clearCriteriaMessage, J as implementBuildingPrompt, L as clearCriteriaQuestion, M as buildReviewCollectingPrompt, Mt as envDefaults, Nt as testFileGlobs, P as builderSystem, Q as normaliseCriteriaScript, S as collecting, St as startGateBlockedMessage, U as designTriagePrompt, V as designImpactPrompt, X as naiveApplyFailure, Y as implementFixPrompt, Z as naivePassPrompt, _ as REQUIREMENTS, a as flagModeOn, bt as seamByHandMessage, ct as refactorBuildingPrompt, d as healthy, dt as refactorNotePrompt, et as observableQuestion, f as FEEDBACK, ft as refactorRules, g as REFACTOR_NOTE, h as REFACTOR, i as skills, it as prototypeUnguidedMessage, j as agentWithSkills, jt as defaults, l as gate, lt as refactorDroppedRefusal, n as summary, nt as prototypeOpenMessage, p as PROTOTYPE, q as idleMessage, r as access, rt as prototypeTestingPrompt, s as shipped, t as steering, tt as prototypeFixingPrompt, ut as refactorFixPrompt, w as fixNits, x as coder, xt as seamSteerMessage, y as answerReviewQuestions, yt as reviewerSystem, z as criterionLine } from "../chunks/reserved-CQ7amcKG.mjs";
|
|
4
4
|
import { a as refusePendingHandOff, o as tail } from "../chunks/tail-JZ9PAOwp.mjs";
|
|
5
|
-
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-
|
|
5
|
+
import { i as reUnwind, n as humanReview, r as isCode$1, t as buildTail } from "../chunks/review-CXzUXJHL.mjs";
|
|
6
6
|
import { posix } from "node:path";
|
|
7
7
|
//#region src/workflows/shared/fences.ts
|
|
8
8
|
const markFenced = (text) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, start } from "../flows.mjs";
|
|
2
2
|
import { Mt as envDefaults, W as finisherSystem, _ as REQUIREMENTS, gt as reviewGateBlockedMessage, ht as reviewFeedbackFixPrompt, i as skills, j as agentWithSkills, jt as defaults, l as gate, n as summary, r as access, s as shipped, t as steering, x as coder } from "../chunks/reserved-CQ7amcKG.mjs";
|
|
3
3
|
import { a as refusePendingHandOff } from "../chunks/tail-JZ9PAOwp.mjs";
|
|
4
|
-
import { i as reUnwind, t as buildTail } from "../chunks/review-
|
|
4
|
+
import { i as reUnwind, t as buildTail } from "../chunks/review-CXzUXJHL.mjs";
|
|
5
5
|
//#region src/workflows/review/review.ts
|
|
6
6
|
const fixFeedback = async () => {
|
|
7
7
|
await agentWithSkills("feedback.fixing", reviewFeedbackFixPrompt(), {
|
package/dist/workflows.mjs
CHANGED
|
@@ -39584,13 +39584,14 @@ const requirePointers = (review, notes, codeChanged) => {
|
|
|
39584
39584
|
* pointing at its code and each of the lap's notes a chunk of its own; gtd
|
|
39585
39585
|
* then adds each note's text and answer boxes, and the summary chunks.
|
|
39586
39586
|
*/
|
|
39587
|
-
const walkthrough = async (base, report, lap, carry) => {
|
|
39587
|
+
const walkthrough = async (base, report, lap, carry, afterRiskFix) => {
|
|
39588
39588
|
const notes = titledNotes(report);
|
|
39589
39589
|
await reviewing(base, carry, lap, walkthroughBlock(notes, report.nitFix));
|
|
39590
39590
|
const written = read(REVIEW);
|
|
39591
39591
|
if (written === void 0) return;
|
|
39592
39592
|
const review = unranked(written);
|
|
39593
39593
|
requirePointers(written, notes, changesSince(base).some((c) => isCode$1(c.path)));
|
|
39594
|
+
if (!afterRiskFix && reviewRisks(written).length > 0) return;
|
|
39594
39595
|
const full = sectionedReview(await ranked(review, withWalkthrough(review, report), new Set(notes.map((n) => n.title))), new Set(reviewChunks(review).map((c) => c.title)));
|
|
39595
39596
|
if (full !== written) await run("review.carrying", writeScript(REVIEW, full), {
|
|
39596
39597
|
label: "Ordering the review and adding the quality lap's findings",
|
|
@@ -39616,16 +39617,22 @@ const humanReview = (base, escalations = { rounds: 0 }, options = {}) => {
|
|
|
39616
39617
|
const severities = /* @__PURE__ */ new Map();
|
|
39617
39618
|
let nitFix;
|
|
39618
39619
|
const answered = /* @__PURE__ */ new Set();
|
|
39620
|
+
let riskFixed = false;
|
|
39619
39621
|
return review$1(base, {
|
|
39620
39622
|
file: REVIEW,
|
|
39621
39623
|
feedback: feedback.file,
|
|
39622
39624
|
isCode: isCode$1,
|
|
39623
39625
|
reviewing: (b, carry) => {
|
|
39624
39626
|
const fixedAt = nitFix;
|
|
39627
|
+
const afterRiskFix = riskFixed;
|
|
39628
|
+
riskFixed = false;
|
|
39625
39629
|
const report = options.report ?? NO_FINDINGS;
|
|
39626
|
-
return options.walkthrough === true ? guarded(() => walkthrough(b, composedOf(b, report, fixedAt, answered), options.lap ?? "first", carry), guard)() : composing(b, report, carry, fixedAt, answered);
|
|
39630
|
+
return options.walkthrough === true ? guarded(() => walkthrough(b, composedOf(b, report, fixedAt, answered), options.lap ?? "first", carry, afterRiskFix), guard)() : composing(b, report, carry, fixedAt, answered);
|
|
39627
39631
|
},
|
|
39628
|
-
fixRisks: guarded((notes) =>
|
|
39632
|
+
fixRisks: guarded((notes) => {
|
|
39633
|
+
riskFixed = true;
|
|
39634
|
+
return fixRisks(notes, fixAccess, rules);
|
|
39635
|
+
}, guard),
|
|
39629
39636
|
keepGreen: () => healthy(() => fixCheck(rules), {
|
|
39630
39637
|
escalations,
|
|
39631
39638
|
guard,
|
package/package.json
CHANGED
package/src/testkit/steps.ts
CHANGED
|
@@ -193,6 +193,21 @@ Given(
|
|
|
193
193
|
},
|
|
194
194
|
)
|
|
195
195
|
|
|
196
|
+
// An agent's edit in place, keeping whatever gtd wrote around it.
|
|
197
|
+
Given(
|
|
198
|
+
"step {string} replaces the text {string} in {string} with {string}",
|
|
199
|
+
(world: TestkitWorld, step: string, text: string, path: string, replacement: string) => {
|
|
200
|
+
world.act(step, (_, repo) => {
|
|
201
|
+
const content = repo.read(path) ?? ""
|
|
202
|
+
assert.ok(content.includes(text), `${path}: ${content}`)
|
|
203
|
+
repo.write(
|
|
204
|
+
path,
|
|
205
|
+
content.replace(text, () => replacement),
|
|
206
|
+
)
|
|
207
|
+
})
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
|
|
196
211
|
Given("step {string} deletes {string}", (world: TestkitWorld, step: string, path: string) => {
|
|
197
212
|
world.act(step, (_, repo) => repo.remove(path))
|
|
198
213
|
})
|
|
@@ -390,6 +405,14 @@ Then(
|
|
|
390
405
|
},
|
|
391
406
|
)
|
|
392
407
|
|
|
408
|
+
Then(
|
|
409
|
+
"{string} contains {string} exactly once",
|
|
410
|
+
(world: TestkitWorld, path: string, text: string) => {
|
|
411
|
+
const content = world.repo().read(path) ?? ""
|
|
412
|
+
assert.strictEqual(content.split(text).length - 1, 1, `${path}: ${content}`)
|
|
413
|
+
},
|
|
414
|
+
)
|
|
415
|
+
|
|
393
416
|
Then("{string} does not contain {string}", (world: TestkitWorld, path: string, text: string) => {
|
|
394
417
|
const content = world.repo().read(path)
|
|
395
418
|
assert.ok(content !== undefined && !content.includes(text), `${path}: ${content}`)
|
|
@@ -161,6 +161,56 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
161
161
|
gtd(check): build.review.carrying → build.review.await-review
|
|
162
162
|
"""
|
|
163
163
|
|
|
164
|
+
Scenario: a risk is fixed before the human sees the review, re-reviewed in the reviewer's own draft
|
|
165
|
+
Given judge step "build.quality.triage" answers:
|
|
166
|
+
"""
|
|
167
|
+
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.85},{"id":"Q3","answer":"design","p":0.8}]
|
|
168
|
+
"""
|
|
169
|
+
And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
|
|
170
|
+
"""
|
|
171
|
+
it("[AC-1] subtracts", () => {})
|
|
172
|
+
it("subtracts a larger number", () => {})
|
|
173
|
+
"""
|
|
174
|
+
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
175
|
+
And step "build.health.check" changes nothing
|
|
176
|
+
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
177
|
+
"""
|
|
178
|
+
# Review: abc1234
|
|
179
|
+
|
|
180
|
+
<!-- base: 0000000 -->
|
|
181
|
+
|
|
182
|
+
## Subtract
|
|
183
|
+
|
|
184
|
+
- [ ] ./src/calc.ts#2-2 — Risk: overflows on large numbers
|
|
185
|
+
|
|
186
|
+
## AC-1 ignores floating point
|
|
187
|
+
|
|
188
|
+
- [ ] ./src/calc.ts#2-2 — where it applies
|
|
189
|
+
"""
|
|
190
|
+
When the process is driven
|
|
191
|
+
Then the process rests at "build.review.fix.risks.fixing"
|
|
192
|
+
Given step "build.review.reviewing" is no longer scripted
|
|
193
|
+
And step "build.review.reviewing" replaces the text "Risk: overflows on large numbers" in ".gtd/REVIEW.md" with "subtract(a, b), now guarded"
|
|
194
|
+
And step "build.review.fix.risks.fixing" changes nothing
|
|
195
|
+
When the process is driven
|
|
196
|
+
Then the process rests at "build.review.await-review"
|
|
197
|
+
And ".gtd/REVIEW.md" contains "subtract(a, b), now guarded"
|
|
198
|
+
And ".gtd/REVIEW.md" contains "## Fixed automatically" exactly once
|
|
199
|
+
And ".gtd/REVIEW.md" contains "## Dropped as noise" exactly once
|
|
200
|
+
And ".gtd/REVIEW.md" contains "## Walkthrough" exactly once
|
|
201
|
+
And ".gtd/REVIEW.md" contains "Should subtract round? The criterion does not say." exactly once
|
|
202
|
+
And ".gtd/REVIEW.md" contains "Tick exactly one:" exactly once
|
|
203
|
+
And the commit subjects end with:
|
|
204
|
+
"""
|
|
205
|
+
gtd(check): build.health.check → build.review.reviewing
|
|
206
|
+
gtd(agent): build.review.reviewing → build.review.fix.risks.fixing
|
|
207
|
+
gtd(agent): build.review.fix.risks.fixing → build.health.check
|
|
208
|
+
gtd(check): build.health.check → build.review.reviewing
|
|
209
|
+
gtd(agent): build.review.reviewing → build.review.ranking
|
|
210
|
+
gtd(judge): build.review.ranking → build.review.carrying
|
|
211
|
+
gtd(check): build.review.carrying → build.review.await-review
|
|
212
|
+
"""
|
|
213
|
+
|
|
164
214
|
Scenario: an unsure verdict makes a finding a note, never a fix or a drop
|
|
165
215
|
Given judge step "build.quality.triage" answers:
|
|
166
216
|
"""
|
|
@@ -1185,6 +1185,44 @@ Feature: the refactor is reviewed on its own before the feature is implemented
|
|
|
1185
1185
|
"""
|
|
1186
1186
|
And no commit body contains "refactor.build.review.composing"
|
|
1187
1187
|
|
|
1188
|
+
Scenario: a risk the refactor's re-review marks again reaches the human unfixed
|
|
1189
|
+
Given step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
1190
|
+
"""
|
|
1191
|
+
# Review: abc1234
|
|
1192
|
+
|
|
1193
|
+
<!-- base: 0000000 -->
|
|
1194
|
+
|
|
1195
|
+
## calc
|
|
1196
|
+
|
|
1197
|
+
- [ ] ./src/calc.ts#1-2 — Risk: drops the sign
|
|
1198
|
+
"""
|
|
1199
|
+
When the process is driven
|
|
1200
|
+
Then the process rests at "refactor.build.review.fix.risks.fixing"
|
|
1201
|
+
Given step "refactor.build.review.reviewing" is no longer scripted
|
|
1202
|
+
And step "refactor.build.review.reviewing" replaces the text "Risk: drops the sign" in ".gtd/REVIEW.md" with "Risk: still drops the sign"
|
|
1203
|
+
And step "refactor.build.review.fix.risks.fixing" changes nothing
|
|
1204
|
+
And step "refactor.build.health.check" changes nothing
|
|
1205
|
+
When the process is driven
|
|
1206
|
+
Then the process rests at "refactor.build.review.await-review"
|
|
1207
|
+
And ".gtd/REVIEW.md" contains:
|
|
1208
|
+
"""
|
|
1209
|
+
## Walkthrough
|
|
1210
|
+
|
|
1211
|
+
### calc
|
|
1212
|
+
|
|
1213
|
+
- [ ] ./src/calc.ts#1-2 — Risk: still drops the sign
|
|
1214
|
+
"""
|
|
1215
|
+
And ".gtd/REVIEW.md" contains "## Walkthrough" exactly once
|
|
1216
|
+
And the commit subjects end with:
|
|
1217
|
+
"""
|
|
1218
|
+
gtd(agent): refactor.build.review.reviewing → refactor.build.review.fix.risks.fixing
|
|
1219
|
+
gtd(agent): refactor.build.review.fix.risks.fixing → refactor.build.health.check
|
|
1220
|
+
gtd(check): refactor.build.health.check → refactor.build.review.reviewing
|
|
1221
|
+
gtd(agent): refactor.build.review.reviewing → refactor.build.review.ranking
|
|
1222
|
+
gtd(judge): refactor.build.review.ranking → refactor.build.review.carrying
|
|
1223
|
+
gtd(check): refactor.build.review.carrying → refactor.build.review.await-review
|
|
1224
|
+
"""
|
|
1225
|
+
|
|
1188
1226
|
Rule: with a quality lens on both the refactor and the feature
|
|
1189
1227
|
|
|
1190
1228
|
Background:
|
|
@@ -179,37 +179,6 @@ Feature: a reviewer walks the human through the feature's code
|
|
|
179
179
|
"""
|
|
180
180
|
Then driving the process is refused with "AC-1 ignores floating point — no chunk of its own"
|
|
181
181
|
|
|
182
|
-
Scenario: a risk the reviewer marks is fixed before the human sees the review
|
|
183
|
-
Given step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
184
|
-
"""
|
|
185
|
-
# Review: abc1234
|
|
186
|
-
|
|
187
|
-
<!-- base: 0000000 -->
|
|
188
|
-
|
|
189
|
-
## Subtract
|
|
190
|
-
|
|
191
|
-
- [ ] ./src/calc.ts#2-2 — Risk: overflows on large numbers
|
|
192
|
-
|
|
193
|
-
## AC-1 ignores floating point
|
|
194
|
-
|
|
195
|
-
- [ ] ./src/calc.ts#2-2 — returns the raw difference
|
|
196
|
-
"""
|
|
197
|
-
And step "build.review.fix.risks.fixing" changes nothing
|
|
198
|
-
And step "build.health.check" changes nothing
|
|
199
|
-
When the process is driven
|
|
200
|
-
Then the process rests at "build.review.await-review"
|
|
201
|
-
And the commit subjects end with:
|
|
202
|
-
"""
|
|
203
|
-
gtd(agent): build.review.reviewing → build.review.ranking
|
|
204
|
-
gtd(judge): build.review.ranking → build.review.carrying
|
|
205
|
-
gtd(check): build.review.carrying → build.review.fix.risks.fixing
|
|
206
|
-
gtd(agent): build.review.fix.risks.fixing → build.health.check
|
|
207
|
-
gtd(check): build.health.check → build.review.reviewing
|
|
208
|
-
gtd(agent): build.review.reviewing → build.review.ranking
|
|
209
|
-
gtd(judge): build.review.ranking → build.review.carrying
|
|
210
|
-
gtd(check): build.review.carrying → build.review.await-review
|
|
211
|
-
"""
|
|
212
|
-
|
|
213
182
|
Scenario: a standard conflict the reviewer writes itself is refused
|
|
214
183
|
Given step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
215
184
|
"""
|
|
@@ -380,6 +380,7 @@ const walkthrough = async (
|
|
|
380
380
|
report: t.ComposedReview,
|
|
381
381
|
lap: t.ReviewRange,
|
|
382
382
|
carry: string | undefined,
|
|
383
|
+
afterRiskFix: boolean,
|
|
383
384
|
): Promise<void> => {
|
|
384
385
|
const notes = t.titledNotes(report)
|
|
385
386
|
await reviewing(base, carry, lap, t.walkthroughBlock(notes, report.nitFix))
|
|
@@ -393,7 +394,8 @@ const walkthrough = async (
|
|
|
393
394
|
notes,
|
|
394
395
|
changesSince(base).some((c) => isCode(c.path)),
|
|
395
396
|
)
|
|
396
|
-
//
|
|
397
|
+
// Its risks are fixed and the review written again, so the re-reviewer edits this draft, never gtd's layout.
|
|
398
|
+
if (!afterRiskFix && reviewRisks(written).length > 0) return
|
|
397
399
|
const full = sectionedReview(
|
|
398
400
|
await ranked(review, t.withWalkthrough(review, report), new Set(notes.map((n) => n.title))),
|
|
399
401
|
new Set(reviewChunks(review).map((c) => c.title)),
|
|
@@ -432,12 +434,16 @@ export const humanReview = (
|
|
|
432
434
|
let nitFix: string | undefined
|
|
433
435
|
// Every note the human answered in this review, left out of a fresh one.
|
|
434
436
|
const answered = new Set<string>()
|
|
437
|
+
// Set by a risk fix, so the review written next reaches the human whatever it marks.
|
|
438
|
+
let riskFixed = false
|
|
435
439
|
return kit.review(base, {
|
|
436
440
|
file: REVIEW,
|
|
437
441
|
feedback: feedback.file,
|
|
438
442
|
isCode,
|
|
439
443
|
reviewing: (b, carry) => {
|
|
440
444
|
const fixedAt = nitFix
|
|
445
|
+
const afterRiskFix = riskFixed
|
|
446
|
+
riskFixed = false
|
|
441
447
|
const report = options.report ?? NO_FINDINGS
|
|
442
448
|
return options.walkthrough === true
|
|
443
449
|
? guarded(
|
|
@@ -447,12 +453,16 @@ export const humanReview = (
|
|
|
447
453
|
composedOf(b, report, fixedAt, answered),
|
|
448
454
|
options.lap ?? "first",
|
|
449
455
|
carry,
|
|
456
|
+
afterRiskFix,
|
|
450
457
|
),
|
|
451
458
|
guard,
|
|
452
459
|
)()
|
|
453
460
|
: composing(b, report, carry, fixedAt, answered)
|
|
454
461
|
},
|
|
455
|
-
fixRisks: guarded((notes) =>
|
|
462
|
+
fixRisks: guarded((notes) => {
|
|
463
|
+
riskFixed = true
|
|
464
|
+
return fixRisks(notes, fixAccess, rules)
|
|
465
|
+
}, guard),
|
|
456
466
|
keepGreen: () => healthy(() => fixCheck(rules), { escalations, guard, guardRests, rules }),
|
|
457
467
|
// Kept until the human sees a review, so a re-review after a risk fix still names it.
|
|
458
468
|
awaitReview: (b) => {
|