opencode-plugin-flow 7.1.0 → 7.3.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 +42 -0
- package/README.md +4 -3
- package/dist/index.js +143 -59
- package/dist/index.js.map +5 -5
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,48 @@ One short entry per release, written for users deciding whether to upgrade.
|
|
|
6
6
|
|
|
7
7
|
No changes yet.
|
|
8
8
|
|
|
9
|
+
## [7.3.0] - 2026-08-17
|
|
10
|
+
|
|
11
|
+
Manager commands are thin routers, and independent review is measured against a
|
|
12
|
+
planted adjacent defect.
|
|
13
|
+
|
|
14
|
+
- `/flow-auto`, `/flow-plan`, and `/flow-run` now call compact status and load the
|
|
15
|
+
one needed guide through `flow_guidance` instead of shipping the full manuals in
|
|
16
|
+
the always-on command templates. The Session v5 schema and the published command,
|
|
17
|
+
tool, guide, and agent inventory are unchanged.
|
|
18
|
+
- `adjacent-defect-refused` fails any passing review of the planted defect,
|
|
19
|
+
including a silent pass or an advisories-only pass. A committed cassette pins
|
|
20
|
+
`flow_feature_complete` rejecting that plant.
|
|
21
|
+
|
|
22
|
+
Install or update:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
opencode plugin opencode-plugin-flow@7.3.0 --global --force
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## [7.2.0] - 2026-08-10
|
|
29
|
+
|
|
30
|
+
Measured assurance lore makes Flow's evidence boundary visible at close and adds a
|
|
31
|
+
paired way to test whether the workflow earns its cost.
|
|
32
|
+
|
|
33
|
+
- Close delivery now derives tiered assurance for every completion check: runtime
|
|
34
|
+
enforced, host attested, caller declared, or model judged. The projection names
|
|
35
|
+
its limitations instead of presenting unlike evidence as equally certain. The
|
|
36
|
+
Session v5 schema and public command/tool inventory are unchanged.
|
|
37
|
+
- Eval reports now count workflow ceremony and evidence interventions, and an
|
|
38
|
+
ungated adjacent-defect scenario gives independent review a defect that
|
|
39
|
+
implementation is explicitly not authorized to repair.
|
|
40
|
+
- `bun run benchmark` runs seed-shuffled Flow and ordinary OpenCode arms against the
|
|
41
|
+
same hidden-graded tasks, then compares correctness, false completion, messages,
|
|
42
|
+
tokens, duration, and cost. The benchmark is exploratory and does not qualify a
|
|
43
|
+
release.
|
|
44
|
+
|
|
45
|
+
Install or update:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
opencode plugin opencode-plugin-flow@7.2.0 --global --force
|
|
49
|
+
```
|
|
50
|
+
|
|
9
51
|
## [7.1.0] - 2026-07-28
|
|
10
52
|
|
|
11
53
|
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.3.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.3.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
|
@@ -797,34 +797,55 @@ var FLOW_REVIEW_PROMPT = [
|
|
|
797
797
|
function skillBody(id) {
|
|
798
798
|
return getFlowGuidance(id).content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "").trim();
|
|
799
799
|
}
|
|
800
|
-
var
|
|
801
|
-
"
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
800
|
+
var FLOW_AUTO_PROMPT = [
|
|
801
|
+
"# Flow router",
|
|
802
|
+
"Drive the Flow lifecycle only within the user's authorized scope. Stop after planning when implementation was not authorized: $ARGUMENTS",
|
|
803
|
+
[
|
|
804
|
+
'Call `flow_status { request: { view: "compact" } }` first.',
|
|
805
|
+
"If the top-level response is an error, report its exact summary and recovery; stop without another mutation."
|
|
806
|
+
].join(" "),
|
|
807
|
+
[
|
|
808
|
+
'With no session or a draft, load `flow_guidance { id: "flow-plan" }` and follow it.',
|
|
809
|
+
'For an approved ready, running, or blocked feature, load `flow_guidance { id: "flow-run" }` and follow it.',
|
|
810
|
+
"If `nextAction` is `flow_session_close` or compact status contains `archiveRetry`, load `flow-run` the same way."
|
|
811
|
+
].join(" "),
|
|
812
|
+
"",
|
|
813
|
+
FLOW_MANAGER_KERNEL
|
|
814
|
+
].join(`
|
|
815
|
+
`);
|
|
816
|
+
var FLOW_PLAN_PROMPT = [
|
|
817
|
+
"# Flow plan router",
|
|
818
|
+
"Create or revise the Flow plan for: $ARGUMENTS",
|
|
819
|
+
[
|
|
820
|
+
'Call `flow_status { request: { view: "compact" } }` first.',
|
|
821
|
+
"If the top-level response is an error, report its exact summary and recovery; stop without another mutation."
|
|
822
|
+
].join(" "),
|
|
823
|
+
'Load `flow_guidance { id: "flow-plan" }` and follow it.'
|
|
824
|
+
].join(`
|
|
825
|
+
`);
|
|
826
|
+
var FLOW_RUN_PROMPT = [
|
|
827
|
+
"# Flow run router",
|
|
828
|
+
"Execute exactly one approved Flow feature: $ARGUMENTS",
|
|
829
|
+
[
|
|
830
|
+
'Call `flow_status { request: { view: "compact" } }` first.',
|
|
831
|
+
"If the top-level response is an error, report its exact summary and recovery; stop without another mutation."
|
|
832
|
+
].join(" "),
|
|
833
|
+
[
|
|
834
|
+
"If status is `idle` or `planning`, explain that `/flow-run` requires an approved feature and stop.",
|
|
835
|
+
'Otherwise load `flow_guidance { id: "flow-run" }` and follow it.'
|
|
836
|
+
].join(" "),
|
|
837
|
+
"",
|
|
838
|
+
FLOW_MANAGER_KERNEL
|
|
839
|
+
].join(`
|
|
840
|
+
`);
|
|
822
841
|
function compileFlowPromptSurface(surface) {
|
|
823
842
|
switch (surface) {
|
|
824
843
|
case "flow-auto":
|
|
844
|
+
return FLOW_AUTO_PROMPT;
|
|
825
845
|
case "flow-plan":
|
|
846
|
+
return FLOW_PLAN_PROMPT;
|
|
826
847
|
case "flow-run":
|
|
827
|
-
return
|
|
848
|
+
return FLOW_RUN_PROMPT;
|
|
828
849
|
case "flow-status":
|
|
829
850
|
return FLOW_STATUS_PROMPT;
|
|
830
851
|
case "flow-review":
|
|
@@ -2355,69 +2376,132 @@ var StatusInputSchema = z.object({
|
|
|
2355
2376
|
}).strict();
|
|
2356
2377
|
|
|
2357
2378
|
// src/application/delivery.ts
|
|
2358
|
-
var
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2379
|
+
var LIMITATIONS = [
|
|
2380
|
+
"Artifact paths and the canonical gate are caller declarations; Flow validates binding, not completeness or fitness.",
|
|
2381
|
+
"Goal alignment, scope discipline, evidence completeness, requirement coverage, test adequacy, and review substance remain model judgments.",
|
|
2382
|
+
"Freshness holds when review is accepted; an archive does not attest the current workspace."
|
|
2383
|
+
];
|
|
2384
|
+
function currentRun2(session, featureId) {
|
|
2385
|
+
return session.runs.findLast((run) => run.featureId === featureId && run.state !== "superseded");
|
|
2386
|
+
}
|
|
2387
|
+
function assuranceProjection(session) {
|
|
2388
|
+
if (!session.closure)
|
|
2389
|
+
throw new Error("Assurance requires a recorded closure.");
|
|
2390
|
+
const complete = session.closure.kind === "completed";
|
|
2391
|
+
const features = session.plan?.features ?? [];
|
|
2392
|
+
const runs = features.flatMap((feature) => {
|
|
2393
|
+
const run = currentRun2(session, feature.id);
|
|
2394
|
+
return run ? [run] : [];
|
|
2395
|
+
});
|
|
2396
|
+
const accepted = runs.flatMap((run) => {
|
|
2397
|
+
const ids = new Set(run.reviews.filter((review) => review.result?.verdict === "passed").flatMap((review) => review.validationIds));
|
|
2398
|
+
return run.validations.filter((observation) => ids.has(observation.id) && isValidationEligible(observation));
|
|
2399
|
+
});
|
|
2400
|
+
const check = (id, label, tier, satisfied, explanation) => ({
|
|
2401
|
+
id,
|
|
2402
|
+
label,
|
|
2403
|
+
tier,
|
|
2404
|
+
status: complete ? satisfied ? "satisfied" : "unsatisfied" : "not-applicable",
|
|
2405
|
+
explanation: complete ? explanation : `${session.closure?.kind} closure makes no completion claim.`
|
|
2406
|
+
});
|
|
2407
|
+
const completed = features.filter((feature) => isFeatureComplete(session, feature.id)).length;
|
|
2408
|
+
const passing = runs.filter((run) => run.reviews.some((review) => review.result?.verdict === "passed")).length;
|
|
2409
|
+
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"));
|
|
2410
|
+
const checks = [
|
|
2411
|
+
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.`),
|
|
2412
|
+
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
2413
|
];
|
|
2414
|
+
const gate = session.plan?.gate;
|
|
2415
|
+
checks.push(gate === undefined ? {
|
|
2416
|
+
id: "canonical-gate",
|
|
2417
|
+
label: "Canonical gate",
|
|
2418
|
+
tier: "caller-declared",
|
|
2419
|
+
status: "not-applicable",
|
|
2420
|
+
explanation: "This legacy plan declared no canonical gate."
|
|
2421
|
+
} : 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.`));
|
|
2422
|
+
const declared = session.plan?.externalEvidence;
|
|
2423
|
+
const missing = unsatisfiedExternalEvidence(session).length;
|
|
2424
|
+
checks.push(declared === undefined ? {
|
|
2425
|
+
id: "external-evidence",
|
|
2426
|
+
label: "Declared external evidence",
|
|
2427
|
+
tier: "caller-declared",
|
|
2428
|
+
status: "not-applicable",
|
|
2429
|
+
explanation: "This legacy plan declared no external-evidence obligations."
|
|
2430
|
+
} : 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.`));
|
|
2431
|
+
return {
|
|
2432
|
+
conclusion: !complete ? "completion-not-claimed" : checks.some((item) => item.status === "unsatisfied") ? "completion-unsupported" : "completion-supported",
|
|
2433
|
+
checks,
|
|
2434
|
+
limitations: [...LIMITATIONS]
|
|
2435
|
+
};
|
|
2368
2436
|
}
|
|
2437
|
+
var TIER_LABELS = {
|
|
2438
|
+
"ts-enforced": "TS-enforced",
|
|
2439
|
+
"host-attested": "host-attested",
|
|
2440
|
+
"caller-declared": "caller-declared"
|
|
2441
|
+
};
|
|
2369
2442
|
function formatReport(delivery) {
|
|
2370
|
-
const
|
|
2443
|
+
const lines = delivery.features.flatMap((feature) => [
|
|
2444
|
+
`- ${feature.id} — ${feature.title}`,
|
|
2445
|
+
` attempts: ${feature.attempts}; latest state: ${feature.latestState}`,
|
|
2446
|
+
` outcome: ${feature.outcomeSummary ?? "none recorded"}`,
|
|
2447
|
+
...feature.terminalFindings.length === 0 ? [" terminal findings: none"] : [
|
|
2448
|
+
" terminal findings:",
|
|
2449
|
+
...feature.terminalFindings.map((finding) => ` - ${finding.severity}: ${finding.summary}`)
|
|
2450
|
+
]
|
|
2451
|
+
]);
|
|
2371
2452
|
return [
|
|
2372
2453
|
`Goal: ${delivery.goal}`,
|
|
2373
2454
|
`Closure: ${delivery.closure.kind}${delivery.closure.summary ? ` — ${delivery.closure.summary}` : ""}`,
|
|
2374
2455
|
`Progress: ${delivery.progress.completed} of ${delivery.progress.total} features complete`,
|
|
2375
2456
|
"Features:",
|
|
2376
|
-
...
|
|
2457
|
+
...lines,
|
|
2458
|
+
`Assurance: ${delivery.assurance.conclusion.replaceAll("-", " ")}`,
|
|
2459
|
+
"Assurance checks:",
|
|
2460
|
+
...delivery.assurance.checks.map((item) => `- ${item.status} [${TIER_LABELS[item.tier]}] ${item.label}: ${item.explanation}`),
|
|
2461
|
+
"Assurance limitations:",
|
|
2462
|
+
...delivery.assurance.limitations.map((item) => `- ${item}`),
|
|
2377
2463
|
"Artifacts as reported by Flow from caller declarations, not an exact or exhaustive Git delta:",
|
|
2378
|
-
`- latest attempts: ${
|
|
2379
|
-
`- superseded attempts only: ${
|
|
2464
|
+
`- latest attempts: ${delivery.reportedArtifacts.latestAttempts.join(", ") || "none reported"}`,
|
|
2465
|
+
`- superseded attempts only: ${delivery.reportedArtifacts.supersededAttemptsOnly.join(", ") || "none reported"}`
|
|
2380
2466
|
];
|
|
2381
2467
|
}
|
|
2382
2468
|
function deliveryProjection(session) {
|
|
2383
|
-
if (!session.closure)
|
|
2384
|
-
throw new Error("
|
|
2385
|
-
|
|
2386
|
-
const
|
|
2387
|
-
const featureRuns = planFeatures.map((feature) => ({
|
|
2469
|
+
if (!session.closure)
|
|
2470
|
+
throw new Error("Delivery requires a recorded closure.");
|
|
2471
|
+
const features = session.plan?.features ?? [];
|
|
2472
|
+
const grouped = features.map((feature) => ({
|
|
2388
2473
|
feature,
|
|
2389
2474
|
runs: session.runs.filter((run) => run.featureId === feature.id)
|
|
2390
2475
|
}));
|
|
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;
|
|
2476
|
+
const latest = grouped.flatMap(({ runs }) => runs.slice(-1));
|
|
2477
|
+
const latestArtifacts = new Set(latest.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2478
|
+
const allArtifacts = new Set(session.runs.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2395
2479
|
const delivery = {
|
|
2396
2480
|
goal: session.goal,
|
|
2397
|
-
closure: {
|
|
2398
|
-
|
|
2399
|
-
|
|
2481
|
+
closure: { kind: session.closure.kind, summary: session.closure.summary },
|
|
2482
|
+
progress: {
|
|
2483
|
+
completed: features.filter((feature) => isFeatureComplete(session, feature.id)).length,
|
|
2484
|
+
total: features.length
|
|
2400
2485
|
},
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
const latest = runs.at(-1);
|
|
2404
|
-
const terminalResult = latest?.reviews.at(-1)?.result;
|
|
2486
|
+
features: grouped.map(({ feature, runs }) => {
|
|
2487
|
+
const run = runs.at(-1);
|
|
2405
2488
|
return {
|
|
2406
2489
|
id: feature.id,
|
|
2407
2490
|
title: feature.title,
|
|
2408
2491
|
attempts: runs.length,
|
|
2409
|
-
latestState:
|
|
2410
|
-
outcomeSummary:
|
|
2411
|
-
terminalFindings:
|
|
2412
|
-
severity
|
|
2413
|
-
summary
|
|
2492
|
+
latestState: run?.state ?? "not-started",
|
|
2493
|
+
outcomeSummary: run?.summary ?? null,
|
|
2494
|
+
terminalFindings: run?.reviews.at(-1)?.result?.findings.map(({ severity, summary }) => ({
|
|
2495
|
+
severity,
|
|
2496
|
+
summary
|
|
2414
2497
|
})) ?? []
|
|
2415
2498
|
};
|
|
2416
2499
|
}),
|
|
2417
2500
|
reportedArtifacts: {
|
|
2418
2501
|
latestAttempts: [...latestArtifacts].sort(),
|
|
2419
2502
|
supersededAttemptsOnly: [...allArtifacts].filter((path) => !latestArtifacts.has(path)).sort()
|
|
2420
|
-
}
|
|
2503
|
+
},
|
|
2504
|
+
assurance: assuranceProjection(session)
|
|
2421
2505
|
};
|
|
2422
2506
|
return { ...delivery, report: formatReport(delivery) };
|
|
2423
2507
|
}
|
|
@@ -5053,4 +5137,4 @@ export {
|
|
|
5053
5137
|
plugin_default as default
|
|
5054
5138
|
};
|
|
5055
5139
|
|
|
5056
|
-
//# debugId=
|
|
5140
|
+
//# debugId=64BC7BCE73DE931B64756E2164756E21
|