Skip to content

Latest commit

 

History

History
262 lines (172 loc) · 53.8 KB

opencode-sdk-contract.md

File metadata and controls

262 lines (172 loc) · 53.8 KB

OpenCode V2 Plugin Contract

Current measured boundary — tasks 6.2–6.3

Source tasks 7.1–7.3 are parent-accepted at 8831169d3c83c333fdf13d4a4efc743eaf495e2b; 191-test/source and63 CLI evidence, 15-case native and 74-check Stop/handoff remain accepted and were consumed without rerun. The installed/model continuation now records passing worker evidence for selected 6.2 and subsequent 6.3 reconciliation: exact released2.1.1, unchanged accepted packed artifact, genuine OpenAI Sol/high A–D semantics, durable uncertainty/readback, actual child actors and fresh/summary/provider-native recovery, with corrected actual cleanup/17 resource passes reused. Parent acceptance is complete for all 16 implementation tasks; see final acceptance.

Ordinary typed activation remains withheld. The typed evaluation uses the existing internal dependency through an immutable scratch bootstrap importing the installed unchanged package. Actual provider-native replacement replay is same-provider/model only; no cross-route migration or multi-hour guarantee is claimed. The nested plain-decline limitation and original location-reload failure remain failed historical evidence. Native watched-config cleanup is the unload acceptance boundary; registry replacement alone does not prove disposal. All 19 requirements and 73/74corpus dispositions plus CODE_MODE are mapped in the new report. Earlier candidate/unexecuted wording below retains historical scope; no release/operator change is authorized.

Task 7.1–7.3 source candidate — 2026-10-21

Executor codex:oc-loop, assigned only opencode-beads-vuy.10, based on published plan f2d6617619e2c3a35e4aeadd5be0092259b9979c. This section records a working-tree candidate, not parent acceptance, a source commit, installed qualification or activation. Earlier planning and accepted five-operation/170-test evidence below retains its historical scope. The product now declares eleven operations in one native beads namespace with code-mode-only registration; ordinary setup still withholds that registration pending the separate native/installed gates. The owner explicitly raised the Beads support floor to >=1.3.1 during this source work; package release version, dependencies, operator installation and host admission remain unchanged. The published planning baseline now has a parent-owned local support-policy delta.

Owner support correction and CLI contract ledger

Owner decision: “1.3.1 should be the minimum beads version not 1.3.0.” The supported minimum and current qualification target now coincide at 1.3.1. Package beads.minimum, runtime MINIMUM_BD_VERSION, README and positive fixtures use that floor; 1.3.0 is refused before any store/context lookup, even with autoMigrate. Older captures below remain failed historical evidence, neither current qualification nor a current blocker. No compatibility branch or further 1.3.0 execution follows. This supersedes the old dual-version/support/blocker statements in the historical planning sections below.

Existing scripts/install-bd.sh installed official checksum-verified Linux amd64 releases only into /opt/data/cache/scratch/oc-loop-implementation/bin-1.3.0 and bin-1.3.1. Binary identities:

Role Actual version/source reported by binary Binary SHA-256
Historical retired minimum (not supported) 1.3.0 (f45b249ce: HEAD@f45b249ce6b4) fb387d28410390eb9f0ef81f242a0c789c632f0024f58fe6a8b02d3f06ea20ef
Supported minimum/current target 1.3.1 (c1c4b642a: HEAD@c1c4b642ac1c) a8f48d771b9e11eccfced4aed72923b59eda746f7a9b425e7ca1af7a51251653

Historical provisioning and initial commands executed once per then-selected version, from the repository (the 1.3.0 commands are records, not instructions to repeat):

./scripts/install-bd.sh 1.3.0 /opt/data/cache/scratch/oc-loop-implementation/bin-1.3.0
./scripts/install-bd.sh 1.3.1 /opt/data/cache/scratch/oc-loop-implementation/bin-1.3.1
PATH=/opt/data/cache/scratch/oc-loop-implementation/bin-1.3.0:/opt/data/.npm/_npx/60c3515df86f25b1/node_modules/@oven/bun-linux-x64-baseline/bin:$PATH BEADS_ACTOR=codex:oc-loop bun run test:bd-contract
PATH=/opt/data/cache/scratch/oc-loop-implementation/bin-1.3.1:/opt/data/.npm/_npx/60c3515df86f25b1/node_modules/@oven/bun-linux-x64-baseline/bin:$PATH BEADS_ACTOR=codex:oc-loop bun run test:bd-contract

The existing script creates finite disposable Git repositories and a separate HOME/XDG, clears inherited BEADS_DIR, uses explicit cwd, 60-second process bounds, --sandbox on every CLI call and the real executor actor on mutations, then removes those repositories. fixture-owner is an intentionally assigned fixture assignee, never the command author. No real tracker is a fixture. The script now captures every exact argv, cwd, stdout, exit and failure stderr in observed JSON lines. This ledger cites those captures rather than introducing another generator.

Full captures: /opt/data/cache/scratch/oc-loop-implementation/contract-1.3.0.log (SHA-256 84d76a88b653a545fb3b78f5acef229783011d2a3dabb757ba668e83dc9f2373) and contract-1.3.1.log (SHA-256 5855393ab67de6d750489974be832cebe2f6a779103a544d06663d6eaa97182d). Historical 1.3.0 exited1 (59 checks passed, five fixture assertions failed); supported 1.3.1 exited0 (63 checks passed). The assertions were corrected before the 1.3.1 run. The owner retired 1.3.0 support; its old failure remains failed and requires no further qualification or fixture work. Supported 1.3.1 has positive blocked-filter evidence and no demonstrated unsupported core behavior. Only the explicit owner-selected floor changed; no upstream repair was made.

In the table, I/C/P/O identify the historical retired-floor issue repo-g57, child repo-an5.1, parent repo-an5, occupied issue repo-cmo; current equivalents are repo-aa0, repo-3up.1, repo-3up, repo-ki3. A is exactly codex:oc-loop. Substituting these aliases reconstructs the recorded argv; multiline text and full outputs remain in the captures. All listed successful calls exited 0 unless stated otherwise. Product permission resources render their exact fixed subcommand-first argv; writes insert --actor A --sandbox before any -- terminator, reads insert --sandbox there. Product actors derive from actual host session/agent, not this implementation worker's actor.

Surface Runtime argv/evidence in both captures Observed contract and product decision
Ready/list/search <view> --limit 2 --json and filtered variants; ready: --parent P --label contract-label; list additionally --status open; search: --label contract-label --status open -- --assignee=x; assignee calls use --assignee A --limit 2 --json (search adds -- contract) Arrays, filtered child C, limit 2 enforced. Ready assignee query returns [] because I is in_progress; list/search return I. Product defaults 20/max50 and fetches limit+1 where supported. Ready/list completeness is query-scoped; search stays unknown when not demonstrably truncated, because a short filtered response does not prove absence of CLI truncation. Search parent, ready status and blocked assignee/status are not advertised. No version branching.
Blocked blocked --parent P --label contract-label --json --sandbox Historical 1.3.0 returned []; supported 1.3.1 returned C with ordinary issue fields plus blocker fields. The retired 1.3.0 positive case was unverified because its fixture blocker was not created successfully; supported 1.3.1 has positive filter evidence. Product reports bounded returned rows with completeness unknown (false when capped), never claims empty implies globally unblocked. No unsupported --limit flag is sent.
Single-ID get show I --json --sandbox; show contract-not-found --json --sandbox Success is a one-record array preserving notes/acceptance/design and separate audit/owner/assignee. Unknown ID exits 1 with error JSON and stderr, never a synthetic missing record. Product get is sequential, at most25 unique IDs, one approval listing all exact resources and fresh checks before every spawn. Partial success preserves per-ID failures; all failed stays failed.
Direct edges dep list C --direction down --type parent-child --json --sandbox; dep list P --direction up --type parent-child --json --sandbox; unfiltered down call Typed issue arrays: down yields P, up yields C. Unfiltered output preserves discovered-from and parent-child. Current additionally evidences a blocks edge. Product allows one optional qualified CLI type per call, caps after normalizing, preserves unknown status and returns unknown completeness unless capped; no transitive graph or unblocked inference.
Claim update I --claim --json --actor A --sandbox One-record array with assignee=A,status=in_progress; later show confirms author and assignee separately. This is required for acknowledged. Exit0/malformed/wrong actor/state is uncertain, with target and actor. No hidden show or second approval.
Claim conflict update O --claim --json --actor A --sandbox Both exit1, empty stdout. Stderr includes a human diagnostic then JSON {"error":"1 of 1 issues failed to update","failed":[{"id":O,"error":"updating issue: issue already claimed: already assigned to …"}],"schema_version":1}. Product not-claimed requires this single-target structured evidence; arbitrary errors remain uncertain.
Append note update I --append-notes <exact multiline checkpoint> --json --actor A --sandbox One-record array retains prior notes plus newline and exact appended text; fresh show confirms retention. Product requires matching target and appended suffix. Notification failure appends detail without erasing the original diagnostic.
Selected update update C --status blocked --priority 3 --add-label added --remove-label contract-label --json --actor A --sandbox, then separate deferred/open/in_progress status calls Returned records evidence status, priority3 and labels ["added"]; all four nonclosed statuses observed. Product rejects empty updates, closed/ownership/claim changes, conflicting label sets and unknown fields. Requested fields must be evidenced for acknowledgement.
Close close I --reason contract --json --actor A --sandbox Returned one-record array has exact target and status closed. Caller still owns authority and acceptance judgment.
Create create --title --assignee=x --type task --priority 1 --description "contract-marker description" --acceptance "contract acceptance" --design "contract design" --parent P --deps discovered-from:I --labels contract-label --json --actor A --sandbox Generated child ID C; literal title --assignee=x, no assignee, actual created_by A, exact selected fields. Other calls establish bug/feature/chore/epic types. Product uses supported --title, never a free option-like positional, and never auto-assigns. Uncertain creation preserves its identifying query/title; caller searches/gets before considering another write.
Memories/recall/remember memories --json --sandbox [-- contract-marker]; recall contract-knowledge --json --sandbox; remember --key contract-knowledge --json --actor A --sandbox -- <exact text> Version1 metadata plus key→value map; no matches is a successful empty map. Recall found has exact value; missing exits1 with key/value empty/found=false; empty remember exits1 with error, not an empty stored row. Remember actions remembered/updated preserve exact content. Product retains empty-found versus missing fixtures and landed-uncommitted/commit_error/commit_indeterminate branches, without claiming those commit failures were induced in these real runs.
Full prime prime --full --sandbox before and after issue writes Both bounded comparisons were equal; this does not establish independence across all supported project policies/content. Keep full prime and conservative location invalidation for possible issue/memory writes. Existing guard, migration-marker, default-install and setup contracts also ran in disposable repositories.

Historical 1.3.0 fixture failures: adding blocks to a pair already linked discovered-from was correctly refused, causing two downstream positive-blocker assertions to fail; the claim test incorrectly expected [] stdout and different text; the ready test incorrectly expected an in_progress issue. The captures preserve all five failures. Corrections use a separate blocker, the observed conflict diagnostic and ready's actual actionable scope. These historical 1.3.0 failures are not relabeled passing. The now-selected 1.3.1 minimum/current run establishes the positive blocked-filter behavior and passes the aggregate; no obsolete-target repair or inference from help is required. Help/source was discovery only.

Final verification after the owner floor correction

The new floor is implemented in src/guard.ts and package.json only; release version1.0.2, validated1.3.1, dependencies and lockfile remain unchanged. Focused RED minimum131-red.log records26 pass/four failures (old binary admitted, old notice, old README and manifest assertions). GREEN minimum131-green.log records30 pass/zero failures, including 1.3.0 refusal after version alone and 1.3.1 readiness. Positive supported fixtures use1.3.1; retired1.3.0 appears only in rejection fixtures and historical evidence.

One supported1.3.1 contract rerun was justified by the changed runtime floor, using the same command above and unchanged extended script, captured separately as /opt/data/cache/scratch/oc-loop-implementation/minimum131-contract.log: exit0, 63 passed, SHA-256 8b28e3069fbf0bab8051fe28ce47008d7a509a1e7f37020a7d72e1accaa9dfe4. The positive blocked query was exactly bd blocked --parent repo-a5a --label contract-label --json --sandbox, exit0; its returned child repo-a5a.1 has status:open, labels:["contract-label"], blocked_by_count:1, blocked_by:["repo-6oi"]. No1.3.1 core/blocked-filter gap remains from the retired fixture failure. No 1.3.0 command or compatibility repair ran after the owner correction.

Final branch review also tightened declared issue-field normalization and omits optional undefined values so output and readable JSON content agree. These are normalizer corrections in the same module, not a schema validator framework. Their RED minimum131-normalization-red.log is31 pass/one failure; the combined focused GREEN is62/0. Existing operation table cases now check malformed-output branches as well as success; no additional version or permission matrix.

Final cheap checks use new captures minimum131-final-tests.log (190/0, 1895 assertions), minimum131-final-typecheck.log (exit0), and minimum131-package.log (exit0; archive SHA-256 86f781ba6a17949665448e0603ad696afb7eeb6be95311b68ca6e26c105766d7). That is still20 added tests over170; floor rejection/manifest checks retarget existing cases. Package smoke checks all30 command callbacks and17 installed links. Status records registration-plus-execute visibility as observed evidence, with leaf availability explicitly unknown; it never treats execute alone as proof every typed leaf exists. User/repository policy and actual permission identity remain distinct from audit attribution.

Actual native output executor/catalog scratch rerun

A single new scratch adaptation of the existing two probes supplied the initial check:

PATH=/opt/data/.npm/_npx/60c3515df86f25b1/node_modules/@oven/bun-linux-x64-baseline/bin:$PATH bun /opt/data/cache/scratch/oc-loop-implementation/native/candidate-probe.ts

Initial exit0. Pre-floor candidate src/continuity.ts SHA-256 ef9e28be509f35499a183e6a84108d777a3a16236491301a2025ac0b3fac0a8e. All11 actual definitions supply their own input/output schemas through definition, without synthetic schema replacement. Native catalog search returns structured union signatures at tools.beads.get/tools.beads.note. The actual packages/core/src/tool/runtime.ts executor runs inside the copied code-mode result bridge: get acknowledges two separate issue reads, note acknowledges one fixture update and its actual derived actor. These are fixture CLI calls, not real mutations. There is no fallback string/null output schema and no direct-method-only success claim.

Source pin independently checked against the complete cached 2.1.1 tree (ba546ec1598a70db2570e9c6258028d955ba8f58, truncated=false): executed core tool/runtime blob d1c5e29caf157fce941024570bc1fe9d5727621e, code-mode tool-runtime blob 9c179bbfe070959704bc6e750e6e2d0045da2b6a, tool-schema 900556191ca454318de25710df038be96a422035. The copied tool/namespace/tool-error and Promise adapter inputs also match that tree. Original probes are untouched; all copies and this adaptation live only under the new scratch directory. Results /opt/data/cache/scratch/oc-loop-implementation/native/results.json, SHA-256 eaef8c953bd3df7e9671a0aefe01fe87e7761a34aca7d49b5350fb6847fb217c; stderr is empty. Product tests import no host executor and add no dependency or maintained runtime seam. This is a native schema/catalog bridge check, not interpreter, installed-host, model, permission-matcher or lifecycle qualification; .2/.6/.9 retain their acceptance scope.

The same scratch adaptation was rerun after the changed guard floor, then after final normalizer corrections, preserving separate minimum131-results.json and minimum131-final-results.json captures. Final exit0 on continuity SHA-256 0bb76279663fd865ba99247a3f1f71b5442c39b4fbcea7eeac875da21d37515c, guard SHA-256 ebfca3c54960d527c7cdca8193c6310714e408240e01b74db249e15bec10ba18. Final native result bytes are unchanged (SHA-256 eaef8c953bd3df7e9671a0aefe01fe87e7761a34aca7d49b5350fb6847fb217c), and final stderr is empty. Exact final dependency hashes and actual worker session are in native/minimum131-final-inputs.json. These reruns check the changed candidate through the same single scratch seam; they do not create a maintained test/runtime dependency or qualify the installed host. Earlier results and original historical probes are preserved.

Context, guidance and deterministic verification

Guarded full prime is cached separately from per-request actor/capability rendering. Actual request session/agent drives the same executorActor function as typed writes; permission assertions independently retain actual invocation context.agent. Status uses observed request identity/capabilities or unknown, discloses unknown leaf availability and effective registry ownership limits, and never opens a store to invent capability. Native shell remains direct-only. Every possible issue/memory write conservatively invalidates relevant location prime; acknowledged outcomes avoid a redundant generic readback cue. Failed/uncertain/partial outcomes retain targets, actor, original diagnostics and recovery gaps. Task and memory references remain distinct; ordinary unrelated tool errors no longer cause a generic prime refresh, while child/important recovery and external changes retain freshness checks. Owned cleanup clears request observations with the existing session/reference state.

The scope/ownership/stop policy remains in the task-agent prompt; its repeated executor section is removed only from that agent's injected guidance. Detailed project planning/review elaboration moves on demand to existing WORKFLOWS; distinct user/repository policy minimization is unchanged. All30 commands and native override precedence remain, with capability-neutral workflow prompts. CODE_MODE is explicitly linked and packaged: 16 resources plus ADR, 17 links.

Evidence directory /opt/data/cache/scratch/oc-loop-implementation: declaration RED operations-red.log (15 pass/1 fail), first GREEN (16/0), final operation suite (32/0), request-context RED context-red.log (16/1), integrated GREEN (111/0), final full bun test 190 pass/0 fail, bun run typecheck exit0 and bun run test:package exit0. This adds20 tests over the accepted170-test baseline, largely operation-specific table cases and representative request states, not an operations×versions×permissions matrix. Package archive SHA-256 afae8906f44d534a352e8d0fb86469b0ab59b4051e5f5a5e205cb8ef72f658ba; unpacked callbacks and all17 links passed. Strict OpenSpec change/all, planning validator and whitespace checks passed. Worker task markers, review reports, shared/operator integrations and activation remain untouched; the seven active-plan edits are the separately recorded parent support-policy delta. Historical 1.3.0 aggregate failure remains failed and retired; supported 1.3.1 minimum/current qualification passes.

Historical planning correction — Opus S1–S5 / P1–P3

The original REVISE review remains immutable. The correction map records the new local candidate, not independent acceptance. All typed argv/resources preserve subcommand-first prefixes and --sandbox on reads/writes, with actual derived write actor inserted after operation arguments but before any -- terminator. Pre-spawn input/identity/hold/location refusals stay thrown; permission rejection/cancellation remain unchanged and started-write uncertainty survives. Actors derive from actual request/invocation session and agent; audit attribution grants no permission and assertions separately use context.agent.

Future task7.1 authority must explicitly cover existing install-bd.sh into disposable directories and existing bd-contract.ts once per1.3.0/1.3.1 for runtime JSON/mutation/claim/create/close/prime-dependence facts. The SDK ledger will be a table citing captured existing-contract output; help/source is discovery, not passing evidence. No installation or contract run occurred in this correction. Task7.2 reruns existing scratch executor/catalog probes once against the actual candidate and records exact pin/commands/results here; there is no maintained product executor seam. Product tests assert declared outputs/branch invariants and bounded table/shared negatives. Task7.3 uses four representative capability states plus the existing cheap suite/typecheck/package gates. .6 consumes deterministic evidence and adds its own Stop/handoff observation; installed catalog/executor/model confirmation remains .2/.9. No graph/ownership change, implementation, activation, publication or closure follows.

Historical planning correction — broader code-mode architecture (2026-10-21)

The broader Opus assessment and parent pinned-blob verification supersede six-tool sufficiency and the earlier narrow READY. Exact 2.1.1 shell.ts:195 declares native shell codemode:false; tool.ts:236–262 splits direct/code-mode inventories and removes code-mode-only tools when execute is wholly denied. Ordinary shell is a direct model-turn fallback only. Historical direct/code-mode fixture references below describe host hook/permission seams, not proof that the default plugin tools have direct exposure.

The selected unpublished active design section 7 targets eleven typed daily-loop operations with declared outputs, actual host-derived actors visible in context/results, request-capability guidance and targeted freshness. Tasks 7.1–7.3 own this new source delta; historical 3.1 and 6.1 acceptance does not cover it. Keep full prime until measured; do not suppress issue-write refresh until supported prime dependence is established. Planned CODE_MODE adds one resource: 16 resources plus ADR, 17 explicit links; prior 16-link package results remain historical measurements. All 30 human commands/overrides and advanced/admin coverage remain.

The actual actor is derived from the executing session/agent, not a mandatory beads-task-agent identity or delegation topology; permission checks continue using context.agent. Minimum/current Beads remain >=1.3.0 / installed 1.3.1. Unsupported flags, multi-ID show, claim-conflict shapes and prime-state assumptions remain bounded 7.1 checks. Operator 2.0.29 is untouched. The owner-accepted nested plain-decline continuation remains a failed stronger case accepted as a host limitation, not a passing Stop/Cancel test. Existing .2/.6/.9 independently own host, Stop/handoff and installed/model gates. Planning reconciliation only: implementation_authorized=false; no resumed qualification, activation, publication or closure. Earlier source/live records retain their original scope.

Reviewed on 2026-10-11 for the narrow OpenCode boundary used by the published stable package (opencode-beads-2zs). OpenCode 1.x and its @opencode-ai/plugin hook API are no longer supported; 0.10.x and earlier target that line. The current source branch separately prepares an opt-in beta/next candidate on the 2.1 line.

Versions and sources

  • Historical stable 1.0.2 declarations used optional peer range @opencode/plugin >=2.0.26 <3 and development dependency 2.0.26. The only import is type-only (import type { Plugin } from "@opencode/plugin"), so packages install nothing from OpenCode and use the host's runtime.
  • Current beta/next candidate declarations use optional peer range @opencode/plugin >=2.1.1 <2.2 and development typing dependency @opencode/plugin 2.1.1 for development typing only. The exact 2.1.1 runtime target has not been qualified.
  • Authoritative sources: the installed @opencode/plugin 2.0.26 declarations, the plugin guide, the V1 migration guide, plugin configuration, the skills reference, the published OpenAPI document (https://opencode.ai/v2/openapi.json), and @opencode/schema 2.0.26 for event names.
  • Qualification: a throwaway V2 plugin (opencode-beads-2zs.1) and the actual source plugin were loaded into disposable projects on OpenCode 2.0.26. Results were read with opencode debug agents, a JSONL trace from the plugin, and short opencode run sessions. Test sessions were deleted afterwards.

Plugin shape and loading

The package default-exports { id: "opencode-beads", setup }, the object Plugin.define returns; no runtime import is needed. setup returns a cleanup that aborts the event subscription.

OpenCode resolves a package plugin as <name>/server, then <name>. The package exports both specifiers to src/plugin.ts, and the package smoke test checks both. A configured local plugin must be a directory with an index or server entry file; a file path is rejected, relative paths resolve from the config file's directory, and a missing entry is skipped without a log line. Files in .opencode/plugins/ load automatically.

Boundary inventory

Boundary Supported contract and decision
Location ctx.location.directory scopes every bd process. The host's process.cwd() is the user's home directory, so bd is always spawned with an explicit cwd.
Options ctx.options carries the object form { "package": ..., "options": {...} }. The plugin reads luna, autoMigrate, and stealth as strict booleans, and refreshAfterIdleMinutes and taskAgentSteps as non-negative numbers; invalid values fall back to defaults.
Agents ctx.agent.transform(editor => editor.update(id, fn)). The editor has no add; update on an unknown ID creates the agent from OpenCode defaults, including the default permission ruleset. User agents configuration overrides plugin fields one by one, and the editor does not list configuration-only agents, so V1 collision warnings were dropped. The host appends a browser deny rule.
Agent permissions and steps Agent.Info.permissions is an ordered rule list where the last match wins, and agent rules apply after base rules. Shell resources match whole command text with * and ?, and compound commands are checked per command. The task agents append ask rules for git commit *, git push *, bd dolt push *, bd dolt pull *, and bd sync *, each paired with a global-options form such as git * push *. tests/agents.test.ts checks the resulting decisions with a copy of the 2.0.27 matcher (a trailing * also matches nothing). A non-interactive run rejects these (permission.rejected). steps caps a worker invocation (default 100).
Agent lookup ctx.agent.get({ agentID }) returns Agent.Info with mode; it is safe to call inside a session hook (about 1 ms). Subagents are excluded from context; lookup failures fail open.
Context ctx.session.hook("context", event => event.system.push({ type: "text", text })) runs before every agent-loop model request with sessionID and agent. Pushed parts reach the model, are not stored in history, and survive compaction. Subagents launched through the subagent tool run in child sessions with their own sessionID and also pass through this hook. Compaction requests use the separate compaction hook; the plugin registers it only to add Beads state-preservation guidance for sessions that had Beads context. While prime is attached, bd's full-profile and Codex AGENTS.md blocks in the system text are replaced with a pointer (request only).
Events ctx.event.subscribe({ signal }) yields { type, data }. The plugin uses session.compaction.ended (invalidate cached prime) and session.deleted (drop session state), both with data.sessionID. A manual compaction emitted session.compaction.ended, not session.compacted.
Diagnostics V2 has no plugin log API. Rate-limited console.warn lines prefixed [opencode-beads] are emitted, and their failures never affect requests.
Shell There is no $ helper in V2. Bounded process lifecycle control uses Bun.spawn(["bd", "prime"], { cwd, ... }) through a shared runBd runner with timeout and process-group cleanup; node:child_process also works in the host.

Commands and skills

V2 plugin commands are executable (execute calls ctx.session.prompt). Separately, native Markdown and JSON commands support prompt templates, description metadata and argument substitution. The full integration analysis pins current source and records the remaining minimum-host qualification; command retirement was not forced by an absence of V2 prompt templates. Plugin-provided skills need a path. The vendored /beads:* templates were retired instead of ported (opencode-beads-2zs.4). OpenCode V2 discovers the canonical .agents/skills/beads skill installed by bd init natively, and users can load it with @beads.

Compatibility decisions for the historical stable 1.0.2 baseline

The supported line begins at OpenCode 2.0.26, the first version qualified. There is no V1 fallback or dual export: V2 ignores V1 server() exports, and supporting both would mean maintaining two hook implementations. Tests fake only the small context slice the plugin uses (location, options, event, agent.get, agent.transform, session.hook) so behavior can be checked without the host.

When running opencode run from a non-interactive shell during qualification, close stdin (< /dev/null); otherwise the CLI waits indefinitely, which looks like a hang.

Selected beta/next boundary (not yet qualified)

The reconciled design selects N1 on OpenCode >=2.1.1 <2.2, released source ba546ec1598a70db2570e9c6258028d955ba8f58. The package now declares that optional peer range and pins development types to exact @opencode/plugin 2.1.1; the import remains type-only. The host context's existing ctx.app.version field is used to report an unsupported-host diagnostic and skip agent/hook registration outside that range. This source guard and the published declarations do not constitute runtime qualification: exact 2.1.1 remains untested, and future 2.1 patches are admitted candidates subject to checks rather than tested versions. The 2.0.26 runtime evidence above remains historical and does not qualify N1. No stable package version, consumer pin, or host runtime was changed.

Required acceptance covers typed-tool options.permission: shell filtering, actual invocation agent in assertions, canonical fixed-operation resources, save: [] without new broad saved grants, external-directory/location equivalence, holds and cancellation. Command schema/registry accepts string names including the selected oc-beads:<command> namespace; installed UI/dispatch still needs qualification. Compaction only carries acknowledged references and known gaps, never a pending-write queue or same-runner checkpoint. Package assets/registrations/disposal and blocking-child stop races need their own fixtures and installed-host checks. N0 is a historical alternative, not an automatic fallback.

Final review B1 sequencing: task 1.2 prepares the 2.1.1 development types before 1.1’s minimal throwaway host tool/command probes. Current 2.0.26 types lack permission.assert. Adapter-specific fixtures remain in 3.1; fake contexts cannot qualify host filtering or dispatch. Live gates 1.1/4.2/6.2 require a pinned isolated disposable host, initially 2.1.1 within >=2.1.1 <2.2, and later authority to provide/install it. The image CLI package currently reads 2.0.29; the older qualified 2.0.26 results above are historical. Neither qualifies N1, and neither runtime is upgraded by this plan. Missing live prerequisites block live acceptance, not independent source/fixture work.

Expected approval outcomes: save: [] installs no new saved rule even for “always”; repeated ask prompts are intentional and must be explained. Existing shell bd * grants can authorize equivalent typed operations, subject to configured denies; do not broaden scope for convenience. Installed acceptance also checks all 16 explicit resource links under external-directory ask/deny and initial/rebuilt command preservation. The native assessment records config transforms replaying after package transforms, while retaining the bounded public-API collision gate.

The 2.1.1 delta governs relevant fixture migration: SessionCompactionResult.native, metadata.keepID/legacy metadata.recent, fork remapping and native.messages replacement semantics. Keep reference-only hooks; no replacement result or replay window. Tool state/UI metadata are not renamed. Current lifecycle events replace any obsolete session.compacted assumption. Released paginated session.list is unnecessary for known-session recovery. Historical 2.0.32 source evidence remains separate; future admitted 2.1 patches are not claimed tested, and 2.2+ requires another explicit contract review.

Task 3.1/3.2 accepted source and separate activation boundary

Parent acceptance: reviewed the complete source/fixture candidate against f4dddb31eb31cc7b61fb381a5a8740c427be64ba and the owner-corrected CLI responsibility boundary. Standards and spec review found no remaining material source issue. Independently executed 133 tests (0 failures), typecheck, package smoke, strict change validation and whitespace checks. This accepts tasks 3.1/3.2 source only; it does not qualify native host permissions, model behavior or production activation. The execution record opencode-beads-vuy.4 holds the resulting publication identity. Historical review findings retain their original verdicts; this is parent acceptance of the corrected implementation, not a new independent-review verdict.

src/continuity.ts defines only checkpoint/show/memories/recall/remember, with direct argv, shell-filtering metadata, actual invocation agent/session/message/call assertions and save: []. The normal adapter uses runBd; tests override that runner and public host methods, without a separate execution implementation. Actor/cwd/env/force/administration are not model inputs. Matching CLI payloads establish acknowledgement; interrupted or malformed writes remain uncertain with no retry. Memory results retain failed/indeterminate commit fields, and missing-key stdout on a nonzero exit is distinguished from an empty found value. Tests establish plugin-side behavior only.

Attribution correction after targeted review: exact 2.1.1 core/tool.ts lines 264–270 supplies invocation sessionID and agent, independently of model input. These primitives suffice for audit attribution: opencode:<readable-agent>:<12-hex-digest> uses a bounded agent label and SHA-256 of full sessionID, NUL, and full agent ID. The same invocation session/executor keeps its actor across calls; another session or executor has a different actor. Permission evaluation continues to use actual context.agent. Missing invocation identity refuses execution; there is no human Git fallback, model actor parameter, external actor-field requirement or identity service. Attribution is not authentication and never changes assignee: deliberate handoffs still require separately authorized ownership changes and evidence. The earlier actor blocker was overstated.

No-open guard correction: the parent verified the cached where.go, routed.go and main.go Git blobs against the pinned Beads 1.3.1 tree at c1c4b642ac1c08d8c828007a1c2f96e47e43ef7c. The earlier unknown-source-identity statement was incorrect. where.go lines 87–98 can open storage to recover a missing prefix. The shared automatic guard and typed adapter now use bd context --json, not where. Installed context --help documents configuration-only diagnostics; pinned main.go skips its store initialization, context_cmd.go reads configuration, and the proxied variant uses the filesystem context provider. The guard checks .local_version under the reported beads_dir as before. An unsupported or failed context probe produces an explicit probe-failed hold, not silent absence or unsafe fallback. No shadow configuration, environment clearing, alternate database, bootstrap or server was introduced. Help and pinned source were sufficient; no disposable datastore or project-store experiment was needed.

Operative owner correction — upstream responsibility: normal supported Beads routing, including fallback after a local miss, is CLI-owned behavior rather than an observed defect. The owner explicitly withdrew the earlier final-store confinement prerequisite and upstream routing-disable remedy. Checkpoint/show now invoke the installed CLI normally from the actual session/project cwd; no final-store attestation, route-disable flag, backend restriction, local metadata/resolver clone or upstream repair framework is introduced. The plugin does not clear inherited routing environment or claim runtime shell/plugin confinement of internal CLI accesses. Historical review reports and Beads notes remain evidence of prior interpretations, not current authority.

The adapter's boundary is correct invocation context, exact-operation OpenCode shell permission using actual agent/session/message/call identity, save: [], external-directory checks for explicit invocation/location access, executor attribution, cancellation, the existing migration guard and honest outcomes. Session/location and guard are checked again after approval and before spawn. External-directory checks concern the explicit lexical/resolved cwd against host workspace/project roots; internal CLI redirects, backend selection and routing are not additional plugin targets. The guard uses supported bd context --json to obtain beads_dir; it does not decode or snapshot backend metadata/routes. CLI errors remain visible, ambiguous append/remember outcomes are not retried, and memory acknowledgement does not promise database consistency repair or plugin-issued commits.

Source versus activation: one adapter implementation uses the normal runBd default; tests override dependencies only. Ordinary setupBeads registers no continuity tools before successful live 1.1. The internal continuityQualified dependency lets tests exercise that same registration path, without a permanent user activation option or a qualification claim. Source fixtures cover all five requested operations, explicit actor/permission identities, direct argv, denial, location access, guard/cancellation after approval, acknowledgement distinctions and normal inherited routing/backend choice. They do not emulate upstream routing or qualify live host behavior. No source blocker for intended CLI routing remains; parent acceptance and live qualification/activation are separate.

Context now records missing/withheld/failed/empty/ready states separately, aborts invalidated loads, and refreshes at relevant execution/compaction/model/agent transitions. Guard failures diagnose guard_failed and prohibit manual bypass. Write acknowledgements dirty location caches; generic tool failures and child returns supply focused recovery cues without extracting memory or parsing shell text. Session get returns session info directly, whose public location exposes directory, not workspaceID. When available on the host/test slice it checks that directory before supplying context; move/fork/delete discard local references and caches. No private workspace identity is inferred from the public response. Cleanup aborts owned processes/observers and disposes owned registrations, with no final-write guarantee. Only complete instruction bodies demonstrably present in supplied context are reduced; customized local policy survives managed markers.

Compaction hooks append reference/gap guidance only: no Beads call, permission wait, queued persistence, recursive prompt, replacement result or native.messages injection. Fixtures assert that supplied summary/native results, metadata.keepID, legacy metadata.recent, fork-remapped references and changed provider/model fields remain untouched. They do not execute or prove the host's replay/fork/mismatch behavior; the pinned 2.1.1 host owns compatibility and replay, including the fact that native.messages replaces summary replay. Direct/code-mode acknowledgement is handled only inside typed execution; the outer execute hook never repeats it. Live filtering, deny/grant precedence, repeat approval prompts, external access, stop races and replay remain unexecuted qualification/acceptance work.

Tasks 2.2/3.3 source candidate: native registration and diagnostics

Parent source acceptance: the corrected implementation resolves the original Opus plan findings without claiming a new reviewer READY verdict. Parent inspected ordinary registration/status, per-rebuild customization checks, packed delivery and lifecycle changes against baseline 38206ff428838e0290b6d2bb3db5ddc7b7beb9d4, and independently executed 150 tests (0 failures), typecheck, package smoke, strict global OpenSpec and whitespace checks. Packed smoke invokes all 30 native bodies and verifies owned disposal. This accepts tasks 2.2/3.3 source/package/fixtures only; live 1.1/6.2 and privileged continuity activation remain unexecuted. Bead opencode-beads-vuy.5 holds publication identity and limitations.

src/workflows.ts supplies native Promise CommandDefinition bodies for exactly the selected inventory's 29 stems plus oc-beads:status. One ordinary command transform registers every definition, checks abort before adds and retains its owned disposer. Initial command.list() collisions or lookup failures are diagnostic-only: they do not suppress registration. Bodies submit normal session.prompt requests with literal user text, files, agents, skills and delivery (queue/steer), without Claude template evaluation, shell interpolation, permission escalation or synthetic continuation. Typed privileged continuity remains withheld pending separate live 1.1; registering prompt commands does not activate it.

The pinned Promise command declarations expose a synchronous add-only transform. Core command uses map set; state rebuild replays transforms in order. Pinned 2.1.1 plugin/supervisor.ts:99–103 orders pre → packages → post, and plugin/internal.ts:254–259 places ConfigCommandPlugin in post. Its single transform replays refreshed configuration/file commands after package transforms on initial load and config rebuild; removing an override restores our definition. Same-name conflicts with other plugin transforms use normal native ordering and may replace an earlier entry. The package neither promises universal collision prevention nor edits user files or host configuration.

The original Opus resolution review additionally inspected suffix reactivation in the unpinned released cache because that registry file was absent from baseline211. That detail is not pinned 2.1.1 evidence or live qualification. The report retains its original REVISE verdict; this correction is not a second independent review. Installed reactivation/discovery and permissions remain separate unexecuted acceptance work.

The registered status body reuses the existing context controller, persistence gaps and config-only guard. It reports actual registration completion, effective public command names/descriptions, missing expected names, description mismatches and lookup failures. A mismatch is observable evidence of a different effective definition, not owner proof. Public owner identity and identical-description overrides are unavailable; matching names/descriptions do not prove ownership. A failed lookup reports unknown effective entries and does not invent missing names or override findings. Context distinguishes missing, withheld, failed, empty and ready; an in-progress load or unknown session location is explicitly uncertain. Unconfirmed location withholds the probe and discards stale session references. Status never runs prime or a store-opening diagnostic, claims a final write, or reports live qualification from fixture results.

Skill registration always installs the public transform when packaged guidance is readable; the candidate editor's list is checked on every rebuild for a colliding ID/name. There is no initial list collision/unavailability gate. A removed customization therefore allows restoration through native rebuild, while later config transforms keep their precedence. Abort guards and the owned disposer apply to the skill as well. Missing/unreadable packaged guidance is diagnosed without filesystem exception details; accepted asset bodies remain unchanged.

Package smoke resolves both existing exports, compares the unpacked inventory/resource bytes, resolves all 16 explicit links from the installed base and checks a missing installed file. It invokes the actual registration callbacks, verifies all 30 native definitions, executes their prompt bodies with attachments/delivery, and derives the five owned disposers from agent/skill/command registration plus the two registered hooks. Focused fixtures replay a small ordered list of package/config callbacks to check overrides, restoration and owned removal; they do not clone host internals.

The host-owned 2.1.1 skill scan stops at depth six, skips hidden/ignored directories and stops descending when SKILL.md is found. Resource preparation applies ignore globs and samples only ten files; config skill watches watch out-of-root resolved files specifically. Fixtures load explicit assets below hidden/ignored node_modules paths deeper than six levels without emulating scanners/watchers. Explicit package registration and all 16 links do not imply recursive discovery or resource watches. No package watcher is added. External-directory ask/deny, model use and installed namespace dispatch remain unexecuted live 1.1/6.2 checks.

Cleanup is awaitable and idempotent: abort context loads/processes and the event subscription immediately, clear session/location references, then await every owned disposer and observer. Disposal failures are reported; a later setup failure releases earlier registrations. Fixtures include command/skill abort guards, native status cancellation, user config preservation and late-context suppression. They establish source/package obligations, not live unload behavior or a final durable write. Source task markers, acceptance and release/activation state remain parent-owned.

Task 1.1 exact released-host observations — 2026-10-21

This append records new measurements; preceding source acceptances and historical unexecuted statements retain their original scope. Task 1.1 is not qualified: exact 2.1.1 code-mode explicit decline loses the distinct abort outcome. The qualification record supplies identities, per-case results, reproduction, sanitized traces and cleanup. Actor codex:oc-host, actual worker session 01a15c62-d106-7e80-af58-5a47b1032cd4, repository baseline 072429f1a7723ade23e756a921924d9d0acacf31.

The official @opencode/cli-linux-x64-baseline@2.1.1 tarball passed published SRI/SHA-1 and registry-signature checks; extracted executable SHA-256 4595933989548a452e920c971d5931898d7d64e73e039f777ed62c84a18916c9 reports opencode v2.1.1. Official tag/source pin remains ba546ec1598a70db2570e9c6258028d955ba8f58. The real host ran only on loopback with separate HOME/XDG and an initially committed scratch repository under /opt/data/cache/scratch/vuy2-oc-host. A controlled local HTTP/SSE provider fixture drove minimal throwaway tools/commands through native host execution; no production adapter, mock host, external-provider/model acceptance or project Beads probe was used.

Passed measured contracts: whole-shell denial removes direct and code-mode probes from actual model inventories; selective denial leaves discovery intact and blocks the forbidden resource. Assertions carry actual invoking identity, including an original restrictive invocation after the live session default was switched to an allowing agent. Allow/ask and correction feedback propagate without rejected-target execution/retry. save: [] plus “always” leaves the saved list empty and asks again. Fixture-owned shell bd * grants authorize equivalent typed operations and a harmless native-shell stub; configured denies still win. Native grant edits/revocation are re-evaluated, including within one unchanged live session. Native shell and typed probes request the same explicit external-directory and shell resources and execute pwd in the same approved cwd; external correction/deny prevents target execution. The real command registry admits and invokes exact oc-beads:ready with literal arguments and queue delivery.

Failed measured contract: direct explicit decline yields Permission.DeclinedError, an aborted native tool/step and no next provider request. In code mode the same rethrown leaf outcome becomes outer execute completion with inner error metadata and generic Tool.Error text, followed by another provider request. The target does not execute or retry, and metadata.error: true must not be mistaken for target success; nevertheless the distinct decline/abort outcome is lost. Evidence: session ses_ea396cc6affenEp6WisS1KyHef, message msg_15c6933a4001vwxlp9uhAKg3TG, call vuy2_declined-code, cases.jsonl, plugin.jsonl, provider traces and native message readback. This conflicts with the selected preserved-outcome guarantee and the plain-rejection step-ending contract in the pinned Promise permission declaration. The affected path stopped; no upstream/product patch, translator, fallback or activation follows. Parent/owner disposition and subsequently passing authorized host evidence remain required.

All owned host/provider processes and listeners were stopped; operator 2.0.29 executable/configuration hashes match the pre-provisioning record. .2 remains open for parent review. No task markers, continuityQualified, installed-package/model acceptance, child qualification, publication or source acceptance were changed.

Superseding owner decision: retain code mode and accept bounded continuation

The owner accepted the observed OpenCode 2.1.1 code-mode plain-decline continuation limitation while retaining composition. The failed stronger whole-step-termination case above remains failed historical evidence, now a known accepted host limitation; it is not relabeled as passed. The adapter must still propagate assertion rejection unchanged, preserve correction feedback, never execute the rejected target or automatically retry it, and never acknowledge target success. Later operations retain independent permissions. Explicit Stop/Cancel, scope and honest persistence guarantees are not waived.

Remaining qualification/activation is paused for parent review of the broader code-mode planning reconciliation in opencode-beads-wgv, with new source tasks 7.1–7.3. An isolated pinned-host-executor reproduction of our undeclared output-schema defect is recorded under /opt/data/cache/scratch/opencode-codemode-shape-review/; this is fixture evidence, not installed-product/model acceptance, and the defect is NOT covered by the upstream limitation acceptance. Task 1.1 remains unchecked pending parent reconciliation. No product source, activation, public filing, publication or operator change follows from this decision.