# Plugin Refactor Notes
This is a short working note, not the plugin architecture record.
Use these source records for durable details:
- module inventory: `src/plugin/docs.md`
- runtime dataflow, lifecycle, and commit points: `docs/plugins/runtime.md`
- security model, proof types, and redaction rules: `docs/plugins/security.md`
- required coverage and fixture policy: `docs/plugins/testing.md`
- active refactor queue and latest-slice notes: `src/plugin/refactor_todos.md`
- historical migration ledger:
`../../archive/plugin_refactor_todos_26-5-30_after-redact-paired-discard-debug.md`
## Current Boundary Review
`src/plugin` keeps the caller-facing facade in `mod.rs`; internals are split by
authority boundary:
- `abi`: WIT vocabulary, generated bindings, and generated-to-Alma decode
- `policy`: config, manifest, capability, scheduler, and workspace policy
- `runtime`: component bytes, runtime limits, lifecycle, and Wasmtime adapter
- `host`: per-update imports, handles, effects, workspace I/O, and task-style
workspace observation infrastructure
- `operational`: redacted operational diagnostic vocabulary
- `trace`: redacted replay/telemetry facade split by event envelope,
effect/status payloads, workspace payloads, proposal receipts, display
adapters, and tests
`abi/decode` is a facade plus responsibility modules: validated runtime decode
limits, closed diagnostic field labels, redacted decode errors, bounded scalar
proofs, workspace-path proofs, WIT capability references, buffer edit requests,
and decode-boundary tests. New generated-to-Alma decoders should enter through
the module that owns the first proof they construct, keeping generated WIT
types at the adapter edge and preserving redacted field/error vocabulary.
`policy/config` is a facade plus responsibility modules: registry validation
and enabled-entry load proofs, static config entries and effective grant
projection, stable identity grammar, current WIT-world proof, component-path
shape validation, proposal review policy, and config-boundary tests. New
static config surfaces should enter through the module that owns the proof or
schema family, with `config/mod.rs` staying as a re-export facade.
`host/import` is a facade plus responsibility modules: snapshot capture,
active session mutation, successful-return and discard batches, resource-table
vocabulary, and layered import errors. New exposed imports should enter through
the session boundary and keep error/resource/batch changes in their owning
modules.
`host/workspace_io` is a facade plus responsibility modules: access proofs and
filesystem-policy tokens, deferred request batches, shared commit errors,
request-budget accounting, owner-facing reports, adapter-facing task-result
vocabulary, task queue batches, queue mutation, queue errors, queue reports,
reservation accounting, and worker queue scheduling. Keep new workspace
authority or queue invariants in the owning module rather than growing the
facade.
`host/effect` is a facade plus responsibility modules: effect admission limit
proofs, commit errors, internal effect/proposal vocabulary, pending and sealed
batch transitions, discard/drain reports, and effect-boundary tests. New effect
variants should enter through typed batch admission methods, not by exposing
the raw storage enum as caller-constructed API.
`trace` is a facade plus responsibility modules: the event envelope and trace
class vocabulary, drained effect/status payloads, workspace I/O and observation
task payloads, proposal receipt payloads, shared redacted display adapters, and
trace golden tests. New trace event classes should enter through `event.rs`;
new redacted payload shapes should live with the boundary they summarize.
ECS plugin modules stay outside `src/plugin`; they adapt sealed plugin batches
into owner systems and do not own runtime authority.
`runtime/wasmtime` is a facade plus responsibility modules: runtime/instance
orchestration, scheduled-update and revocation proofs, store-local host-import
state, update/resource-table vocabulary, workspace observe-task resources,
guest deadline/fuel guards, runtime error projections, and tests. New Wasmtime
behavior should enter through the module that owns the invariant instead of
growing the facade.
The strongest current invariant is the successful guest-return boundary. Host
imports decode guest values into Alma-owned proofs, authorize through captured
host authority, and keep effects, deferred workspace I/O, and workspace observe
task requests discardable until the guest update returns successfully. Failed
updates discard all pending work; successful updates seal batches for later
owner-mediated publication. Empty sealed batches are valid boundary evidence
but are scheduler no-ops, so they cannot spend bounded queue capacity.
Successful-return batches, owner-published batches, and scheduled-update proofs
now construct through local private constructors that enforce same-plugin
identity in release builds at the proof boundary instead of relying on repeated
struct literals or debug-only checks.
Successful-return aggregate debug output is count-shaped at the host-import and
owner-batch boundary, so nested sealed work remains available through typed
accessors without being formatted by outer proofs.
Guest-visible `workspace.observe` is exposed only as the task-based
start/poll/take ABI. `start` reserves an identity-scoped task handle during an
active update, successful-return batches enqueue host-owned filesystem work,
poll remains payload-free, and take is the only guest byte-delivery path. The
runtime-owned task queue supports explicit single-task execution, non-zero
capped batch execution, and an explicitly named all-task drain for callers that
choose unbounded execution.
Dropping the returned task resource before successful guest return removes the
matching pending request and releases its reservation, so a discarded handle
cannot later publish filesystem work.
Persistent task resources retain the workspace path that authorized them and
the host-session boundary returns that path paired with the reserved task handle
as one resource-authority proof. Poll/take revalidates the path against the
current update snapshot before reaching queue state. Denied revalidation
tombstones the resource without freeing the table rep and returns the typed
authorization rejection; a later guest or canonical drop of that invalidated
resource is an idempotent cleanup no-op. Resource-table/tracker divergence is
treated as a typed resource-handle rejection in release builds; resolution and
table-delete errors tombstone the tracked resource before returning, and drop
removes the tracked entry after freeing the table slot. A newly pushed task
resource that would reuse a table rep still recorded by the tracker tombstones
the stale tracker entry, removes the new table entry, and fails closed.
Resources returned by failed updates are tombstoned the same way. The Wasmtime
drop path now classifies task-resource cleanup as active or inactive before it
can cancel an uncommitted host-session request, so host-invalidated drops are a
typed no-op instead of a caller-side `Option` convention. Runtime cleanup for
these tasks is gated by a matching `PluginRevocationReport`,
cancels queue-owned pending and retained state, removes matching uncommitted
active-update requests, and tombstones persistent task resources before unload.
Runtime enqueue rejects sealed task batches whose plugin identity does not
match the runtime instance, and enqueue, single-task execution, capped batch
execution, and drain-all execution require the matching active
`PluginInstanceState`, so wrong-plugin or revoked instances cannot admit
caller-held sealed batches or run filesystem reads. The old blocking
`workspace.observe` import remains absent.
The Wasmtime adapter also exposes a revocation helper that checks runtime/state
identity before mutating host state, then returns the lifecycle revocation proof
and runtime-owned workspace observation task cleanup together.
Runtime-owned shutdown cleanup can also cancel all queued observe-task work,
remove uncommitted active-update task requests, and tombstone persistent task
resources without a revocation proof; caller-held sealed batches remain outside
that cleanup.
Starting a fresh Wasmtime update also tombstones any task resources still marked
as started by a prior abandoned update, so stale uncommitted resources fail
closed instead of surviving by forgotten bookkeeping.
The Wasmtime adapter exposes a helper for the intended owner-loop path: run one
update, enqueue successful-return observe-task batches back onto the producing
runtime queue, and return a `WasmtimePluginScheduledUpdate` proof that keeps
the remaining sealed owner work paired with already-scheduled task evidence.
Post-update task scheduling failures retain that unpublished owner-work proof
and retryable enqueue error in one private-field payload, and the retry API
rebuilds the scheduled-update proof after task scheduling later succeeds.
Scheduled-update owner publication consumes that proof directly, so callers
split owner work only when handing it to the separate owner queues.
The ECS owner-publication helper validates the intent queue and workspace I/O
worker queue into a private paired admission proof before mutating either one.
That proof retains the target queue borrows through publication, and each owner
queue admission owns the admitted sealed batch with its queue storage borrow
until publication, so paired owner work cannot be partially published when one
owner queue is saturated and admitted publication is no longer a raw
validate-then-push convention.
Successful owner publication returns a named redacted report with the admitted
effect and workspace I/O request counts instead of relying on a unit return.
Its retryable error keeps owner work behind typed accessors for retry or
discard, and its consuming split names the queue rejection and owner-work proof.
Debug output is count-shaped and does not format the retained owner batch
internals.
The scheduled-update owner-publication helper consumes the Wasmtime scheduled
update batch directly. On success it returns one named report carrying the
owner-publication report and the already-admitted observe-task report; on
owner-queue failure it keeps the task report with the retryable owner work,
making the partially completed runtime scheduling step explicit. Retrying that
failure consumes the paired owner-queue error and either returns the named
success report or preserves the same paired failure shape.
The consuming success split is `into_parts`, so callers cannot consume only the
scheduled-task half and silently drop owner-publication evidence.
Scheduled-update and scheduled-owner-publication split APIs now return named
part structs instead of positional tuples, preserving the existing ownership
split while making owner work, retryable owner errors, discard evidence, and
already-scheduled task evidence explicit at call sites.
Successful-return and owner-batch split APIs now follow the same pattern:
`PluginHostImportBatches::into_parts`,
`PluginHostOwnerBatches::into_parts`, and the crate-local owner/task split all
return named boundary values instead of positional tuples. Runtime behavior is
unchanged, but call sites now name editor effects, workspace I/O, and runtime
task work at the point where authority is handed to the next owner.
Retryable low-level enqueue errors now use the same named-boundary shape for
their consuming splits: plugin intent queue, workspace I/O worker queue, and
workspace observe-task queue errors expose named queue-error and batch fields
instead of `(source, batch)` tuples. Runtime behavior, redaction, and retry
semantics are unchanged.
The remaining authority-bearing runtime splits now use named boundary values as
well: active-update setup names the captured host snapshot and update handles,
task-resource authority names the reserved handle and authorizing path, and
Wasmtime revocation cleanup names lifecycle revocation separately from
runtime-owned task cleanup. Runtime behavior is unchanged, but callers no
longer split those proofs by tuple position.
Abandoning post-update task scheduling or scheduled-update owner publication
now consumes the paired proof and returns named redacted discard reports, so
callers do not split cleanup evidence by tuple convention. At the scheduled
owner-publication boundary, already-admitted observe-task evidence is named as
scheduled work, and debug output reports counts instead of retained owner batch
internals or task-handle lists.
Failed-update, owner-work, post-update schedule, and scheduled owner discard
debug output now reports outer identity and counts directly instead of
delegating through nested discard reports.
Observe-task enqueue failures also keep their source and retryable batch paired
behind a private-field payload with a closed public kind and source-specific
accessors, including a pre-queue runtime/batch identity mismatch.
The remaining Wasmtime enqueue mismatch identity accessors now return named
borrowed identity pairs, and the private workspace I/O request-ledger split uses
the same named `into_parts` shape.
Drained effect reports now consume into named owner request batches that retain
the drained plugin identity, so editor-effect and status-owner requests are no
longer separated by positional tuple convention.
Scheduled-update and observe-task enqueue errors expose the same redacted
operational event projection as runtime errors, so future owner-loop callers do
not need to inspect unpublished owner work for diagnostics.
Wasmtime scheduled-update and post-update schedule-error debug output is
count-shaped at the outer runtime boundary, so retained owner batches and
retryable task batches are not formatted by nested convention.
Wasmtime observe-task enqueue failure debug output reports the closed public
kind, relevant identity proofs, optional queue rejection kind, and retained task
count without formatting the retryable sealed task batch.
The Wasmtime store keeps the task queue, persistent task resources, and
active-update task resource reps in one runtime-owned state. Task resource reps
are typed inside the adapter. The active-update started-resource set owns the
record, forget, commit, invalidation, and cancellation-retain transitions for
uncommitted reps. The persistent-resource cap is derived from the same queue
limit that bounds task scheduling as a non-zero proof.
Observe-task queue and pending/sealed batch debug projections are count-shaped;
task handles stay available through explicit task-result and enqueue-report
accessors rather than incidental formatting.
Workspace observe-task reservation accounting is RAII-owned, and its private
counter now treats underflow as an invariant violation in all builds rather than
as a debug-only check.
Sealed observe-task admission now retains the validated pending queue storage
through enqueue, so reservation validation and pending publication stay paired
inside one proof instead of relying on a caller-side validate-then-push
convention.
`workspace_io/observe_tasks` is now a facade plus responsibility modules:
pending and sealed task batches, queue and limit ownership, closed queue error
vocabulary, redacted enqueue, discard, and cancellation reports, and RAII
reservation accounting. Runtime behavior is unchanged.
## Working Rules
- Start capability extensions at `CapabilityAtom`.
- Start exposed host-import extensions at `WitHostImport`.
- Extend authorization through `PluginHostAuthority`, then thread narrower
proofs through context, snapshot, and session boundaries.
- Keep workspace request accounting centered on `workspace_io/budget.rs`.
- Keep deferred workspace I/O batch ownership centered on
`workspace_io/batch.rs`.
- Keep shared workspace I/O commit rejection vocabulary centered on
`workspace_io/error.rs`.
- Keep workspace task batches, queue mutation, queue errors, reports, and
reservation accounting under `workspace_io/observe_tasks/`.
- Keep adapter-facing task result vocabulary in
`workspace_io/observe_result.rs`.
- Keep owner-facing workspace reports and guest read projections in
`workspace_io/report.rs`.
- Keep pending/sealed effect transitions centered on `effect/batch.rs`.
- Keep effect admission limits centered on `effect/limit.rs`.
- Keep effect commit rejection vocabulary centered on `effect/error.rs`.
- Keep snapshot-derived effect payload vocabulary centered on
`effect/proposal.rs`.
- Keep drained and discarded effect evidence centered on `effect/report.rs`.
- Extend runtime trace goldens whenever the trace vocabulary grows.
## Next Review Boundary
The task-based `workspace.observe` surface now exists. The next behavior slice
should not widen workspace authority further; keep it to runtime scheduling
ergonomics or focused invariant hardening unless a new plan changes the public
plugin surface.
Safe follow-up work:
- tighten tests or goldens for existing surfaces
- audit any remaining tuple-shaped public splits where named boundary values
would make retained authority or cleanup evidence harder to separate
accidentally
- integrate the Wasmtime scheduled-update proof into a runtime owner loop
- continue tightening redacted reporting where existing debug output exposes
more detail than the next boundary needs
- update source records when a durable decision changes
- update `PluginHostImportError::operational_rejection` in the same slice as
any new host-import source error
- repeat the identity-string audit when adding new outer presentation,
telemetry, or Wasmtime adapter surfaces