@llm4ts/flow 2.50.0 → 2.52.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/dist/FlowEvents.d.ts +2 -0
- package/dist/FlowEvents.d.ts.map +1 -1
- package/dist/FlowEvents.js +3 -1
- package/dist/FlowEvents.js.map +1 -1
- package/dist/Recheck.d.ts +69 -0
- package/dist/Recheck.d.ts.map +1 -0
- package/dist/Recheck.js +111 -0
- package/dist/Recheck.js.map +1 -0
- package/dist/Review.d.ts +32 -33
- package/dist/Review.d.ts.map +1 -1
- package/dist/Review.js +104 -45
- package/dist/Review.js.map +1 -1
- package/dist/ReviewCache.d.ts +1 -1
- package/dist/ReviewCache.d.ts.map +1 -1
- package/dist/ReviewCache.js +1 -1
- package/dist/ReviewCache.js.map +1 -1
- package/dist/ReviewModel.d.ts +36 -0
- package/dist/ReviewModel.d.ts.map +1 -0
- package/dist/ReviewModel.js +50 -0
- package/dist/ReviewModel.js.map +1 -0
- package/dist/Stories.d.ts +36 -2
- package/dist/Stories.d.ts.map +1 -1
- package/dist/Stories.js +255 -90
- package/dist/Stories.js.map +1 -1
- package/package.json +4 -2
- package/src/FlowEvents.ts +3 -1
- package/src/Recheck.ts +160 -0
- package/src/Review.ts +178 -58
- package/src/ReviewCache.ts +1 -1
- package/src/ReviewModel.ts +63 -0
- package/src/Stories.ts +270 -18
package/dist/Stories.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stories.d.ts","sourceRoot":"","sources":["../src/Stories.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAIvC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAA;AACjC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAG1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAE9D,OAAO,KAAK,EAAY,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG7E,OAAO,EAQL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxE,OAAO,EAGL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAQL,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAU7D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,IAAI,EAAQ,MAAM,WAAW,CAAA;AAGtC,OAAO,EACL,WAAW,EACX,YAAY,
|
|
1
|
+
{"version":3,"file":"Stories.d.ts","sourceRoot":"","sources":["../src/Stories.ts"],"names":[],"mappings":"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"}
|
package/dist/Stories.js
CHANGED
|
@@ -16,7 +16,7 @@ import * as Stream from "effect/Stream";
|
|
|
16
16
|
import { cap } from "./Context.js";
|
|
17
17
|
import { BoardItem, eventedBoard } from "./BoardSync.js";
|
|
18
18
|
import { makeChat } from "./Chat.js";
|
|
19
|
-
import { implementPlanFlow } from "./Flow.js";
|
|
19
|
+
import { flowReviewer, implementPlanFlow } from "./Flow.js";
|
|
20
20
|
import { GateBaseline, baselineKey, ensureBaseline as ensureStoredBaseline, failingLinesOf, storyGateLogDir, mergeGateLogDir, writeBaseline } from "./Gates.js";
|
|
21
21
|
import { describeFlowError, rosterExhaustedPrefix, EpicCheckoutDirty, MissingDependency, PerimeterViolation, StoryFailed } from "./FlowError.js";
|
|
22
22
|
import { Began, EvidenceChecked, Info, JudgedDimension, StoryJudged, Timed, withLane } from "./FlowEvents.js";
|
|
@@ -27,8 +27,9 @@ import { loadVersioned, makePlanStore, saveVersioned } from "./Persistence.js";
|
|
|
27
27
|
import { Plan, Task } from "./Plan.js";
|
|
28
28
|
import { stage } from "./PlanExecution.js";
|
|
29
29
|
import { planFrom } from "./Planner.js";
|
|
30
|
-
import { ReviewIssue, ReviewResult, applyTriage, isBlocking, loadRepoReviewRules, nonBlockingIssues } from "./Review.js";
|
|
30
|
+
import { ReviewIssue, ReviewResult, applyTriage, isBlocking, loadRepoReviewRules, minimalReviewers, reviewAndFixLoop, nonBlockingIssues } from "./Review.js";
|
|
31
31
|
import { checkOracle, defaultOracleRules, parseUnifiedDiff } from "./OracleGuard.js";
|
|
32
|
+
import { ReviewMemory } from "./Recheck.js";
|
|
32
33
|
import { cachedValue, fingerprintOf } from "./ReviewCache.js";
|
|
33
34
|
import { withContract } from "./AutonomyContract.js";
|
|
34
35
|
import { fabricatedStatusIssues, unverifiedClaims } from "./Evidence.js";
|
|
@@ -39,8 +40,16 @@ const join = (root, path) => `${root.replace(/[\\/]+$/, "")}/${path.replace(/^[\
|
|
|
39
40
|
// ---- Story seats ----------------------------------------------------------
|
|
40
41
|
/** A flow context rooted in a story's worktree, plus that story's own usage totals. */
|
|
41
42
|
/** A story judge's review with the rubric's scores; a plain `ReviewResult` has none. */
|
|
43
|
+
/** A dimension below the bar the story was not held to, with the judge's reasoning (ADR 0045). */
|
|
44
|
+
export const ExcusedDimension = Schema.Struct({
|
|
45
|
+
id: Schema.String,
|
|
46
|
+
score: Schema.Number,
|
|
47
|
+
max: Schema.Number,
|
|
48
|
+
reasoning: Schema.String
|
|
49
|
+
});
|
|
42
50
|
export class StoryVerdict extends ReviewResult.extend("StoryVerdict")({
|
|
43
|
-
dimensions: Schema.Array(JudgedDimension)
|
|
51
|
+
dimensions: Schema.Array(JudgedDimension),
|
|
52
|
+
excused: Schema.optionalKey(Schema.Array(ExcusedDimension))
|
|
44
53
|
}) {
|
|
45
54
|
}
|
|
46
55
|
/** What a judge answers: a scored verdict, or a plain review result. */
|
|
@@ -451,6 +460,14 @@ const combined = (first, second) => first.isClean && second.isClean
|
|
|
451
460
|
* nothing was deferred.
|
|
452
461
|
*/
|
|
453
462
|
export const deferredPath = (stateDir, storyId) => join(stateDir, `stories/${storyId}.deferred.md`);
|
|
463
|
+
/** Where a story review keeps its reviewers' memory between rounds and runs (ADR 0044). */
|
|
464
|
+
export const reviewStatePath = (stateDir, storyId) => join(stateDir, `stories/${storyId}.review-state.json`);
|
|
465
|
+
/** Where a story's last judge verdict is kept, beside a fingerprint of what it judged. */
|
|
466
|
+
export const judgeVerdictPath = (stateDir, storyId) => join(stateDir, `stories/${storyId}.judge.json`);
|
|
467
|
+
/** A story's last judge verdict, when it was a scored one (ADR 0045: what the debt reads). */
|
|
468
|
+
export const lastStoryVerdict = (files, stateDir, storyId) => files.read(judgeVerdictPath(stateDir, storyId)).pipe(Effect.flatMap((text) => text === undefined
|
|
469
|
+
? Effect.succeed(undefined)
|
|
470
|
+
: Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({ result: StoryVerdict })))(text).pipe(Effect.map((entry) => entry.result))), Effect.catch(() => Effect.succeed(undefined)));
|
|
454
471
|
/** A deferred finding as one Markdown bullet, prefixed with where it was found. */
|
|
455
472
|
export const deferredLine = (source, issue) => {
|
|
456
473
|
const where = issue.file === undefined
|
|
@@ -516,10 +533,12 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
516
533
|
// Parallel tasks are opt-in (ADR 0034 amendment): one coder per story unless asked.
|
|
517
534
|
const taskConcurrency = Math.max(1, options.taskConcurrency ?? 1);
|
|
518
535
|
const deferring = options.deferNonBlocking === true;
|
|
536
|
+
const reviewAtStory = options.reviewAt === "story";
|
|
537
|
+
const reviewRounds = Math.max(1, options.reviewRounds ?? 3);
|
|
519
538
|
const statePath = (story) => join(options.stateDir, `stories/${story.id}.json`);
|
|
520
539
|
const planPath = (story) => join(options.stateDir, `stories/${story.id}.plan.md`);
|
|
521
540
|
/** The judge's last verdict beside a fingerprint of what it judged. */
|
|
522
|
-
const judgePath = (story) =>
|
|
541
|
+
const judgePath = (story) => judgeVerdictPath(options.stateDir, story.id);
|
|
523
542
|
/** One file per review lens, beside a fingerprint of the task and diff. */
|
|
524
543
|
const reviewCacheDir = (story) => join(options.stateDir, `stories/${story.id}.review`);
|
|
525
544
|
/** What reviewers and the judge found, round by round, for people to read. */
|
|
@@ -1155,7 +1174,10 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
1155
1174
|
// A lens's answer is kept beside the task and diff it answered, so a
|
|
1156
1175
|
// rerun over the same change asks nothing; every round is written to
|
|
1157
1176
|
// the story's findings log for people to read.
|
|
1158
|
-
|
|
1177
|
+
// Reviewing per story (ADR 0044), a task runs on its gates alone.
|
|
1178
|
+
...(reviewAtStory
|
|
1179
|
+
? { reviewers: [] }
|
|
1180
|
+
: { reviewCache: { files, dir: reviewCacheDir(story) } }),
|
|
1159
1181
|
onReview: (task, round, result, settled) => Effect.gen(function* () {
|
|
1160
1182
|
// A settled round's leftovers are deferred, not dropped: when the
|
|
1161
1183
|
// rounds ran out, blocking ones too, since the task commits anyway.
|
|
@@ -1168,15 +1190,17 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
1168
1190
|
onTaskReply: (task, _reply, trailer, startedAt) => checkEvidence(task, trailer, startedAt),
|
|
1169
1191
|
// The repository's own review rules ride along as one extra lens
|
|
1170
1192
|
// (ADR 0027 decision 9), read from the epic checkout.
|
|
1171
|
-
repoRules: loadRepoReviewRules(files, context.workDir),
|
|
1193
|
+
...(reviewAtStory ? {} : { repoRules: loadRepoReviewRules(files, context.workDir) }),
|
|
1172
1194
|
...(options.votes === undefined ? {} : { votes: options.votes }),
|
|
1173
1195
|
...(options.fixer === undefined ? {} : { fixer: options.fixer }),
|
|
1174
1196
|
...(options.stall === undefined ? {} : { stall: options.stall }),
|
|
1175
|
-
...(options.reviewers === undefined ? {} : { reviewers: options.reviewers }),
|
|
1197
|
+
...(options.reviewers === undefined || reviewAtStory ? {} : { reviewers: options.reviewers }),
|
|
1176
1198
|
...(options.maxRounds === undefined ? {} : { maxRounds: options.maxRounds }),
|
|
1177
1199
|
...(deferring ? { settle: "blocking" } : {})
|
|
1178
1200
|
});
|
|
1179
1201
|
if (mergeFailure !== undefined) {
|
|
1202
|
+
// The tasks run again: what the story review remembered no longer holds.
|
|
1203
|
+
yield* files.remove(reviewStatePath(options.stateDir, story.id));
|
|
1180
1204
|
// The branch already caught up with the epic above: the gates in the
|
|
1181
1205
|
// worktree now see the combination that was red after the merge.
|
|
1182
1206
|
yield* appendRevision("make the epic gates green after merging", [
|
|
@@ -1211,94 +1235,232 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
1211
1235
|
const addRevision = (verdict, empty) => appendRevision("close the judge's findings", empty
|
|
1212
1236
|
? `The story "${story.title}" has no changes yet, and what it must provide is not all in place on the epic branch.`
|
|
1213
1237
|
: `The story "${story.title}" was judged short of done.`, verdict);
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1238
|
+
/**
|
|
1239
|
+
* One judge round over the story's branch: the verdict, published on the
|
|
1240
|
+
* lane, logged in the findings and kept beside a fingerprint of what was
|
|
1241
|
+
* judged. `empty` says the branch had no changes, so its owned code was
|
|
1242
|
+
* judged instead.
|
|
1243
|
+
*/
|
|
1244
|
+
const judgeOnce = (judge, round) => Effect.gen(function* () {
|
|
1245
|
+
const diff = yield* git.diffVsBase(epicBranch);
|
|
1246
|
+
// No changes is not a failure by itself: the story's code may already
|
|
1247
|
+
// be on the epic (an earlier story or run put it there). Judge that
|
|
1248
|
+
// code instead: the owned paths against the empty tree.
|
|
1249
|
+
const empty = diff.trim().length === 0;
|
|
1250
|
+
const judgedCode = empty ? yield* git.diffVsBaseScoped(emptyTree, story.owned, false) : diff;
|
|
1251
|
+
// What the coder claimed and could not show travels with the subject
|
|
1252
|
+
// (ADR 0027 decision 6): the judge weighs the code knowing the
|
|
1253
|
+
// story's own account of its testing is not evidence.
|
|
1254
|
+
const subject = evidenceNotes.length === 0
|
|
1255
|
+
? judgedCode
|
|
1256
|
+
: [
|
|
1257
|
+
"Evidence notes from the run (not part of the diff):",
|
|
1258
|
+
...evidenceNotes.map((note) => `- ${note}`),
|
|
1259
|
+
"",
|
|
1260
|
+
judgedCode
|
|
1261
|
+
].join("\n");
|
|
1262
|
+
// The verdict is kept beside a fingerprint of what was judged: a
|
|
1263
|
+
// rerun that finds the same diff gets the same answer without a
|
|
1264
|
+
// model call, and the last verdict is on disk to read. Only the
|
|
1265
|
+
// first round reuses it; a later round follows a revision and asks.
|
|
1266
|
+
// The round is an EVALUATOR span (ADR 0026): the verdict's scores and
|
|
1267
|
+
// findings land on it, so a trace shows why a story was sent back.
|
|
1268
|
+
const judged = yield* withKindSpan(`story ${story.id}: judge ${round}`, { kind: "EVALUATOR" }, Effect.gen(function* () {
|
|
1269
|
+
const outcome = empty && subject.trim().length === 0
|
|
1270
|
+
? // Nothing to judge is decided here, not asked of a model.
|
|
1271
|
+
{
|
|
1272
|
+
value: ReviewResult.make({
|
|
1273
|
+
issues: [
|
|
1274
|
+
ReviewIssue.make({
|
|
1275
|
+
severity: "Critical",
|
|
1276
|
+
title: `nothing exists yet under ${story.owned.join(", ")}`,
|
|
1277
|
+
description: "The story's owned paths are empty on the epic branch."
|
|
1278
|
+
})
|
|
1279
|
+
],
|
|
1280
|
+
summary: `judge:${story.id}`
|
|
1281
|
+
}),
|
|
1282
|
+
reused: false
|
|
1283
|
+
}
|
|
1284
|
+
: yield* cachedValue(files, judgePath(story), Verdict, fingerprintOf([
|
|
1285
|
+
empty ? "code" : "diff",
|
|
1286
|
+
subject,
|
|
1287
|
+
story.id,
|
|
1288
|
+
story.title,
|
|
1289
|
+
story.description,
|
|
1290
|
+
story.provides.join("\n"),
|
|
1291
|
+
// Absent for the default bar: verdicts stored before 2.52 stay valid.
|
|
1292
|
+
...(options.judgeBar === undefined ? [] : [options.judgeBar])
|
|
1293
|
+
]), withTimedRole("judge", judge(story, subject, watchedSeats, empty ? "code" : "diff")), { reuse: round === 1 });
|
|
1294
|
+
const scored = outcome.value instanceof StoryVerdict ? outcome.value.dimensions : [];
|
|
1295
|
+
yield* Effect.annotateCurrentSpan({
|
|
1296
|
+
"llm4ts.judge.cleared": outcome.value.isClean,
|
|
1297
|
+
"llm4ts.judge.reused": outcome.reused,
|
|
1298
|
+
...Object.fromEntries(scored.map((d) => [`llm4ts.judge.${d.id}`, d.score]))
|
|
1299
|
+
});
|
|
1300
|
+
const now = yield* Clock.currentTimeNanos;
|
|
1301
|
+
yield* Effect.option(Effect.currentSpan).pipe(Effect.map((span) => {
|
|
1302
|
+
if (span._tag === "Some") {
|
|
1303
|
+
for (const issue of outcome.value.issues) {
|
|
1304
|
+
span.value.event("judge finding", now, {
|
|
1305
|
+
title: issue.title,
|
|
1306
|
+
severity: issue.severity
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}));
|
|
1311
|
+
return outcome;
|
|
1312
|
+
}));
|
|
1313
|
+
const verdict = judged.value;
|
|
1314
|
+
if (judged.reused) {
|
|
1315
|
+
yield* laneOf(story).publish(Info.make({
|
|
1316
|
+
lane: story.id,
|
|
1317
|
+
message: `judge round ${round}: reused the verdict for an unchanged ${empty ? "code" : "diff"}`
|
|
1318
|
+
}));
|
|
1319
|
+
}
|
|
1320
|
+
yield* appendFindings(story, `judge round ${round} — ${verdict.isClean ? "cleared" : "not cleared"}${verdict instanceof StoryVerdict
|
|
1321
|
+
? ` (${verdict.dimensions.map((d) => `${d.id} ${d.score}/${d.max}`).join(", ")})`
|
|
1322
|
+
: ""}${judged.reused ? ", reused" : ""}`, verdict);
|
|
1323
|
+
yield* laneOf(story).publish(StoryJudged.make({
|
|
1324
|
+
lane: story.id,
|
|
1325
|
+
round,
|
|
1326
|
+
cleared: verdict.isClean,
|
|
1327
|
+
issues: verdict.issues.length,
|
|
1328
|
+
dimensions: verdict instanceof StoryVerdict ? verdict.dimensions : []
|
|
1329
|
+
}));
|
|
1330
|
+
return { verdict, empty };
|
|
1331
|
+
});
|
|
1332
|
+
/**
|
|
1333
|
+
* The story review (ADR 0044): every lens and the judge on the story's
|
|
1334
|
+
* diff, one fix turn per round, re-checks after the first. Returns the
|
|
1335
|
+
* judge's note; fails the story when the judge is still not cleared.
|
|
1336
|
+
*/
|
|
1337
|
+
const reviewStory = () => Effect.gen(function* () {
|
|
1338
|
+
const statePath = reviewStatePath(options.stateDir, story.id);
|
|
1339
|
+
const repoRules = yield* loadRepoReviewRules(files, context.workDir);
|
|
1340
|
+
const lenses = [
|
|
1341
|
+
...(options.reviewers ?? minimalReviewers),
|
|
1342
|
+
...(repoRules === undefined ? [] : [repoRules])
|
|
1343
|
+
];
|
|
1217
1344
|
const judge = options.judge;
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
const judgedCode = empty ? yield* git.diffVsBaseScoped(emptyTree, story.owned, false) : diff;
|
|
1225
|
-
// What the coder claimed and could not show travels with the subject
|
|
1226
|
-
// (ADR 0027 decision 6): the judge weighs the code knowing the
|
|
1227
|
-
// story's own account of its testing is not evidence.
|
|
1228
|
-
const subject = evidenceNotes.length === 0
|
|
1229
|
-
? judgedCode
|
|
1345
|
+
const latest = yield* Ref.make(undefined);
|
|
1346
|
+
const lastStanding = yield* Ref.make(undefined);
|
|
1347
|
+
// The fix turn is a fresh chat: it gets the story and where each finding points.
|
|
1348
|
+
const storyFixPrompt = (prompt, standing) => {
|
|
1349
|
+
const located = (standing?.issues ?? []).flatMap((issue) => issue.file === undefined
|
|
1350
|
+
? []
|
|
1230
1351
|
: [
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1352
|
+
`- ${issue.title} (${issue.lens ?? "gates"}): ${issue.file}${issue.line === undefined ? "" : `:${issue.line}`}`
|
|
1353
|
+
]);
|
|
1354
|
+
return [
|
|
1355
|
+
`Story ${story.id}: ${story.title}`,
|
|
1356
|
+
story.description,
|
|
1357
|
+
...(story.acceptance.length === 0
|
|
1358
|
+
? []
|
|
1359
|
+
: ["Done when:", ...story.acceptance.map((line) => `- ${line}`)]),
|
|
1360
|
+
"",
|
|
1361
|
+
prompt,
|
|
1362
|
+
...(located.length === 0 ? [] : ["", "Where the findings point:", ...located])
|
|
1363
|
+
].join("\n");
|
|
1364
|
+
};
|
|
1365
|
+
const judgeRuns = yield* Ref.make(0);
|
|
1366
|
+
const lastRound = yield* Ref.make(0);
|
|
1367
|
+
// A judge that scores `provides` at 0 is owed a piece no fix turn writes.
|
|
1368
|
+
const missingPiece = (verdict) => verdict instanceof StoryVerdict &&
|
|
1369
|
+
verdict.dimensions.some((d) => d.id === "provides" && d.score <= 0);
|
|
1370
|
+
const result = yield* reviewAndFixLoop({
|
|
1371
|
+
reviewers: lenses,
|
|
1372
|
+
reviewerService: flowReviewer(storyContext),
|
|
1373
|
+
coder: yield* makeChat(storyContext.coder, { system, events, agent: "coder" }),
|
|
1374
|
+
taskTitle: `story ${story.id}: ${story.title}`,
|
|
1375
|
+
currentDiff: git.diffVsBase(epicBranch),
|
|
1376
|
+
events: storyContext.events,
|
|
1377
|
+
lint: gates,
|
|
1378
|
+
triage,
|
|
1379
|
+
maxRounds: reviewRounds,
|
|
1380
|
+
...(deferring ? { settle: "blocking" } : {}),
|
|
1381
|
+
...(options.fix === undefined ? {} : { fix: options.fix }),
|
|
1382
|
+
cache: { files, dir: reviewCacheDir(story) },
|
|
1383
|
+
fixWith: (prompt) => Effect.gen(function* () {
|
|
1384
|
+
const verdict = yield* Ref.get(latest);
|
|
1385
|
+
if (missingPiece(verdict)) {
|
|
1386
|
+
// Used once: a later round's fix answers that round, not this verdict.
|
|
1387
|
+
yield* Ref.set(latest, undefined);
|
|
1388
|
+
yield* addRevision(verdict, false);
|
|
1389
|
+
yield* guarded(implementTasks, implementTasks);
|
|
1390
|
+
yield* catchUp(story, git);
|
|
1391
|
+
yield* repairPerimeter;
|
|
1392
|
+
return "";
|
|
1393
|
+
}
|
|
1394
|
+
// A BLOCKED_ON from the fix turn stops the story like a task's would.
|
|
1395
|
+
yield* guarded(coderTurn(storyFixPrompt(prompt, yield* Ref.get(lastStanding))), Effect.void);
|
|
1396
|
+
yield* git.commitAll(`${story.id}: story review fixes`);
|
|
1397
|
+
return "";
|
|
1398
|
+
}),
|
|
1399
|
+
onRound: (round, standing, settled) => Effect.andThen(Effect.andThen(Ref.set(lastRound, round), Ref.set(lastStanding, standing)), appendFindings(story, `story review round ${round} — ${standing.isClean ? "clean" : `${standing.issues.length} issue(s)`}${settled ? "" : ", fixing"}`, standing)),
|
|
1400
|
+
recheck: {
|
|
1401
|
+
extra: judge === undefined
|
|
1402
|
+
? []
|
|
1403
|
+
: [
|
|
1245
1404
|
{
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
],
|
|
1254
|
-
summary: `judge:${story.id}`
|
|
1255
|
-
}),
|
|
1256
|
-
reused: false
|
|
1405
|
+
name: "judge",
|
|
1406
|
+
review: () => Effect.gen(function* () {
|
|
1407
|
+
const round = yield* Ref.updateAndGet(judgeRuns, (n) => n + 1);
|
|
1408
|
+
const { verdict } = yield* judgeOnce(judge, round);
|
|
1409
|
+
yield* Ref.set(latest, verdict);
|
|
1410
|
+
return verdict;
|
|
1411
|
+
})
|
|
1257
1412
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
]), withTimedRole("judge", judge(story, subject, watchedSeats, empty ? "code" : "diff")), { reuse: round === 1 });
|
|
1266
|
-
const scored = outcome.value instanceof StoryVerdict ? outcome.value.dimensions : [];
|
|
1267
|
-
yield* Effect.annotateCurrentSpan({
|
|
1268
|
-
"llm4ts.judge.cleared": outcome.value.isClean,
|
|
1269
|
-
"llm4ts.judge.reused": outcome.reused,
|
|
1270
|
-
...Object.fromEntries(scored.map((d) => [`llm4ts.judge.${d.id}`, d.score]))
|
|
1271
|
-
});
|
|
1272
|
-
const now = yield* Clock.currentTimeNanos;
|
|
1273
|
-
yield* Effect.option(Effect.currentSpan).pipe(Effect.map((span) => {
|
|
1274
|
-
if (span._tag === "Some") {
|
|
1275
|
-
for (const issue of outcome.value.issues) {
|
|
1276
|
-
span.value.event("judge finding", now, {
|
|
1277
|
-
title: issue.title,
|
|
1278
|
-
severity: issue.severity
|
|
1279
|
-
});
|
|
1413
|
+
],
|
|
1414
|
+
memory: {
|
|
1415
|
+
load: Effect.map(files.read(statePath), (text) => {
|
|
1416
|
+
if (text === undefined)
|
|
1417
|
+
return undefined;
|
|
1418
|
+
try {
|
|
1419
|
+
return Schema.decodeUnknownSync(ReviewMemory)(JSON.parse(text));
|
|
1280
1420
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
lane: story.id,
|
|
1289
|
-
message: `judge round ${round}: reused the verdict for an unchanged ${empty ? "code" : "diff"}`
|
|
1290
|
-
}));
|
|
1421
|
+
catch {
|
|
1422
|
+
// An unreadable state starts the review over.
|
|
1423
|
+
return undefined;
|
|
1424
|
+
}
|
|
1425
|
+
}),
|
|
1426
|
+
save: (memory) => files.writeAtomic(statePath, JSON.stringify(Schema.encodeSync(ReviewMemory)(memory), null, 2))
|
|
1427
|
+
}
|
|
1291
1428
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1429
|
+
});
|
|
1430
|
+
const rounds = yield* Ref.get(lastRound);
|
|
1431
|
+
const left = result.issues;
|
|
1432
|
+
// Lenses and the judge always name themselves: a finding with no
|
|
1433
|
+
// lens is a gate's (target gates, oracle, perimeter, frozen tests).
|
|
1434
|
+
const gateLeft = left.filter((issue) => issue.lens === undefined);
|
|
1435
|
+
if (gateLeft.length > 0) {
|
|
1436
|
+
return yield* failed(story, `its gates are still red after ${rounds} story review round${rounds === 1 ? "" : "s"}:\n${issueLines(ReviewResult.make({ issues: gateLeft }))}`);
|
|
1437
|
+
}
|
|
1438
|
+
const judgeLeft = left.filter((issue) => issue.lens === "judge" && (!deferring || isBlocking(issue)));
|
|
1439
|
+
if (judgeLeft.length > 0) {
|
|
1440
|
+
return yield* failed(story, `judge not cleared after ${rounds} story review round${rounds === 1 ? "" : "s"}:\n${issueLines(ReviewResult.make({ issues: judgeLeft }))}`);
|
|
1441
|
+
}
|
|
1442
|
+
const waiting = deferring ? left : [];
|
|
1443
|
+
for (const issue of waiting) {
|
|
1444
|
+
deferred.push(deferredLine("story review", issue));
|
|
1445
|
+
}
|
|
1446
|
+
const carried = deferring ? [] : left.filter((issue) => issue.lens !== "judge");
|
|
1447
|
+
if (carried.length > 0) {
|
|
1448
|
+
yield* appendFindings(story, "carried: review rounds ran out", ReviewResult.make({ issues: carried }));
|
|
1449
|
+
}
|
|
1450
|
+
return judge === undefined
|
|
1451
|
+
? undefined
|
|
1452
|
+
: `judge cleared (story review round ${rounds}${waiting.length === 0 ? "" : `, ${waiting.length} finding(s) deferred`})`;
|
|
1453
|
+
});
|
|
1454
|
+
let judgeNote;
|
|
1455
|
+
let inPlace = false;
|
|
1456
|
+
const storyDiff = reviewAtStory ? yield* git.diffVsBase(epicBranch) : "";
|
|
1457
|
+
if (reviewAtStory && storyDiff.trim().length > 0) {
|
|
1458
|
+
judgeNote = yield* stage(laneOf(story), `story ${story.id}: review`, reviewStory());
|
|
1459
|
+
}
|
|
1460
|
+
else if (options.judge !== undefined) {
|
|
1461
|
+
const judge = options.judge;
|
|
1462
|
+
for (let round = 1; round <= judgeRounds; round += 1) {
|
|
1463
|
+
const { verdict, empty } = yield* judgeOnce(judge, round);
|
|
1302
1464
|
// Deferring, a verdict with nothing blocking clears: what it found
|
|
1303
1465
|
// waits for the follow-up round instead of another revision.
|
|
1304
1466
|
if (verdict.isClean || (deferring && !verdict.issues.some(isBlocking))) {
|
|
@@ -1384,6 +1546,7 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
1384
1546
|
status: "merged",
|
|
1385
1547
|
...(last === undefined ? {} : { executor: last })
|
|
1386
1548
|
}));
|
|
1549
|
+
yield* files.remove(reviewStatePath(options.stateDir, story.id));
|
|
1387
1550
|
const inherited = inheritedByStory.get(story.id);
|
|
1388
1551
|
return StoryOutcome.make({
|
|
1389
1552
|
id: story.id,
|
|
@@ -1409,7 +1572,9 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
|
|
|
1409
1572
|
: { [attr.session]: context.trace.runId, [attr.run]: context.trace.runId })
|
|
1410
1573
|
});
|
|
1411
1574
|
const annotated = (story, effect) => Object.entries(storySpanAttributes(story)).reduce((acc, [key, value]) => Effect.annotateSpans(key, value)(acc), effect);
|
|
1412
|
-
const attempt = (story) => annotated(story, stage(laneOf(story), `story ${story.id}`,
|
|
1575
|
+
const attempt = (story) => annotated(story, stage(laneOf(story), `story ${story.id}`,
|
|
1576
|
+
// A story that ends failed starts its review over next time (ADR 0044).
|
|
1577
|
+
Effect.tapError(runStory(story), () => Effect.ignore(files.remove(reviewStatePath(options.stateDir, story.id)))), {
|
|
1413
1578
|
kind: "AGENT",
|
|
1414
1579
|
root: true,
|
|
1415
1580
|
attributes: storySpanAttributes(story)
|