返回 DeepSeek-Reasonix
2026-08-07-spatiotemporal-composability-v2.md
根目录 / docs / superpowers / specs / 2026-08-07-spatiotemporal-composability-v2.md
1 # Spatiotemporal Composability (Plugin/Runtime v2)
2
3 ## Context
4
5 Reasonix already has a solid extension kernel: `Contribution → RuntimeSnapshot`,
6 `RuntimeSet` reverse/idempotent cleanup with generation guards, Builder freeze
7 and activate stages, and `Rebuild` that keeps the old controller on failure.
8
9 This design ports four established runtime-composition mechanisms into that
10 kernel without adopting an external runtime or changing the implementation
11 language:
12
13 1. Unified runtime effect scopes
14 2. Typed, reactive dependency graph
15 3. Generation / epoch component lifecycle
16 4. Verifiable atomic reload (build → activate → publish → drain)
17
18 ## Fixed decisions
19
20 - Upgrade native plugins to `reasonix.io/plugin/v2`
21 - Reject v1 and legacy native manifests (no apiVersion) at install, doctor, boot
22 - Keep existing stdio/HTTP transport; do not rewrite the transport layer
23 - Do not introduce an external runtime dependency
24 - Irreversible external work uses effect receipts; never fake rollback
25 - Receipt recovery is process-local, bounded, and does not promise crash recovery
26 - Preserve system prompt, tool schema, and memory prefix cache-first constraints
27 - Claude compatibility plugin paths are out of scope for v2 native runtime changes
28
29 ## Lifecycle
30
31 Component states:
32
33 ```text
34 Inactive → Preparing → Active → Draining → Inactive
35 Failed (activation or cleanup failure; retains error + receipts)
36 ```
37
38 Component granularity for v1 of this design: one native v2 sidecar runtime
39 package is one component node. Host-owned providers, interceptors, and UI
40 bindings are child contributions of that node. Individual tools are not
41 independent lifecycle nodes yet.
42
43 ## EffectScope
44
45 `RuntimeSnapshot` stays immutable configuration. Live handles live only in an
46 `EffectScope` (default implementation held by `RuntimeSet`):
47
48 | Class | Meaning |
49 | --- | --- |
50 | Reversible | Dispose undoes the effect |
51 | Cancelable | Cancel stops further work; wait for completion on dispose |
52 | Compensatable | Dispose may run a compensate function |
53 | Irreversible | Record receipt only; never claim rollback success |
54
55 Rules:
56
57 - Each effect runs dispose at most once
58 - Within a component: reverse registration order
59 - Across components: reverse topological drain order
60 - Independent components may activate/clean in parallel
61 - Mid-activation failure auto-disposes already tracked effects
62 - Dispose waits for cancelable background work
63 - Cleanup errors become diagnostics; they never skip remaining dispose
64
65 Resources that must track through EffectScope: sidecar process, MCP
66 client/transport, provider stream, watcher, event subscription, UI hub binding,
67 goroutine/background job, session temporary resources, controller cleanup
68 callback.
69
70 ## Snapshot / plan / controller boundary
71
72 ```text
73 RuntimeSnapshot = immutable configuration and dependency view
74 RuntimeSet = live resources owned by one generation (EffectScope)
75 RuntimePlan = transition from old snapshot to new snapshot
76 Controller = owner of the published generation
77 ```
78
79 Live handles (process, connection, watcher, callback, goroutine) must never
80 enter `RuntimeSnapshot`.
81
82 ## Capability contract
83
84 `internal/extensioncontract` is a leaf package shared by `pluginpkg` and
85 `internal/extension`:
86
87 - `CapabilityKey{Namespace, Kind, ID}` — always namespaced
88 - `Capability{Key, Version, SchemaHash}` — stable schema hash required for
89 provider/tool/UI capabilities
90 - `Requirement` extends capability with `VersionRange` and `Optional`
91
92 Versions use `golang.org/x/mod/semver`. Key, kind, version, and schema hash
93 participate in canonical hashing.
94
95 ## Manifest v2
96
97 Native `reasonix-plugin.json` requires:
98
99 ```json
100 {
101 "apiVersion": "reasonix.io/plugin/v2",
102 "name": "example",
103 "version": "2.0.0",
104 "requires": [...],
105 "provides": [...],
106 "runtime": { "command": "...", "intercepts": [], "replaces": [], "capabilities": [] }
107 }
108 ```
109
110 Validation:
111
112 - Missing / v1 / unknown major apiVersion → hard reject
113 - `provides` is the capability ceiling for handshake
114 - Handshake must not declare undeclared capabilities
115 - Declared-but-missing capabilities become `Unavailable` (no forge)
116 - Non-optional missing requirements keep component `Inactive`
117 - Optional missing requirements allow activation with diagnostics
118 - Required dependency cycles fail with full cycle path
119 - Multiple providers for the same `(namespace, kind, id)` need explicit
120 selection or report conflict
121 - Replacement slots stay single-owner; interceptors stay additive by priority
122
123 Claude/Codex compatibility manifests are unchanged.
124
125 ## Dependency graph and RuntimePlan
126
127 Build flow:
128
129 ```text
130 Discover → Parse → Validate → Resolve capabilities → Validate versions/schema
131 → Detect cycles → Topological activate order → Reverse drain order
132 ```
133
134 Deterministic sort keys: dependency rank, scope rank, priority, canonical
135 component ID.
136
137 Epoch identity for a consumer:
138
139 ```text
140 epoch = [capability key, provider component ID, provider version, provider schema hash]
141 ```
142
143 Only epoch changes force consumer reload.
144
145 `RuntimePlan` carries Added / Removed / Reloaded / Unchanged plus ActivateOrder
146 and DrainOrder. Diagnostics record `PrefixChanged` only after comparing the old
147 and new snapshot `CacheHash`, while `ProviderChanged` records provider capability
148 add/remove/reload independently. No-op plans must not change `CacheHash`.
149 Changes should affect only the relevant subgraph (provider, MCP server,
150 interceptor chain, UI hub).
151
152 ## Atomic publish / drain
153
154 ```text
155 Discover → ResolveGraph → CreatePlan → Preflight
156 → ActivateNewGeneration → AwaitReady → PublishController → DrainOldGeneration
157 ```
158
159 - Preflight failure: old runtime untouched
160 - New activation failure: dispose entire new generation scope
161 - Do not publish until new runtime is Active
162 - Publish is a single atomic pointer swap
163 - After publish: old controller Draining (no new turns; finish in-flight)
164 - Drain timeout cancels remainder and records receipts
165 - Stale generation UI / provider stream / event output is silently dropped
166
167 ## Effect receipts
168
169 Irreversible and compensatable effects record owner, generation, component,
170 class, timestamps, receipt id, and compensation status. Provider requests
171 already submitted must not be reported as rolled back. File writes need prior
172 state or compensate. Sent messages get receipts and duplicate-send protection.
173 Cancellation stops later work only.
174
175 The ledger retains at most 32 generations and 256 receipts per generation.
176 Eviction marks recovery evidence incomplete, releases associated file priors,
177 and prevents a clean-rollback claim. The ledger is not persisted; process-crash
178 recovery is explicitly outside this design's scope.
179
180 ## Permissions
181
182 The dependency graph answers what capabilities a component may obtain. It does
183 not replace the sandbox: trusted host components, native sidecars (process
184 boundary), and untrusted plugins still face permission interceptors at call
185 time.
186
187 ## Error reasons (extension protocol)
188
189 In addition to the existing frozen table, v2 adds:
190
191 - `dependency_unsatisfied`
192 - `dependency_cycle`
193 - `schema_mismatch`
194 - `activation_failed`
195 - `stale_generation`
196 - `cleanup_failed`
197
198 (`unsupported_version` already exists.)
199
200 ## Migration
201
202 - `reasonix plugin migrate <name> --to-v2` rewrites only pre-extension native
203 manifests that omit `apiVersion`, backs up the original, and errors on
204 dependencies it cannot infer. It does not accept Manifest v1.
205 - `reasonix plugin doctor` reports dependency and protocol errors
206 - v1 / missing apiVersion native manifests fail install, doctor, and boot
207
208 ## Acceptance
209
210 1. Every v2 component resource has a clear owner and EffectScope
211 2. Activation failure never leaks new-generation resources
212 3. Missing dependencies become diagnostic Inactive, never panics
213 4. Dependency replacement reloads only the affected subgraph
214 5. Publish/drain order is testable
215 6. Irreversible work is never marked rollback-success
216 7. v1 native manifests are rejected without dual-read or migration
217 8. Prompt/tool cache stability guards pass
218 9. Doctor explains why a component is inactive
219 10. No external runtime or language dependency
220
221 ## Implementation status (repo)
222
223 | Area | Status |
224 | --- | --- |
225 | EffectScope / LiveScope / RuntimeSet | Done |
226 | extensioncontract + DependencyGraph + RuntimePlan | Done |
227 | Manifest `reasonix.io/plugin/v2` + reject v1 | Done |
228 | Extension Protocol major v2 + schema/SDK gen | Done |
229 | PublishGate + stale UI/provider chunk drop | Done |
230 | Sidecar incremental adopt (StartPackagesWithPlan) | Done |
231 | RebuildFrom + desktop/CLI lastBuildResult | Done |
232 | MCP Host ReplaceServerBackend stable proxy | Done |
233 | Provider liveClient re-resolve | Done |
234 | LifecycleRegistry + FormatRuntimeStatus | Done |
235 | Manifest provides ceiling on handshake | Done |
236 | Full subgraph-only BuildRuntime (skip tools/prompt) | Done: plan classify + ReuseAssembly rediscovery skip + CacheHash reuse |
237 | Controller admission on PublishGate | Done (`turnDroppedDraining`) |
238 | Lifecycle metrics + graph/plan benchmarks | Done (soft CI baselines) |
239 | EffectScope for every MCP/watcher/UI resource | Done (activator + session-resources fold + Track* helpers) |
240 | Receipt-driven recovery / checkpoint | Done (`DecideResume` + doctor/desktop + provider-submit receipts) |
241 | Drain timeout product path | Done (`ScheduleDrainWatch` / `SweepAndForceExpire`) |
242 | Doctor runtime + desktop RuntimeDoctor | Done |
243 | Integration matrix (rapid reload, provider classify, admission) | Done |
244
244 lines MARKDOWN