Appendix A. As-built deviations that change plugin code (D1–D20)
From next/docs/stage-2/04-kernel-as-built.md §1. "Built wins" means: write code against the built column. "Flippable" means a README request exists; plan for both.
| # | Package | Topic | Built behavior (one line) |
|---|---|---|---|
| D1 | kernel-core | Registration conflicts | provide/define/register conflicts are recorded as activation problems (degraded) with a no-op disposer; invalid_facts, invalid_contract, unknown_command, scope_disposed still reject. A registration is visible once its promise settles. |
| D2 | kernel-core | Nouns, multi contracts, actors | Noun ownership is composition order (ties by plugin id); RESERVED_NOUNS = kernel composition plugin host preference secret; a multi contract may not carry a mutation; every MethodDef carries actors and destructive. |
| D3 | kernel-core | Interceptor order and budget | Priority desc → exact before wildcard → composition order → registration order; the budget covers only the interceptor's own work before next(); an overrun skips it and cannot veto. |
| D4 | kernel-contract | method() / defineService shape |
Inputs must be z.strictObject; no kind means "query"; defineService fills command, cli.name, tool.name. |
| D5 | kernel-contract | Actors, exposure gates, host target | Anonymous loopback callers are human:local via: "anonymous"; expose.cli is not enforced over HTTP; target: "host" is 503 service_unavailable in core today. |
| D6 | cli | CLI tree, --json, exit codes |
--json prints the bare result; {ok:false,error} on error; exits usage 2, no runtime 4, declined confirm 1, SIGINT 130, --timeout 5; a word collision drops the later claimant with a warning. |
| D7 | kernel-loader | Placement | Bundled and path: plugins run in-process; marketplace installs in a worker; worker gaps: declared .default()/.transform() do not reach the handler; kind: custom in a worker is refused. |
| D8 | kernel-loader | Plan-time problems and boot | Collisions and config problems are degraded + detail, not failed loads; boot is sequential in topological order; the composition file is not watched. |
| D9 | kernel-loader | Build outputs are checked | contributes.slots is cross-checked at build; a browser registration absent from app.contributions.json fails the load with contributions_mismatch; kernel/plugins.install has no CLI positional yet. |
| D10 | kernel-loader | Background services | A non-empty contributes.background starts a separate worker; no schedules table; fork --detach disables the original. |
| D11 | kernel-store | Kinds and reducers | Core kinds are snake_case; row kinds turn, interaction, notice are reducer-synthesized and refused as event kinds; $.progress is a legal delta path on every item kind. |
| D12 | kernel-store | Entity events | kernel/<entity>.changed for thread project environment host thread-head interaction thread-annotation, plus kernel/thread.purged, kernel/store.appended, derived kernel/store.rows and kernel/store.changed. |
| D13 | kernel-store | SQL handle and CAS | Rows are SqlResultRow; plugin tables live in plugins/data/<id>/data.db via ctx.storage.openDatabase(migrations); kernel/preferences.set takes expectedUpdatedAt and returns {updatedAt}. |
| D14 | kernel-host | Roles and the host entry | HostRoleName = provider-bridge environment-provider skill-discovery terminal-backend ai-service rpc; defineHostEntry derives commands from the rpc rows; RoleContext.watch is a no-op, openLane is absent. |
| D15 | kernel-host | Env handles and the relay | RootRef.source carries path; turn.completed {status: "interrupted"} is the settlement on exit; core → role rpc from a server tier is not wired yet (service_unavailable). |
| D16 | kernel-ui | Slots, routes, arbitration | A priori slots: root, diff.renderer, source.renderer; every other slot is declared by an occupant's children; arbitration replaces → pin → priority → sorted plugin id, committed in sorted-id waves. |
| D17 | kernel-ui | Services and commands | useService resolves local kernel-ui/* → local container → core scope over one WebSocket; a lost socket is a reload plus a toast; ServiceUnavailable is thrown from the hook. |
| D18 | kernel-ui | Data hooks and CSS | defineRowStore gates on {seq, op}; defineQuery(...).useHandle() is a hook; authored CSS roots every selector at [data-bb-plugin-effect="<id>"]; zod is bundled into every tier. |
| D19 | plugin-sdk | Facade rules | definePlugin({config?, activate}); ctx.agents.configure is watch-held over the _pending copy of threads/agent-config; ctx.realtime.declare + publish is the only channel API. |
| D20 | plugin-sdk | Test harness rules | Four helpers; createAppHarness requires a synchronous setup and root-scoped children; happy-dom is installed by hand; every suite calls expectDerivedSurfaces. |
Superseded at dc07292bf: D15's "not wired yet" — core now passes createHostClients(...).clientFor to the loader (packages/core/src/core.ts). D9's install positional and the dotted-noun grammar moved with the Stage 2 integration (see §0.7).
Appendix B. Known gaps to plan around (H1–H8)
See next/docs/stage-2/04-kernel-as-built.md §6. The ones that touch plugin code: a lost browser socket reloads the page (H1); ctx.hostClient from a server tier is unwired (H5-adjacent, D15); target: "host" methods answer 503 (D5); the cli tier (defineOfflineHandlers) is not built. (D9's missing install positional is closed at dc07292bf: bb plugin install <source> parses, kernel-loader/src/service.ts.)