Skip to content

refactor(runtime): generic frame-context resolver registry#422

Closed
wolfy-j wants to merge 5 commits into
mainfrom
feat/frame-context-resolvers
Closed

refactor(runtime): generic frame-context resolver registry#422
wolfy-j wants to merge 5 commits into
mainfrom
feat/frame-context-resolvers

Conversation

@wolfy-j

@wolfy-j wolfy-j commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Why

Options that decorate a newly-spawned frame's context (today: the network overlay; soon: a filesystem fs_root) were hand-wired into every dispatcher — system/process/manager.go Start() and both runtime/{wasm,lua}/component/function/lifecycle.go Execute() each called netapi.ApplyOverlayPair. That leaks api/net (and would leak api/fs, api/service/otel, ...) into generic machinery, and every new frame option must be added to every dispatcher.

What

ctxapi.FrameResolvers — an ordered registry of resolvers:

type FrameResolver func(ctx, options attrs.Attributes) ([]Pair, error)

Carried on the AppContext (like the interceptor registry), populated once at boot. The two real choke points apply the whole set generically:

  • system/function/functions.go executor() — shared by lua and wasm functions.
  • system/process/manager.go Start().

Adding a frame-context option is now one resolver + one Register line, zero dispatcher edits. The dispatchers no longer import api/net/api/fs.

Network migrated

api/net.OverlayResolver() replaces ApplyOverlayPair (same logic: per-call → app-default precedence, ErrNetworkNotFound for unknown ids) and is registered by the network boot component (order 200). The netapi calls + imports are removed from all three dispatchers.

Performance

Reads are lock-free — an atomic snapshot pointer (copy-on-write under a mutex only in Register), mirroring the interceptor registry's chain.Load(). Benchmarks:

  • no resolvers (the common no-overlay call): 1.4 ns/op, 0 allocs — just the atomic load.
  • 2 resolvers: 145 ns/op — the same per-resolver work as the old direct call.

Race-tested (go test -race).

Not in this PR

  • fs_root registers its resolver on this mechanism (that PR builds on this one).
  • otel span propagation lives in the otel interceptor (not a dispatcher leak); it can migrate to a resolver as a no-risk follow-up.

Tests

  • api/context: registry order, append-to-input, nil-receiver no-op, first-error-stops-and-wraps (errors.Is preserved), duplicate/nil rejection, ctx round-trip, benchmarks.
  • api/net: ApplyOverlayPair tests retargeted to OverlayResolver.
  • system/process: manager network tests now wire the resolver registry, proving the manager applies it generically.

wolfy-j added 5 commits July 4, 2026 13:50
Frame-decorating options (the network overlay, and future ones like a
filesystem root) were hand-wired into every dispatcher: the process manager
and both the wasm and lua function lifecycles each called
netapi.ApplyOverlayPair, leaking api/net into generic machinery and forcing
every new option to touch every dispatcher.

Introduce ctxapi.FrameResolvers: an ordered registry of
(ctx, options) -> ([]Pair, error) resolvers, carried on the AppContext and
populated once at boot. The two real choke points — the function registry
executor (shared by lua and wasm) and the process manager Start — apply the
whole set generically. Reads are lock-free (atomic snapshot, copy-on-write on
register), mirroring the interceptor registry; the no-resolver path is ~1.4ns
with zero allocations.

Network migrates onto it: api/net.OverlayResolver replaces ApplyOverlayPair
and is registered by the network boot component. The dispatchers no longer
import api/net or api/fs; a new frame-context option is one resolver plus one
Register line, with no dispatcher edits.
- Keep api/net.ApplyOverlayPair as a deprecated thin wrapper over
  OverlayResolver so out-of-tree Go callers do not break (codex).
- Network boot component fails loud (ErrFrameResolversMissing) instead of
  silently skipping registration when the registry is absent, so a wiring
  bug cannot degrade the overlay to clearnet (fable #1).
- Remove the now-redundant SetMultiple(task.Context) from the wasm and lua
  Execute handlers: the function registry executor already applies task.Context
  to the same forked frame and is the sole caller (fable #3).
- Drop the speculative FrameResolverOrderFSRoot constant and fs-root comment
  references; this PR migrates only the network overlay (fable #4).
- Fix stale comments and the test section header (fable #5).
@wolfy-j

wolfy-j commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #420. The frame-context resolver commit stack has been moved onto feat/wasm-fs-prefix-option so #420 is now the resolver PR.

@wolfy-j wolfy-j closed this Jul 4, 2026
@wolfy-j wolfy-j deleted the feat/frame-context-resolvers branch July 4, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant