Breaking redesign draft. Not a drop-in replacement for the old Loom bundle.
Loom helps agents organize work and recover its context later. It does not own agent definitions, general engineering skills, models, permissions, Git commands or a mandatory workflow state machine. This repository contains the plugin and its own tests, configuration example, documentation and migration helper.
.loom.yaml: layered settings, external-agent role mapping and live reads.- Automatic, bounded role instructions in the current agent's model context. Native agent definitions, permissions, tools and model settings are untouched.
- Actions shared across sessions in the same project, with optional plans.
- Optional Phase → Wave → Task → Subtask levels and dependency validation.
- Task context with goals, parent/dependency context and recent ledger entries.
- Persistent observations, claims, results, Assessment and QA records.
- Reported completion/reopening, durable attributed questions/answers and complete
relational
full_threadhistory with explicit part/cursor continuation. - Usage documentation returned directly by tool search.
- A bundled native
loomskill, discoverable and loaded on demand. - Seven tools:
loom_agents,loom_action,loom_plan,loom_context,loom_record,loom_assessment, andloom_qa. - Read-only, paginated OpenCode 2.2.0 native Session discovery via
loom_action sessions, optionally filtered by Action; includes native archive status, without changing Sessions.
A conversation needs no Action. An Action needs no plan. A task does not need to pass through fixed review stages. Revision checks prevent lost edits; they are not execution grants. Replanning does not erase earlier evidence or results.
Do not replace or pull this branch into your active OpenCode config root.
The original skills are now preserved separately at
github.com/bateau84/skills (its skills/
Git tree matches the former Loom skills/ tree). Native OpenCode still owns
skill installation and discovery; Loom does not load skills from GitHub.
Export agent definitions, launchers, themes, profiles and any private settings
separately before replacing a config-root checkout. The migration is deliberately
not automatic: legacy exported prompts may reference removed Loom tools.
Current native agent mappings use the migrated definitions; do not reinstall
removed external Loom skills. The bundled loom skill is the usage guide.
See Migration for other installations and legacy exports.
The alpha milestone is useful Research → Worker handoff and fresh-session resumption with trustworthy history, clear tool guidance, and one tested manual backup/restore procedure. The former assignment/delivery sender is retired; its acceptance does not qualify this replacement. A historical real native handoff, completion/replan/removal and independently checked fresh-Session recovery trial also passed its declared scope; it sent no automatic continuation. Successful context reads now survive selection-save failures with a separate truthful outcome. See the alpha policy and evidence limits, coordination direction and manual stopped-writers backup/restore.
Advanced storage recovery, maintenance/capacity modes, strict WAL-growth quotas,
scheduling, dashboards and legacy import are deferred, not alpha prerequisites.
No history-size or whole-process resource guarantee is advertised. The Action
paused, cancelled and completed labels record intent/progress; they do not
interrupt an OpenCode session or certify successful delivery.
Upgrade candidate: OpenCode 2.2.0, with @opencode/plugin@2.2.0
pinned in the source package and lockfile. The isolated installed boot,
scripted-question, Session discovery and compaction checks use the exact
2.2.0 CLI and SDK; see merged PR #199 and the current CI checks.
Only a successful run on the reviewed head qualifies the tested behavior.
Evidence collected under 2.1.1 or the older 2.0.32 host
does not establish 2.2.0 compatibility. The historical 2.0.32 qualification
(PR #182) passed E1–E3 and its negative control with the packed alpha artifact
(SHA-256 ef7dad9a30ade952cd055fe5c637ff19d1b7b75ef8da590cde07fad58e7efd15).
No test claims live-provider or external-MCP coverage.
The separately pinned Code Mode search fixture remains 2.0.29; it cannot
qualify the new host, and 2.0.29 is not a supported SDK/host target. Selective
session-read remains a version-specific source-qualified N/A, never proof of
authorization. Neither workflow upgrades your personal CLI or changes
your active configuration. See
2.2.0 migration and compatibility and
installed-host validation.
Model-routing host boundary: this branch now packages SDK 2.2.0.
The previous native initial-selection implementation was investigated using
CLI 2.1.4 / SDK 2.1.1 and is retained as a historical contract-test
candidate. No current SDK 2.2.0 host pairing, including CLI 2.1.4,
is qualified for managed subscription routing. The production adapter
returns not-connected and the early model.request hook denies such
opt-in requests with LOOM_MODEL_ROUTING_HOST_UNQUALIFIED. Valid,
explicitly disabled routing preserves native OpenCode execution.
Test-only billing attestations do not authorize real provider requests;
see model routing.
Bun support: >=1.4.2 <2.0.0. Use Bun 1.4.2 to reproduce validation.
The exact tested baseline is recorded in .bun-version and used by the CI jobs.
This is a supported minimum based on that tested baseline, not a claim that
1.4.2 was the first release to fix the issue or that every newer release was tested.
Bun 1.3.14 is not supported with the pinned SDK. The local review found that
its missing-module value is not an Error instance. OpenCode's resolver then
fails while probing absent optional tui/rpc entrypoints even though server.ts
resolves. The correction is to stop advertising that runtime, not add dummy TUI
or RPC files, patch the SDK, or ignore resolver failures.
Before testing, check bun --version and bun --revision, select the baseline
with your normal Bun installation method, and run bun run test:native in the
separate plugin checkout. The resolver test checks real server loading and absent
optional entrypoints; an unsupported test runtime fails with a version message.
.bun-version and engines.bun declare the boundary; they do not install or
upgrade Bun, replace OpenCode's embedded runtime, or enforce plugin loading.
Changing the Bun executable on PATH does not establish the runtime used inside a
packaged OpenCode executable. Test that installed host separately using
the boot probe before switching normal work.
The boot probe still proves only loading and equivalent intact empty-ledger checks,
not two-agent behavior, meaningful-record persistence, or graceful teardown.
The capture-gap repair also requires a full OpenCode host restart on first installation; hot-upgrading cannot recover uncertainty discarded by the old implementation. See Evidence pairing. The CI jobs install Loom dependencies from the committed lockfile with frozen installation and a lock-stability check. The separately installed host's dependency graph remains a separate concern. The manual state procedure has isolated meaningful-record restore evidence, not native Session continuity proof.
In a separate plugin checkout, run bun install --frozen-lockfile. Add that checkout
to your existing OpenCode configuration, without replacing your other entries:
{
"plugins": ["/absolute/path/to/loom"]
}Configure external agent names in .loom.yaml; start from
examples/loom.yaml. The file is read at the next relevant
hook or tool call. No background watcher or model request is used for reload.
Precedence, from low to high:
$OPENCODE_CONFIG_DIR/.loom.yaml, or${XDG_CONFIG_HOME:-~/.config}/opencode/.loom.yaml.<project-working-directory>/.loom.yaml.<project-working-directory>/.loom.local.yaml.
Mappings merge, lists replace. agents.<name>: null removes an inherited entry;
enabled: false disables Loom guidance and companion-loader access for that agent. Native agent descriptions
are the default; the Loom description is a coordination-only override. A role can
belong to many agents and an agent can have many roles. Missing configured agents
are reported by loom_agents; no placeholder agents or permissions are created.
roles.<role>.description is returned as
loom_agents.result.role_descriptions[role] for role discovery and native delegation.
The shared map also includes described roles not currently assigned to an agent;
roles without a configured description have no entry. Cross-reference it with
agents[].roles and the agent's availability. Descriptions are coordination
metadata, not injected instructions, automatic matching or permissions.
roles.<role>.instructions remains the additional injected role guidance.
Supported sections are version, agents, roles, instructions, context,
ledger, and optional model-routing, model-classes, and model-aliases.
Unknown settings fail validation. OpenCode still owns provider credentials,
installed model definitions, and native permissions. Historical 2.1.4 candidate behavior: Loom's dormant adapter can assign exact native models and reasoning variants from
verified provider/connection evidence and guards against unknown or unapproved
paid requests at provider dispatch. It never switches providers during
a running native stream. Initial selection now reads the shared,
per-account SQLite quota state across this user's OpenCode processes, and
each managed request needs an atomic reservation before dispatch. Only
native OpenCode ChatGPT OAuth with the known Codex integration evidence is
recognized as included without a separate billing attestation; quota state
alone never authorizes paid or subscription use. A classified native quota,
rate-limit or auth failure updates shared state, but the plugin does not
invent successful request accounting or a provider reset. Once the active
model becomes unavailable, its request is rejected rather than switched or
silently charged to OpenCode's global default.
loom_agents exposes model_routing_runtime and selected/unavailable
agents. See model routing and
shared subscription quota for the
exact host/account-affinity and recovery limits. The current
No current SDK 2.2.0 host is qualified for managed routing, including CLI 2.1.4.
Invalid configuration continues to disable guidance and
automatic capture instead of applying a partial or stale role mapping;
do not treat an invalid-startup configuration as an enforced billing
policy. Diagnostics remain available through loom_agents; native tools
remain usable.
Each file is limited to 64 KiB. Context budgets are characters, not tokens:
there is no fabricated tokenizer-independent max_tokens guarantee. The schema
is a first implementation, not an implementation of every earlier sketch.
The repository-root .okf-profile.yaml configures
okf-mcp document relationships; it is not a Loom agent, model or
provider setting. Start okf-mcp with the repository root as its working
directory. Explicit --profile .okf-profile.yaml loading fails if the
profile is invalid; auto-discovery can instead fall back to untyped links.
Restart okf-mcp after changing the profile.
A separate okf-mcp --validate command checks document conformance in its own
short-lived process; it does not prove the running MCP server has reloaded
the profile. After restarting, inspect graph_integrity.profile_loaded and
query real depends-on/references links. A false value means the graph
is using a default or otherwise inactive profile.
The profile maps observed document headings to references and depends-on
links, with no mandatory relationship-count invariants. It does not establish
runtime-model routing correctness or a validated live documentation graph.
Loading this plugin also registers the native skill loom. Agents can discover
its description and load the guide with OpenCode V2's regular tool:
skill({ id: "loom" })The guide ships inside the plugin and has a real package
path. No separate skill installation, global-directory copy or network fetch is
needed. The public server.ts entrypoint composes the existing core plugin with
this skill; src/plugin.ts remains the internal core implementation.
Only metadata is advertised until OpenCode loads the skill. The V2 autoinvoke
flag is enabled for discovery; it does not inject the full guide at startup.
Native skill permissions still apply, and Loom does not force loading, rewrite
agents or eagerly inject the guide. Companion loaders are separate tools with role checks. The guide is available
without an Action or configured role, including when instructions.enabled is
false. General engineering and review skills remain external.
The skill transform preserves an existing skill with the same ID, including its
visibility settings. Rename or remove an old custom loom skill to use the
bundled one. Reload replays registration without duplicating the entry, and
unloading removes this plugin's contribution, not other skills. Update the guide
alongside changes to Loom's tools. The guide body is read during plugin setup;
an already loaded host needs plugin re-registration and a fresh native skill
load to use an updated body, not just a YAML reread. This documentation change
does not reload or restart the host. The guide describes typed OQ routing through native OpenCode 2.2.0 APIs,
legacy fail-closed manual questions, one-shot best-effort first-answer resume,
and complete stored history.
See collaboration. Historical assignment/delivery records
remain inert; guide loading or configuration cannot authorize/replay old intents.
Start with the tool guide and Research → Worker example. It covers operation inputs, side effects, exact dependency-record retrieval, replanning, review records and fresh-session resumption. Usage is also registered directly in tool descriptions and returned by tool search; agents do not need to fetch this Markdown first. See tool discovery. Live-model usefulness of the guidance remains separate from scripted-host feasibility.
For work worth retaining, optionally create an Action and save a Plan if useful.
Pass the Action and Task IDs in the native delegation message. The receiving
agent calls loom_context once with those IDs to select work and get its context.
Future model turns receive that context automatically for configured agents.
Unconfigured agents get no automatic Loom instructions or context injection,
but can still load the bundled skill.
This is an explicit task selection, not a permission/attachment gate. Native execution is never gated by it. The current implementation intentionally does not infer a child's task from its parent's current selection, which might have changed after dispatch. Fully automatic native handoff remains future work.
Ask uses question, explicit Boolean blocking, and exactly one of
recipient:{kind:"agent"|"role"|"dispatcher"|"user",id?:string} (preferred)
or legacy to_agent (manual). Provide id for agent/role, not for dispatcher/user.
Optional context, context_attachments, reference:{action_id,kind,id}.
Answer uses question_id, answer, optional context_attachments;
attachments is not an alias. The native caller determines author/Session;
Action association is optional and unchanged by answering.
On OpenCode 2.2.0, a typed agent/role OQ from a top-level Session attempts
native subagent delegation, including the host's depth and permission checks.
Blocking direct OQs use foreground native execution and return a single
tool result; nonblocking direct OQs use native background completion.
Neither sends a duplicate Loom answer notice.
Questions raised inside child Sessions reach the dispatching Session, which
can delegate to a suitable role or ask the user. A primary agent must ask
the user via native interaction; Loom cannot independently verify that a
relayed user answer genuinely came from the user. New typed OQs accept answers from the verified direct child Session and agent,
or, for forwarded OQs, only from the child explicitly bound by the dispatcher
using loom_record bind-respondent after native delegation.
Native model choice prefers the specialist's configured model over the
asker's. Legacy to_agent questions
stay fail-closed and manual. Dispatch admission is not execution, answer,
or approval; no automatic retry is promised. Blocking is advisory, not a gate.
Only the FIRST committed blocking legacy v1 or forwarded typed v2 answer qualifies for best-effort Loom resumption of the exact asking Session. Direct typed OQs use native continuation. Later/nonblocking answers do not submit separate Loom resume input. No retries, transfers, recovery replay or consumption guarantee: crash after commit may permanently lose resume. Missing/moved/inaccessible targets fail visibly, never retarget; current native agent/model/permissions remain unchanged. Target checks are not leases; the 5000ms deadline bounds waiting, not cancellation of started I/O. Admission is not processing or completion. Answer durability and separate SYSTEM outcome annotation are independent. General assignment/delivery operations AND views are retired.
loom_record query view:full_thread requires explicit Action/node OR Session
scope. It returns escaped complete stored content, actual authors/IDs/links and
qualifications, with next_cursor and labelled oversized-record parts. Follow the
cursor with the same scope until null; a short page can still be incomplete.
No recursive evidence expansion, invented raw bodies, cross-project/native Session
reads, frozen concurrent snapshot or perpetual-cursor guarantee is provided.
Use loom_record to record useful results, claims, Assessment or QA findings.
Choose relevant skills independently. Use loom_assessment to load ASSESSMENT.md
and loom_qa to load QA.md from the installed skill. The enabled caller needs
the corresponding existing assessment or qa role in .loom.yaml; native
skill permissions still apply. No Action, Plan, prior skill receipt or mandatory
review stage is required. Missing companions are reported, never substituted.
OpenCode's flat <skill-id>.md entries are supported too: any companion lives
under a separate <skill-id>/ sibling directory to avoid mixing review lenses
between flat skills. The bundled loom skill has no companions, so its result
is available:false, not a path error.
See Assessment and QA companions for configuration,
examples and limits. Loading instructions and recording findings are separate;
neither guarantees independent review. loom_record add stores results,
claims, concerns, Assessment and QA under the calling Session when no Action
is selected. session_only:true forces Session scope even if another Action
is selected; existing Action-linked records are unchanged. Reviews still
require a verdict. loom_context with
{"mode":"independent"} works without an Action and reads no Loom work
history, but does not save a selection or erase prior native conversation.
The host observer records start and finish separately, with session/message/call IDs and input/result digests. It never stores raw tool arguments or output. Use the source session and call IDs to find the original result through OpenCode; use explicit result/claim records and artifact references for useful summaries. Missing starts stay unattributed; unfinished starts are not success. An in-flight call retains its original work association when the Session changes tasks.
Action-linked records may cite existing observations in that Action;
Session-only records may cite unscoped observations from their own Session.
Observations come from host capture, not loom_record add. A link is
not proof that a statement is true. A stored review PASS is not an Action PASS.
contract_match means that the plan/goal fingerprint matches, not that code,
external facts or the evidence itself have been revalidated. Records remain
available when a task changes or disappears from a new plan revision.
Data is stored separately from old Loom state under
${XDG_STATE_HOME:-~/.local/state}/loom/actions-v1/<project-key>/ledger.sqlite.
Different projects use different databases; project worktrees can share Action
history. The plugin checks the actual Session location before handling its calls.
Do not sync this private database into source control. It is local history, not a
security boundary against other programs running as your user. Capture can be
disabled with ledger.capture: off. Explicitly recorded text is not redacted;
never put credentials or other secrets in a result, claim or artifact reference.
Before an upgrade, preserve state using the offline procedure. Retired sender tables are preserved without operational hydration or sending. Meaningful populated history has a frozen synthetic preservation fixture; that is not universal migration or installed-host acceptance. No destructive repair or legacy workflow-ledger import is provided. See state and rollback.
See the maintained test, script and CI guide for every entrypoint, fixture, prerequisite, safety boundary and workflow trigger.
Use the Bun version in .bun-version (currently 1.4.2).
bun --version
bun install --frozen-lockfile
bun run typecheck
bun run test:core # Node 22.16+; core tests use actual SQLite
bun run test:native # Bun/SQLite + registered plugin-hook and parser testsTests isolate config and state in temporary directories. No paid inference is required by CI. The native contract test uses a fake host API with real Bun primitives; it does not replace a live OpenCode installation smoke test.
See Design and implementation boundaries and Coordination; the alpha operating policy defines the narrower release scope.