opencode-plugin-flow 7.1.0 → 7.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/CHANGELOG.md +23 -0
- package/README.md +4 -3
- package/dist/index.js +99 -36
- package/dist/index.js.map +4 -4
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ One short entry per release, written for users deciding whether to upgrade.
|
|
|
6
6
|
|
|
7
7
|
No changes yet.
|
|
8
8
|
|
|
9
|
+
## [7.2.0] - 2026-08-10
|
|
10
|
+
|
|
11
|
+
Measured assurance lore makes Flow's evidence boundary visible at close and adds a
|
|
12
|
+
paired way to test whether the workflow earns its cost.
|
|
13
|
+
|
|
14
|
+
- Close delivery now derives tiered assurance for every completion check: runtime
|
|
15
|
+
enforced, host attested, caller declared, or model judged. The projection names
|
|
16
|
+
its limitations instead of presenting unlike evidence as equally certain. The
|
|
17
|
+
Session v5 schema and public command/tool inventory are unchanged.
|
|
18
|
+
- Eval reports now count workflow ceremony and evidence interventions, and an
|
|
19
|
+
ungated adjacent-defect scenario gives independent review a defect that
|
|
20
|
+
implementation is explicitly not authorized to repair.
|
|
21
|
+
- `bun run benchmark` runs seed-shuffled Flow and ordinary OpenCode arms against the
|
|
22
|
+
same hidden-graded tasks, then compares correctness, false completion, messages,
|
|
23
|
+
tokens, duration, and cost. The benchmark is exploratory and does not qualify a
|
|
24
|
+
release.
|
|
25
|
+
|
|
26
|
+
Install or update:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
opencode plugin opencode-plugin-flow@7.2.0 --global --force
|
|
30
|
+
```
|
|
31
|
+
|
|
9
32
|
## [7.1.0] - 2026-07-28
|
|
10
33
|
|
|
11
34
|
The last route to a dishonest `completed` closure is closed, and the two claims
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ expensive, and it is overhead when it is not.
|
|
|
40
40
|
Install the exact npm release through OpenCode:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
opencode plugin opencode-plugin-flow@7.
|
|
43
|
+
opencode plugin opencode-plugin-flow@7.2.0 --global --force
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Omit `--global` for project scope. Version pins are exact and never update on
|
|
@@ -51,7 +51,7 @@ The equivalent manual project configuration is:
|
|
|
51
51
|
```json
|
|
52
52
|
{
|
|
53
53
|
"$schema": "https://opencode.ai/config.json",
|
|
54
|
-
"plugin": ["opencode-plugin-flow@7.
|
|
54
|
+
"plugin": ["opencode-plugin-flow@7.2.0"]
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -128,7 +128,8 @@ you granted.
|
|
|
128
128
|
implicitly — Flow reports the blocker and waits for an explicit retry or an
|
|
129
129
|
independent-feature choice. The last passing feature allows closure, and every
|
|
130
130
|
accepted close returns a delivery summary derived from recorded state: each
|
|
131
|
-
feature's attempts, latest outcome, and
|
|
131
|
+
feature's attempts, latest outcome, terminal findings, and tiered assurance with
|
|
132
|
+
explicit limitations.
|
|
132
133
|
|
|
133
134
|
Findings keep stable ids across retries, and a failed review must carry every
|
|
134
135
|
still-live finding forward — the runtime rejects a submission that drops one. A
|
package/dist/index.js
CHANGED
|
@@ -2355,69 +2355,132 @@ var StatusInputSchema = z.object({
|
|
|
2355
2355
|
}).strict();
|
|
2356
2356
|
|
|
2357
2357
|
// src/application/delivery.ts
|
|
2358
|
-
var
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2358
|
+
var LIMITATIONS = [
|
|
2359
|
+
"Artifact paths and the canonical gate are caller declarations; Flow validates binding, not completeness or fitness.",
|
|
2360
|
+
"Goal alignment, scope discipline, evidence completeness, requirement coverage, test adequacy, and review substance remain model judgments.",
|
|
2361
|
+
"Freshness holds when review is accepted; an archive does not attest the current workspace."
|
|
2362
|
+
];
|
|
2363
|
+
function currentRun2(session, featureId) {
|
|
2364
|
+
return session.runs.findLast((run) => run.featureId === featureId && run.state !== "superseded");
|
|
2365
|
+
}
|
|
2366
|
+
function assuranceProjection(session) {
|
|
2367
|
+
if (!session.closure)
|
|
2368
|
+
throw new Error("Assurance requires a recorded closure.");
|
|
2369
|
+
const complete = session.closure.kind === "completed";
|
|
2370
|
+
const features = session.plan?.features ?? [];
|
|
2371
|
+
const runs = features.flatMap((feature) => {
|
|
2372
|
+
const run = currentRun2(session, feature.id);
|
|
2373
|
+
return run ? [run] : [];
|
|
2374
|
+
});
|
|
2375
|
+
const accepted = runs.flatMap((run) => {
|
|
2376
|
+
const ids = new Set(run.reviews.filter((review) => review.result?.verdict === "passed").flatMap((review) => review.validationIds));
|
|
2377
|
+
return run.validations.filter((observation) => ids.has(observation.id) && isValidationEligible(observation));
|
|
2378
|
+
});
|
|
2379
|
+
const check = (id, label, tier, satisfied, explanation) => ({
|
|
2380
|
+
id,
|
|
2381
|
+
label,
|
|
2382
|
+
tier,
|
|
2383
|
+
status: complete ? satisfied ? "satisfied" : "unsatisfied" : "not-applicable",
|
|
2384
|
+
explanation: complete ? explanation : `${session.closure?.kind} closure makes no completion claim.`
|
|
2385
|
+
});
|
|
2386
|
+
const completed = features.filter((feature) => isFeatureComplete(session, feature.id)).length;
|
|
2387
|
+
const passing = runs.filter((run) => run.reviews.some((review) => review.result?.verdict === "passed")).length;
|
|
2388
|
+
const structural = session.plan !== null && features.length > 0 && completed === features.length && passing === features.length && runs.some((run) => run.reviews.some((review) => review.kind === "final" && review.result?.verdict === "passed")) && !runs.some((run) => (run.reviews.at(-1)?.result?.findings ?? []).some((finding) => finding.severity === "blocking"));
|
|
2389
|
+
const checks = [
|
|
2390
|
+
check("recorded-completion", "Recorded completion", "ts-enforced", structural, `${completed}/${features.length} features and ${passing}/${features.length} independent reviews pass, including a final review with no terminal blocker.`),
|
|
2391
|
+
check("accepted-validation", "Accepted validation", "host-attested", runs.length === features.length && runs.every((run) => accepted.some((observation) => observation.runId === run.id)), `${runs.filter((run) => accepted.some((item) => item.runId === run.id)).length}/${features.length} terminal runs have eligible host evidence accepted by review.`)
|
|
2367
2392
|
];
|
|
2393
|
+
const gate = session.plan?.gate;
|
|
2394
|
+
checks.push(gate === undefined ? {
|
|
2395
|
+
id: "canonical-gate",
|
|
2396
|
+
label: "Canonical gate",
|
|
2397
|
+
tier: "caller-declared",
|
|
2398
|
+
status: "not-applicable",
|
|
2399
|
+
explanation: "This legacy plan declared no canonical gate."
|
|
2400
|
+
} : check("canonical-gate", "Canonical gate", "host-attested", accepted.some((observation) => observation.command === gate && observation.scope === "broad"), `${JSON.stringify(gate)} must have eligible broad evidence accepted by review.`));
|
|
2401
|
+
const declared = session.plan?.externalEvidence;
|
|
2402
|
+
const missing = unsatisfiedExternalEvidence(session).length;
|
|
2403
|
+
checks.push(declared === undefined ? {
|
|
2404
|
+
id: "external-evidence",
|
|
2405
|
+
label: "Declared external evidence",
|
|
2406
|
+
tier: "caller-declared",
|
|
2407
|
+
status: "not-applicable",
|
|
2408
|
+
explanation: "This legacy plan declared no external-evidence obligations."
|
|
2409
|
+
} : check("external-evidence", "Declared external evidence", declared.length === 0 ? "caller-declared" : "host-attested", missing === 0, `${declared.length - missing}/${declared.length} declared obligations have eligible evidence on their declared host with named cases passing.`));
|
|
2410
|
+
return {
|
|
2411
|
+
conclusion: !complete ? "completion-not-claimed" : checks.some((item) => item.status === "unsatisfied") ? "completion-unsupported" : "completion-supported",
|
|
2412
|
+
checks,
|
|
2413
|
+
limitations: [...LIMITATIONS]
|
|
2414
|
+
};
|
|
2368
2415
|
}
|
|
2416
|
+
var TIER_LABELS = {
|
|
2417
|
+
"ts-enforced": "TS-enforced",
|
|
2418
|
+
"host-attested": "host-attested",
|
|
2419
|
+
"caller-declared": "caller-declared"
|
|
2420
|
+
};
|
|
2369
2421
|
function formatReport(delivery) {
|
|
2370
|
-
const
|
|
2422
|
+
const lines = delivery.features.flatMap((feature) => [
|
|
2423
|
+
`- ${feature.id} — ${feature.title}`,
|
|
2424
|
+
` attempts: ${feature.attempts}; latest state: ${feature.latestState}`,
|
|
2425
|
+
` outcome: ${feature.outcomeSummary ?? "none recorded"}`,
|
|
2426
|
+
...feature.terminalFindings.length === 0 ? [" terminal findings: none"] : [
|
|
2427
|
+
" terminal findings:",
|
|
2428
|
+
...feature.terminalFindings.map((finding) => ` - ${finding.severity}: ${finding.summary}`)
|
|
2429
|
+
]
|
|
2430
|
+
]);
|
|
2371
2431
|
return [
|
|
2372
2432
|
`Goal: ${delivery.goal}`,
|
|
2373
2433
|
`Closure: ${delivery.closure.kind}${delivery.closure.summary ? ` — ${delivery.closure.summary}` : ""}`,
|
|
2374
2434
|
`Progress: ${delivery.progress.completed} of ${delivery.progress.total} features complete`,
|
|
2375
2435
|
"Features:",
|
|
2376
|
-
...
|
|
2436
|
+
...lines,
|
|
2437
|
+
`Assurance: ${delivery.assurance.conclusion.replaceAll("-", " ")}`,
|
|
2438
|
+
"Assurance checks:",
|
|
2439
|
+
...delivery.assurance.checks.map((item) => `- ${item.status} [${TIER_LABELS[item.tier]}] ${item.label}: ${item.explanation}`),
|
|
2440
|
+
"Assurance limitations:",
|
|
2441
|
+
...delivery.assurance.limitations.map((item) => `- ${item}`),
|
|
2377
2442
|
"Artifacts as reported by Flow from caller declarations, not an exact or exhaustive Git delta:",
|
|
2378
|
-
`- latest attempts: ${
|
|
2379
|
-
`- superseded attempts only: ${
|
|
2443
|
+
`- latest attempts: ${delivery.reportedArtifacts.latestAttempts.join(", ") || "none reported"}`,
|
|
2444
|
+
`- superseded attempts only: ${delivery.reportedArtifacts.supersededAttemptsOnly.join(", ") || "none reported"}`
|
|
2380
2445
|
];
|
|
2381
2446
|
}
|
|
2382
2447
|
function deliveryProjection(session) {
|
|
2383
|
-
if (!session.closure)
|
|
2384
|
-
throw new Error("
|
|
2385
|
-
|
|
2386
|
-
const
|
|
2387
|
-
const featureRuns = planFeatures.map((feature) => ({
|
|
2448
|
+
if (!session.closure)
|
|
2449
|
+
throw new Error("Delivery requires a recorded closure.");
|
|
2450
|
+
const features = session.plan?.features ?? [];
|
|
2451
|
+
const grouped = features.map((feature) => ({
|
|
2388
2452
|
feature,
|
|
2389
2453
|
runs: session.runs.filter((run) => run.featureId === feature.id)
|
|
2390
2454
|
}));
|
|
2391
|
-
const
|
|
2392
|
-
const latestArtifacts = new Set(
|
|
2393
|
-
const allArtifacts = new Set(session.runs.flatMap((run) => run.artifactsChanged.map((
|
|
2394
|
-
const completed = planFeatures.filter((feature) => isFeatureComplete(session, feature.id)).length;
|
|
2455
|
+
const latest = grouped.flatMap(({ runs }) => runs.slice(-1));
|
|
2456
|
+
const latestArtifacts = new Set(latest.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2457
|
+
const allArtifacts = new Set(session.runs.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2395
2458
|
const delivery = {
|
|
2396
2459
|
goal: session.goal,
|
|
2397
|
-
closure: {
|
|
2398
|
-
|
|
2399
|
-
|
|
2460
|
+
closure: { kind: session.closure.kind, summary: session.closure.summary },
|
|
2461
|
+
progress: {
|
|
2462
|
+
completed: features.filter((feature) => isFeatureComplete(session, feature.id)).length,
|
|
2463
|
+
total: features.length
|
|
2400
2464
|
},
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
const latest = runs.at(-1);
|
|
2404
|
-
const terminalResult = latest?.reviews.at(-1)?.result;
|
|
2465
|
+
features: grouped.map(({ feature, runs }) => {
|
|
2466
|
+
const run = runs.at(-1);
|
|
2405
2467
|
return {
|
|
2406
2468
|
id: feature.id,
|
|
2407
2469
|
title: feature.title,
|
|
2408
2470
|
attempts: runs.length,
|
|
2409
|
-
latestState:
|
|
2410
|
-
outcomeSummary:
|
|
2411
|
-
terminalFindings:
|
|
2412
|
-
severity
|
|
2413
|
-
summary
|
|
2471
|
+
latestState: run?.state ?? "not-started",
|
|
2472
|
+
outcomeSummary: run?.summary ?? null,
|
|
2473
|
+
terminalFindings: run?.reviews.at(-1)?.result?.findings.map(({ severity, summary }) => ({
|
|
2474
|
+
severity,
|
|
2475
|
+
summary
|
|
2414
2476
|
})) ?? []
|
|
2415
2477
|
};
|
|
2416
2478
|
}),
|
|
2417
2479
|
reportedArtifacts: {
|
|
2418
2480
|
latestAttempts: [...latestArtifacts].sort(),
|
|
2419
2481
|
supersededAttemptsOnly: [...allArtifacts].filter((path) => !latestArtifacts.has(path)).sort()
|
|
2420
|
-
}
|
|
2482
|
+
},
|
|
2483
|
+
assurance: assuranceProjection(session)
|
|
2421
2484
|
};
|
|
2422
2485
|
return { ...delivery, report: formatReport(delivery) };
|
|
2423
2486
|
}
|
|
@@ -5053,4 +5116,4 @@ export {
|
|
|
5053
5116
|
plugin_default as default
|
|
5054
5117
|
};
|
|
5055
5118
|
|
|
5056
|
-
//# debugId=
|
|
5119
|
+
//# debugId=50DAD2770F6274F964756E2164756E21
|