refactor(compact): align compact/summary and CompactionResult on shadowed* naming
This commit is contained in:
@@ -11,7 +11,7 @@ Compaction extends [`SessionEventMap`](session.md) with three event types via de
|
|||||||
| Event | Payload | Role |
|
| Event | Payload | Role |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `compact/start` | `{ turn }` | acquires the log-recorded lock |
|
| `compact/start` | `{ turn }` | acquires the log-recorded lock |
|
||||||
| `compact/summary` | `{ summary, compactedRange, compactedEventSeqs, tokenCount }` | provenance: the summary blocks, the shadowed seq range, and the estimated token count |
|
| `compact/summary` | `{ summary, shadowedRange, shadowedSeqs, shadowedTokenCount }` | provenance: the summary blocks, the shadowed seq range, and the estimated token count |
|
||||||
| `compact/end` | `{ turn, error? }` | releases the lock (`error` set when summarization threw) |
|
| `compact/end` | `{ turn, error? }` | releases the lock (`error` set when summarization threw) |
|
||||||
|
|
||||||
The lock brackets the **whole** operation: `compact/start` is appended first, then summarization, the `compact/summary` provenance record, and the `user/message` replacement all land, and only then `compact/end`. Releasing the lock last turns a crash mid-operation into a detectable orphaned lock (a `compact/start` with no matching `compact/end`) rather than a `compact/end` that falsely claims compaction finished.
|
The lock brackets the **whole** operation: `compact/start` is appended first, then summarization, the `compact/summary` provenance record, and the `user/message` replacement all land, and only then `compact/end`. Releasing the lock last turns a crash mid-operation into a detectable orphaned lock (a `compact/start` with no matching `compact/end`) rather than a `compact/end` that falsely claims compaction finished.
|
||||||
@@ -37,7 +37,7 @@ interface CompactionResult {
|
|||||||
/** The seq numbers of all shadowed surface nodes. */
|
/** The seq numbers of all shadowed surface nodes. */
|
||||||
shadowedSeqs: number[]
|
shadowedSeqs: number[]
|
||||||
/** Estimated token count of the shadowed content. */
|
/** Estimated token count of the shadowed content. */
|
||||||
compactedTokenCount: number
|
shadowedTokenCount: number
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Per the [capability-seams RFC](../../implemented/architecture/2026-06-13-capabil
|
|||||||
|
|
||||||
### The contract depends on `dsh-session` and `dsh-llm` — a deliberate deviation
|
### The contract depends on `dsh-session` and `dsh-llm` — a deliberate deviation
|
||||||
|
|
||||||
The capability-seams RFC states the interface package "depends only on cordis" (true of `dsh-bash`, whose vocabulary is self-contained). Compaction **cannot** honor that: its verbs are defined *over* a `Session` (`compactRegion(session, start, end)`) and its output *is* the content vocabulary (`CompactionResult.summary: ContentBlock[]`). There is no way to express the contract without naming `Session`/`SessionEvent` (from `dsh-session`) and `ContentBlock` (from `dsh-llm`).
|
The capability-seams RFC states the interface package "depends only on cordis" (true of `dsh-bash`, whose vocabulary is self-contained). Compaction **cannot** honor that: its verbs are defined *over* a `Session` (`compactRegion(session, start, end)`) and its output *is* the content vocabulary (`CompactionResult.summary: ContentBlock[]`). There is no way to express the contract without naming `Session`/`SessionEvent` (from `dsh-session`) and `ContentBlock` (from `dsh-llm`).
|
||||||
|
|
||||||
This is not a coupling smell — it is the contract's domain. The "only cordis" guidance was always shorthand for "the interface depends only on what the contract genuinely names, and never on an implementation." `dsh-session` and `dsh-llm` are themselves interface/vocabulary packages, not implementations; `dsh-compact` still imports no backend. The seam's real invariant — *consumers and implementations evolve independently behind an abstract service* — holds intact. We record the deviation here so a future reader doesn't mistake it for an accident or "fix" it by smuggling `Session` behind an opaque handle.
|
This is not a coupling smell — it is the contract's domain. The "only cordis" guidance was always shorthand for "the interface depends only on what the contract genuinely names, and never on an implementation." `dsh-session` and `dsh-llm` are themselves interface/vocabulary packages, not implementations; `dsh-compact` still imports no backend. The seam's real invariant — *consumers and implementations evolve independently behind an abstract service* — holds intact. We record the deviation here so a future reader doesn't mistake it for an accident or "fix" it by smuggling `Session` behind an opaque handle.
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ The `compact/*` events extend `SessionEventMap` (merge-extensible) via declarati
|
|||||||
| Event | Payload | On surface? |
|
| Event | Payload | On surface? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `compact/start` | `{ turn }` | no (log-only) |
|
| `compact/start` | `{ turn }` | no (log-only) |
|
||||||
| `compact/summary` | `{ summary, compactedRange, compactedEventSeqs, tokenCount }` | no (log-only) |
|
| `compact/summary` | `{ summary, shadowedRange, shadowedSeqs, shadowedTokenCount }` | no (log-only) |
|
||||||
| `compact/end` | `{ turn, error? }` | no (log-only) |
|
| `compact/end` | `{ turn, error? }` | no (log-only) |
|
||||||
|
|
||||||
## Implementing a backend
|
## Implementing a backend
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ declare module '@deepseek-ai/dsh-session' {
|
|||||||
*/
|
*/
|
||||||
'compact/summary': {
|
'compact/summary': {
|
||||||
summary: ContentBlock[]
|
summary: ContentBlock[]
|
||||||
compactedRange: { startSeq: number; endSeq: number }
|
shadowedRange: { start: number; end: number }
|
||||||
compactedEventSeqs: number[]
|
shadowedSeqs: number[]
|
||||||
tokenCount: number
|
shadowedTokenCount: number
|
||||||
}
|
}
|
||||||
/** Marks the end of a compaction — log-only, releases the lock. `error` set if summarization failed. */
|
/** Marks the end of a compaction — log-only, releases the lock. `error` set if summarization failed. */
|
||||||
'compact/end': { turn: number; error?: string }
|
'compact/end': { turn: number; error?: string }
|
||||||
@@ -53,5 +53,5 @@ export interface CompactionResult {
|
|||||||
/** The seq numbers of all shadowed surface nodes. */
|
/** The seq numbers of all shadowed surface nodes. */
|
||||||
shadowedSeqs: number[]
|
shadowedSeqs: number[]
|
||||||
/** Estimated token count of the shadowed content. */
|
/** Estimated token count of the shadowed content. */
|
||||||
compactedTokenCount: number
|
shadowedTokenCount: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ class StubCompactService extends CompactService {
|
|||||||
const startEvent = session.append('compact/start', { turn: 0 })
|
const startEvent = session.append('compact/start', { turn: 0 })
|
||||||
const summaryEvent = session.append('compact/summary', {
|
const summaryEvent = session.append('compact/summary', {
|
||||||
summary: [{ type: 'text', text: 'stub' }],
|
summary: [{ type: 'text', text: 'stub' }],
|
||||||
compactedRange: { startSeq: start, endSeq: end },
|
shadowedRange: { start, end },
|
||||||
compactedEventSeqs: [],
|
shadowedSeqs: [],
|
||||||
tokenCount: 0,
|
shadowedTokenCount: 0,
|
||||||
})
|
})
|
||||||
const endEvent = session.append('compact/end', { turn: 0 })
|
const endEvent = session.append('compact/end', { turn: 0 })
|
||||||
return {
|
return {
|
||||||
@@ -48,7 +48,7 @@ class StubCompactService extends CompactService {
|
|||||||
summary: [{ type: 'text', text: 'stub' }],
|
summary: [{ type: 'text', text: 'stub' }],
|
||||||
shadowedRange: { start, end },
|
shadowedRange: { start, end },
|
||||||
shadowedSeqs: [],
|
shadowedSeqs: [],
|
||||||
compactedTokenCount: 0,
|
shadowedTokenCount: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user