fix(core): finalize v2 session context epochs

This commit is contained in:
Kit Langton 2026-06-04 21:17:05 -04:00
commit cd812e2045
33 changed files with 1245 additions and 791 deletions

View file

@ -21,15 +21,15 @@ Watcher-backed caches are a later efficiency optimization for roots with proven
## Existing Pieces
| Existing piece | Responsibility |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `Watcher.locationLayer` | Publish advisory `file.watcher.updated` events for local filesystem changes. |
| `EventV2.subscribe(...)` | Expose advisory events as scoped Effect streams. |
| `State.create(...)` | Rebuild replayable plugin and config contribution state from scoped transforms. |
| `SynchronizedRef.modifyEffect(...)` | Serialize effectful state refresh and store the next value only after success. |
| `SystemContext` | Convert coherent source samples into one immutable baseline, chronological updates, unavailable state, and removal tombstones. |
| `SystemContextRegistry` | Assemble Location-scoped built-in, instruction, and plugin context producers in stable contribution-key order. |
| `LocationServiceMap` | Own and clean up Location-scoped services, watcher subscriptions, and observation caches together. |
| Existing piece | Responsibility |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `Watcher.locationLayer` | Publish advisory `file.watcher.updated` events for local filesystem changes. |
| `EventV2.subscribe(...)` | Expose advisory events as scoped Effect streams. |
| `State.create(...)` | Rebuild replayable plugin and config contribution state from scoped transforms. |
| `SynchronizedRef.modifyEffect(...)` | Serialize effectful state refresh and store the next value only after success. |
| `SystemContext` | Convert coherent source samples into one immutable baseline, chronological updates, unavailable state, and removal tombstones. |
| `SystemContextRegistry` | Assemble Location-scoped built-in, instruction, and plugin context producers in stable contribution-key order. |
| `LocationServiceMap` | Own and clean up Location-scoped services, watcher subscriptions, and observation caches together. |
The missing reusable piece is deliberately small: retain the last successful value, mark it stale, and serialize refresh attempts.
@ -153,10 +153,11 @@ embedded skill
Add a Location-scoped contributor to `SystemContextRegistry`:
```ts
yield* registry.contribute({
key: "core/instructions",
load: loadAmbientInstructions(),
})
yield *
registry.contribute({
key: SystemContext.Key.make("core/instructions"),
load: loadAmbientInstructions(),
})
```
`InstructionContext` owns instruction discovery, deterministic ordering, and source loading. `SystemContextRegistry` owns contributor composition and lifecycle. `SystemContext` remains unaware of files and URLs.
@ -213,15 +214,15 @@ temporary discovery or read failure
-> aggregate SystemContext.unavailable
```
| Observation | Source outcome |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Local scan succeeds and discovers readable file | Include its exact contents in the available aggregate source. |
| Local scan succeeds and a previously discovered file is absent | Remove it from the aggregate value; remove the aggregate source when no instructions remain. |
| Local scan or file read fails transiently | Preserve the admitted aggregate source as `SystemContext.unavailable`; never emit mass removals. |
| Empty local file | Include the empty exact content in the available aggregate source. |
| URL returns `2xx` body | Available source with exact contents. |
| URL times out or returns transient failure | `SystemContext.unavailable`. |
| URL returns `404` or `410` | Decide the explicit removal contract before URL implementation. |
| Observation | Source outcome |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Local scan succeeds and discovers readable file | Include its exact contents in the available aggregate source. |
| Local scan succeeds and a previously discovered file is absent | Remove it from the aggregate value; remove the aggregate source when no instructions remain. |
| Local scan or file read fails transiently | Preserve the admitted aggregate source as `SystemContext.unavailable`; never emit mass removals. |
| Empty local file | Include the empty exact content in the available aggregate source. |
| URL returns `2xx` body | Available source with exact contents. |
| URL times out or returns transient failure | `SystemContext.unavailable`. |
| URL returns `404` or `410` | Decide the explicit removal contract before URL implementation. |
Aggregate instruction removal text must be model-meaningful:
@ -309,6 +310,18 @@ sequenceDiagram
If coverage is not proven, bypass the cache and observe directly whenever the safe boundary naturally requests current state. This is safe-turn refresh, not a background polling loop.
When coverage is proven, cache each known candidate instruction path independently rather than invalidating one aggregate instruction cache:
```text
candidate instruction path
-> one Refreshable<File | Absent>
-> watcher event invalidates only the matching path
-> next safe provider boundary reloads only stale candidates
-> available candidates become ordered per-file Context Sources
```
Ambient candidates include the global `AGENTS.md` path and one `AGENTS.md` candidate in every applicable ancestor directory, including candidates that are currently absent so later additions are observable.
## URL Sources
URLs never share an observation cache with local discovery.
@ -372,31 +385,36 @@ Nested instructions discovered after successful read-tool activity remain a Sess
- Idle Sessions are not woken by local edits, URL timers, or plugin changes.
- Context Epoch admission remains serialized by the Session event transaction at the next naturally scheduled provider turn.
## Proposed Implementation Order
## Implementation Status And Follow-Up Order
Implemented in the direct-observation slice:
1. Add the Location-scoped `SystemContextRegistry` backed by stable-keyed scoped contributions.
2. Register built-in and ambient instruction producers with `SystemContextRegistry`.
3. Observe local instructions directly at each safe provider boundary.
4. Preserve admitted instructions after transient scan/read failure and block initial provider turns while context is unavailable.
5. Test ordering, edit, unlink, empty file, transient scan failure, discovered-then-missing races, durable restart behavior, and deterministic context admission.
Follow-up order:
1. Add and unit-test `Refreshable.make(load)` with `get` and `invalidate`.
2. Add model-meaningful instruction removal rendering support before unlink lands.
3. Add the Location-scoped `SystemContextRegistry` backed by stable-keyed scoped contributions.
4. Register built-in and ambient instruction producers with `SystemContextRegistry`.
5. Observe local instructions directly at each safe provider boundary.
6. Test add, edit, unlink, empty file, transient scan failure, transient read failure, restart, and deterministic ordering.
7. Add configured local exact paths and globs.
8. Add configured URL observations with explicit `404` and `410` semantics.
9. Add root-specific watcher registration and watcher-backed `Refreshable` invalidation where coverage is proven.
10. Migrate local `SkillV2` directory observations to per-source refreshables after skill failure semantics are corrected.
11. Add durable Session-scoped nested read discovery.
2. Add truthful root-specific watcher registration.
3. Move ambient instructions from one directly observed aggregate to one watcher-invalidated Refreshable and Context Source per candidate file.
4. Add configured local exact paths and globs.
5. Add configured URL observations with explicit `404` and `410` semantics.
6. Migrate local `SkillV2` directory observations to per-source refreshables after skill failure semantics are corrected.
7. Add durable Session-scoped nested read discovery.
## Open Questions
1. Should the first local scan failure preserve prior discovered sources as unavailable, or fail the current provider turn until a coherent rescan succeeds?
2. Should configured URL sources treat `404` and `410` as confirmed removals?
3. What root-specific watcher API cleanly models ignore policy and callback health?
4. Should own-process file mutations publish an advisory invalidation event synchronously after commit?
1. Should configured URL sources treat `404` and `410` as confirmed removals?
2. What root-specific watcher API cleanly models ignore policy and callback health?
3. Should own-process file mutations publish an advisory invalidation event synchronously after commit?
## Compression Line
```text
State remembers what should be loaded.
SystemContextRegistry remembers which context producers participate.
Refreshable remembers whether a successful observation needs loading again.
Context Epoch remembers what the model was told.
```

View file

@ -711,7 +711,7 @@ Compatibility:
Affected schema:
- Add synchronized `session.next.context.updated.1` Session events containing only exact combined model-visible text.
- Add synchronized `session.next.context.updated.1` Session events containing a durable System-message ID and only exact combined model-visible text.
- Add `session_context_epoch.revision` for transactional structured-snapshot advancement.
- Add the first-class `system` Session message projection for chronological context updates.
@ -727,7 +727,7 @@ Compatibility:
- The synchronized event log retains only text actually shown to the model, not internal structured snapshots.
- Existing experimental V2 Session databases remain disposable across incompatible pre-launch event-schema changes.
- Replacement epochs after compaction or model switches, project instructions, skills guidance, and plugin transforms remain follow-up slices.
- Replacement epochs after compaction or model switches, skills guidance, and plugin-defined context remain follow-up slices.
## 2026-06-04: Replace Session Context Epochs Lazily
@ -746,4 +746,22 @@ Compatibility:
- Baseline replacement is bounded operational state and does not add permanent synchronized events.
- Existing experimental V2 Session databases remain disposable across incompatible pre-launch event-schema changes.
- Compaction execution, project instructions, skills guidance, and plugin transforms remain follow-up slices.
- Compaction execution, skills guidance, and plugin-defined context remain follow-up slices.
## 2026-06-05: Register Ambient System Context Producers
Affected schema:
- No database schema changes.
Change:
- Replace the Session-specific context loader with a Location-scoped registry of stable-keyed scoped context producers.
- Register environment/date and ambient instruction producers independently, then evaluate producers concurrently in stable contribution-key order.
- Directly discover and read global plus upward project `AGENTS.md` files at each safe provider-turn boundary.
- Preserve admitted instructions across transient scan/read failures and block first-epoch initialization while any context source is unavailable.
- Retry Context Epoch preparation until stable after optimistic revision mismatches.
Compatibility:
- Watcher-backed per-file `Refreshable` instruction observations, configured sources, nested discovery, and plugin-defined context remain follow-up slices.