docs: publish reviewed CHAT-01 draft contracts and fixtures (#1507)

This commit is contained in:
2026-09-12 23:48:03 -05:00
parent 370823b354
commit 28d4e98ad8
4 changed files with 6433 additions and 0 deletions
+386
View File
@@ -0,0 +1,386 @@
# CHAT-01 draft conversation and control contracts
Issue #1507, authorized charter 26103. Darkwing authors; Filbert independently
reviews; Dewey reviews UI semantics; Rocko reviews architecture. Research baseline
is `370823b354400a73425b4180e07747d97ff87e7a`.
R3 draft, superseding rejected R1 request 26105 and R2 request 26117. R2
findings are Filbert 26119, Dewey 26120 and Rocko's adversarial re-review via
agent-send. All three accept the named companion gates below. Approval must name
all four current file hashes; prior reviews do not approve changed bytes.
These are proposed contracts and synthetic reference models. They implement no
endpoint, engine adapter, permission grant or live migration. Version 2 is a draft
revision, not a migration of an existing Mosaic record. Q21 publication does not
authorize deploying it. Only this directory's four files and scoped tracking are
in scope. Strict foundation isolation remains; no host-seat exception is approved.
## Record and trust boundary
The local-only draft 2020-12 schema has 24 closed record variants and 20 commands.
Every declared property is required. Null means absent or unknown, not permission.
IDs are opaque references, never filesystem paths, bearer credentials or proof.
UTC dates receive calendar validation. Integers fit JavaScript's safe range.
The example.invalid schema ID requires no network access.
Only `clientRequest` is untrusted client input. Its actor, authenticated channel,
grant, policy and supervisor facts come from authoritative server state, never
from a request header, posted record or tmux preamble. Other variants describe
server records or authorized projections. Shape validity authenticates nothing.
Never expose binding, grant, proof records or private blob storage wholesale.
Text, filenames, Markdown, arguments and labels remain untrusted render data.
| Records | Meaning |
|---|---|
| binding, grant, connection | Approved exact scope, source/config/engine pins, execution/cohort, current generation and fence; independent actor authority; authenticated connection incarnation |
| clientRequest, request, receipt | Expected target and command; immutable causal admission; monotonically revised actual or uncertain outcome |
| catalogueItem, clientView | Authorized catalogue plus safe current target, connection, view revision, observer/controller mode, supported operations/reasons and attachment limits |
| entry, event, cursor, page | Stable history/message/block fragments, ordered stream, actor-bound opaque cursor, exact snapshot and stream watermark |
| draft, upload, draftListing | Actor-private composer revision, private staged bytes and bounded private listing; no native delivery before Send |
| frozenPayload, queueItem | Immutable accepted text/upload revisions and digest; queue revision, admission lineage, original actor and dispatch disposition |
| nativeDecision, approval | Stable native decision identity and generation-specific UI projection with exact intent display, native choices and causal decider |
| confirmation | Short-lived single-use actor/connection-incarnation/target/operation/intent confirmation |
| stop, cohortProof, effectReport, turnProof | Recorded stop transitions, selected-cohort observations, explicit effect dispositions and native-turn reconciliation |
Capabilities in this proposal are names for future reviewed grants, not changes
to `roles/`. Missing grants, mappings, evidence, supported operations or required
recording capacity refuse effects. Do not turn a fail-closed refusal into a
fallback tmux paste, CLI launch or guessed source path.
## Safe bootstrap, history and rendering
An authorized catalogue response includes `currentView`. It supplies the target
needed for a first observe/takeover request without exposing the private binding.
View revisions increase on refresh. Transfer, revocation and reconciliation require
a fresh view. Availability intersects current grants, verified adapter support and
control/fence state; request-specific prerequisites still apply. Disabled controls
carry reasons. A stale view never authorizes a command. Recheck authoritative state
at admission and dispatch, including stricter verified byte limits.
All connections start as observers. Catalogue creation time, engine launch time
and last activity are separate nullable values. Relative Age uses last activity.
Earlier conversations and branches are read-only. Opening them never resumes,
forks or launches. Each conversation needs an approved project/workspace mapping;
OS readability, cwd and seat labels do not establish membership.
Cursors bind actor, purpose, conversation, branch, snapshot, source epoch and
expiry. Refuse unknown, foreign, expired or source-replaced cursors and retain the
old view with a reconciliation indicator. Never silently switch to a newer file.
History pages contain at most 100 parts and must also fit 8 MiB serialized UTF-8.
Each part has at most 64 blocks; strings cap at 262144 Unicode characters.
Split into continuation parts instead of clipping. Byte enforcement and renderer
safety need separate implementation tests; schema character limits are insufficient.
Render Markdown/code/tool details and permitted reasoning without active HTML,
unsafe URLs or terminal escape execution. Unavailable reasoning has empty text;
unavailable events have empty content. The schema rejects hidden payloads under
an unavailable label. This does not prove renderer safety or source redaction.
### History and stream identity
Every content event carries an explicit bounded role, including message-start
and final parts. Role comes from authorized native message metadata, never a
request ID or guessed text. Entry and stream roles must agree for the same stable
message; conflicting role or persisted-entry attribution requires reconciliation.
A final event is self-describing even when no history row preceded it. Empty-history
NEW user then assistant output, null causal request and reconnect overlap are tested.
`entry.message` and `event.message` are stable within the execution. A broker may
allocate a transient message key before native persistence, but must preserve it
when attaching a persisted entry ID. Unknown causal requests stay null. A content
event cannot omit its message key merely because request and entry are unknown.
Every block carries `block`, `fragment`, `lastFragment`. Block ordinals start at
zero per message; fragments start at zero per block and stay contiguous across
entry parts. Continuations retain type and tool-call identity. Do not render a
split tool invocation as several independent calls.
Final message events use `updateMode:replace`, exact entry/message, `part` and
`lastPart`. They replace only that addressed part, not the entire message. Text
and reasoning deltas append to the addressed block fragment. Tool updates replace
the addressed fragment. Once authoritative final parts arrive, replace provisional
content rather than appending it again. Missing parts/fragments are incomplete,
never silently dropped. Preserve branch and subagent separation.
A page's `streamEpoch` and `throughSequence` describe an atomic snapshot cut.
Subscribe/replay after that watermark. Deduplicate overlap; repeated event IDs
must have identical bytes. A final-part overlap conflicting with the snapshot,
sequence gap, unavailable replay or new epoch requires reconciliation. Do not
concatenate across a gap. The source adapter must produce this atomic cut or
advertise streaming as unavailable. R3 checks multipart finals, two blocks, null
request correlation, duplicate delivery, page overlap and reconnect gap/epoch
refusal. It does not prove a native stream producer or every delta subtype.
Keep loaded history, selection/caret/scroll and private drafts when paused or
offline. Show stale/paused state and reconcile on reconnect. Native acknowledgment
is not an assistant answer. Actual return-flow acceptance still requires sending
and receiving NEW assistant/tool text in the selected UI without manual refresh.
## Admission, dispatch and deduplication
Resolve authenticated connection and actor, current grant revision/expiry/scope,
approved mapping, verified command support, configuration/trust/policy and evidence
capacity. Browser Origin/Host/CSRF and private peer authentication remain required
runtime gates. Nothing in these files implements authentication.
Commands assert conversation/branch/execution/generation; those assertions never
choose a host process or storage path. Private drafts/uploads need their own
actor-scoped grants, not existing input control. Observe can read an authorized
history branch without acquiring control. Native work needs current control,
except independent takeover and recovery-control acquisition described below.
Commit intent, frozen bytes and queue transition before reporting admission.
The future broker must serialize dispatch, transfer, edits and cancellation.
Several file renames are not a proven multi-record transaction.
Canonical fixture hashing uses UTF-8 JSON, lexically sorted object keys, preserved
array order and no whitespace. Production decoding must reject duplicate keys,
unpaired surrogates, non-finite numbers and unsafe integers. No text normalization
or claim of full RFC 8785 conformance is made.
The client fingerprint covers target and validated command, excluding transport
connection and client request ID. The durable prompt/edit `operationDigest` also
covers the frozen payload digest, including text and complete upload revisions.
Dedup keys are authenticated actor, approved conversation and client request ID.
An exact reconnect retry returns the existing receipt, without requiring that the
new connection is controller and without redispatch. Conflicting content refuses.
Resolve referenced revisions against the original immutable admission on retry,
not the latest mutable draft. Delivery-unknown is never permission to replay.
The checker models a single receipt lookup and derives its current queue outcome
when available, including recovery after takeover. It does not implement a durable
multi-request index or monotonically persisted receipt revisions.
Dispatch resolves queueItem and its stored admission, then rechecks CURRENT
grant/policy/target/controller/fence and the frozen digest/revision. New client
commands require a connected authenticated channel. The scheduler instead uses
the authenticated durable admission and retained execution/controller claim;
ordinary browser disconnect alone does not pause already broker-acknowledged
queued work. Broker acknowledgment means durable admission, not native acceptance.
Native acknowledgment occurs later and never grants replay. Revocation, grant
expiry, generation change or fences still refuse dispatch.
A pre-dispatch refusal moves a still-queued item to `dispatch-refused`, stores
failureReason and emits queue-changed. It is not eligible for another scheduler
attempt. Existing recovered/terminal dispositions are retained. Reconciliation
returns that actual state, not a stale queued receipt. Frozen content remains
preserved; no automatic re-admission or native replay is inferred.
Missing or altered frozen content refuses. A later mutable source-draft revision
is orthogonal and does not invalidate an unchanged admitted snapshot. An edit
creates a new immutable admission and queue revision; original admission and
payload stay unchanged. Per-item cancel affects only that item. Bulk recovery
classifies all selected snapshots before committing dispositions. Valid content
becomes recovered drafts; missing/corrupt content becomes `recovery-failed` with
failureReason and queue-changed evidence. No text is invented from damaged bytes.
Control transfer and cancellation must still succeed. Stop records list successful
queueDrafts separately from queueFailures. These failed items preserve their
references for authorized repair/readback, never automatic resend.
## Commands and private composer lifecycle
All twenty commands are closed unions; no raw RPC/shell/session/provider/permission
command is accepted. Leading slash text, including leading whitespace, receives
`text-policy` refusal in the model. Complete slash/resource suppression and native
command interpretation still block CHAT-03 under B3.
| Commands | Required authority and transition |
|---|---|
| observe | observe plus authorized source/cursor; no launch/control |
| create-draft, update-draft, discard-draft, list-drafts | draft grant; actor/conversation/branch ownership; unsent state and revision CAS for mutations |
| begin-upload, append-upload, complete-upload, discard-upload | upload grant; private recipient scope, revision and committed offset CAS; verify staged bytes/digest before completion |
| issue-confirmation, answer-confirmation | confirm grant; issuance also needs the selected destructive-operation grant; actor, connection incarnation, target, expiry and pending-state checks |
| prompt | send/current controller; own exact unsent draft and valid uploads; freeze and queue, no blind interrupt |
| edit-queued, cancel-queued | send/current controller; owned queued item and exact revision; refuse dispatch races |
| takeover | independent take-control plus observe, active/open binding, expected generation; refuse self-takeover |
| acquire-recovery-control | independent recover-control plus observe, closed admission, absent/disconnected/revoked prior controller and exact confirmation |
| approval | approve/current controller, pending stable native decision, current projection/intent and enabled exact supported choice |
| interrupt | interrupt/current controller; fence, recover undispatched input, request native cancellation and reconcile |
| force-stop | force-stop/current controller and exact confirmation; allowed from active/stopping/uncertain even when work admission is closed |
| recover | recover/current controller, exact confirmation and stopped prior cohort/effects proof; eligibility only, no launch |
Draft creation/editing and upload staging emit no native input. Reload retrieves
only the authenticated actor's drafts. Private cursor/large-list support is held
by the companion below; the model refuses rather than silently truncating. Failed
upload or prompt admission must retain the original text and bytes. The fixture
uses synthetic in-memory bytes, not tested durable storage or disk-full recovery.
At most 10 attachments, 20 MiB/file, 100 MiB/message, narrowed by verified harness
limits. `limits.verified:false` disables attachment acceptance in a real consumer;
ceiling values alone are not evidence of native support. The miniature upload
model tests ownership, byte count, digest, offset, storage capacity and refusal
under unknown/stricter synthetic limits, not MIME sniffing or actual native limits. Chunks cap at 256 KiB. A filename is display
text only. Never extract/execute uploads or put private staging in an agent's
workspace before Send. No implicit TTL removes unsent drafts or uploads.
Uploads progress receiving -> staged, then transferring/delivered/sent where a
verified adapter requires that path. Transfer acknowledgment must bind recipient,
incarnation and bytes. A posted acknowledgment ID proves nothing. Frozen uploads
cannot be discarded out from under an admitted payload. Preview/download access
is NOT authorized by a privateBlobRef.
### Explicit companion dependencies, not hidden endpoints
R3 is not a complete deployable UI API. The following are named prerequisite
contracts, not authorized implementation tasks or additional queue starts:
* **CHAT-01C private readback/content companion** must define authenticated
attachment preview/download ranges, safe public upload projections, durable
queue/receipt/confirmation restoration, and actor-bound paginated private-state
snapshots/cursors. Until reviewed, these controls remain unavailable. A small
own-draft listing fixture does not claim complete browser/server restart recovery.
The UI must not invent a side API or enable durable Send acceptance before this
dependency is disposed.
* **CHAT-03I ingress/handoff companion** must resolve board Reply, agent-send/tmux,
native terminal/internal queued input and launcher/supervisor restart. R3's
disposition for unmediated input is `unmediated-ingress` refusal, not successful
message delivery. There is no deliver-message operation or authenticated peer
sender mapping here. Jason's ruling and a bounded reviewed charter are still
required before changing fleet communications. All-seat control acceptance is
blocked until this conflict is resolved. Existing live seats are untouched.
* **CHAT-03D native dialog companion** must map native confirm/select/input/editor
forms without pretending that every extension dialog is a tool permission.
They are explicitly unsupported in this draft. No generic yes/no substitution or hidden
text-entry endpoint is allowed.
These dependencies belong on subsequent CHAT task charters before implementation.
Their names do not authorize CHAT-02/03, other queue rows, live work or policy grants.
## Control loss, approvals and stopping
Takeover atomically increments generation, makes the old controller an observer,
recovers only undispatched work and reprojects pending decisions. It does not
interrupt active work. Recovered drafts retain original actor, attachments and
lineage. A different actor cannot read/adopt them by taking control. Same-actor
interfaces may explicitly resend after reconciliation; never resend automatically.
Disconnect does not release execution ownership, cancel work, allow a pending
decision or infer denial. Revocation closes affected ingress, clears the revoked
controller reference, increments generation and publishes control transfer.
Grant revocation/expiry must trigger the same ingress reconciliation in production;
the server fixture explicitly exercises connection revocation. Missing that event
producer is a runtime blocker, not permission to retain unsafe control.
Ordinary takeover is unavailable while fenced. Separately authorized recovery-control
acquisition can bind an observer when the previous controller is gone. It increments
generation without reopening admission, releasing the execution claim or launching.
After reconnection, destructive confirmation must be issued and answered again;
confirmation binds connection generation, target, operation and stop context and
is single-use. The digest includes the selected recovery stop or current predecessor
stop for force-stop. Changing that context requires a new confirmation.
Self-takeover is refused rather than becoming a queue-cancel shortcut.
An active/open controller revocation creates a revocation-mode fence record with
the revoked connection and no fabricated client request. A replacement controller
may acquire recovery-control, then request server-origin reconciliation. A trusted
turnProof must establish cleared native input, no revoked pending input, valid
current control and re-projected pending approvals or evidenced terminal decisions.
Use `turnState:input-reconciled` and `approvalDisposition:pending-reprojected` for
this path; the active turn need not be interrupted. The server records
control-reconciled and reopens admission without killing/restarting the engine.
Already fenced Interrupt/Force-stop paths retain their existing stop and proof
requirements. Grant revocation/expiry producers must use the equivalent protocol.
`nativeDecision` retains nativeRequest/toolCall/intent/policy identity across control
transfer. Each takeover creates a NEW approval projection ID with the same decision
reference and unchanged intent display. Old projections become superseded. Dedupe
native responses by stable native decision/request identity, not projection ID.
`resolvedBy` links to a causal request/actor even after takeover. Once a decision is
terminal, an old projection cannot resolve it again.
The approval includes bounded safe intent text before the tool transcript exists.
If the full authorized intent cannot be displayed, do not enable approval. Render
exact native labels. Permission-widening choices remain disabled with a reason.
Non-permission forms are explicitly blocked by CHAT-03D. A cancel choice is enabled
only with an independently verified adapter mapping proving non-allow semantics;
Pi select cancellation cannot inherit confirm cancellation's semantics. Claude's
permission callback has no invented cancel response.
A local deadline makes an unresolved decision uncertain until native evidence
establishes timeout/cancellation/resolution. Interrupt/Force stop likewise mark
pending approvals uncertain, not automatically denied or cancelled. Reconcile
native outcomes before reopening. Native denial remains authoritative. No tool
callback substitutes for strict tool isolation. A turnProof that settles an uncertain
decision must include decisionOutcomes with nativeEvidence. Native resolution with
no representable client choice uses `resolved-natively`, not an unexplained resolved
state. chosen may then be null, but native evidence may not. This does not invent a
human resolvedBy actor for a native timeout or decision.
Interrupt creates a stop record, closes dispatch and recovers queued drafts. It
may advance fenced -> cancelling -> uncertain. A trusted `turnProof` tied to that
stop/cohort must establish interrupted turn, cleared native queue with no remaining
IDs, native approval disposition and recorded effects. Then publish `reconciled`
with the stop reference, mark turn-interrupted and reopen only under current valid
control/trust/policy. Before this transition, prompt and ordinary takeover refuse.
If cancellation hangs, separately confirmed Force stop remains available.
For Pi, documented clear_queue precedes abort; returned text cannot reconstruct
attachments. Claude cancel_queued is capability-gated; SDK interrupt() alone is
insufficient. Neither adapter is proven by these fixtures.
Force stop creates a new stop record referencing its predecessor, fences input,
then records fenced -> cancelling/stopping -> uncertain/stopped. A successor marks
its unfinished predecessor superseded, a terminal presentation state. Immediate
trusted death/effect evidence may move fenced directly to stopped; intermediate
signal observations are not mandatory invented events. Only trusted
cohort/effects observations may promote stopped, and the binding cannot lead its
stop record. SIGTERM, EOF, abort acknowledgment or idle does not prove death.
`cohortProof` binds stop, authority, conversation/execution/cohort and membership
epoch; it includes complete membership, boot/pid/start identity, per-member death
time, observation time and verification digest. `effectReport` lists invocation
dispositions with evidence. Recovery derives no-live-members and reconciled effects
from these records, not world booleans. The fixture's trusted digest registry stands
in for an authenticated supervisor evidence store. Matching a self-posted hash is
NOT trust; a real producer/verifier and complete cohort containment remain B3/B4.
External effects can remain explicitly uncertain; killing never proves rollback.
At most one non-stopped binding may hold a conversation or approved native-session
identity. Active/reserved duplicates refuse `already-active`; uncertain/stopping
or unproven stopped replacements refuse `unsafe-replacement`. Recovery needs the
same conversation/branch/config pins, retained/reacquired exclusive claim, current
authority and confirmation. It only reports eligibility for separately authorized
Resume. Browser/server crash, lost acknowledgment or selecting history cannot start
another engine. The actual execution/writer-claim record is deferred to CHAT-02;
the single-binding fixture models its assumed retained identity, not a claim store.
No private-host command is admitted before Q22/CHAT-04R.
## Verification and retained gates
```sh
node --check docs/plans/chat-01/check.mjs
node docs/plans/chat-01/check.mjs
node docs/plans/chat-00/check.mjs
```
Node plus installed Python jsonschema 4.26.0, no install/network. Missing validator
or invalid schema fails closed. R3 currently tests 98 shapes, 322 required-field
omissions, 76 reference cases and seventeen named lifecycle sequences, plus per-item
recovery, immutable queue-edit, bootstrap and multipart-stream regressions. These
are finite synthetic examples, not comprehensive model checking or runtime proof.
Explicit model limits accepted for bounded review:
* Unmediated-ingress refusal is an asserted CHAT-03I disposition, not a modelled
ingress path. It is excluded from lifecycle counts. Refusal names are bounded
but unregistered draft IDs; no closed public reason-code registry is claimed.
* Control transfer rechecks grants/mapping/recording and verified operation support,
but intentionally does not require native preflight. It is bookkeeping only;
reprojecting an approval never sends a native response. Native actions still gate.
* Leading slash text is refused. Other prefixes such as `!` and `@`, and slash
commands on later lines, remain admitted unchecked by the miniature text model.
Their native interpretation is unverified and held under B3, not declared safe.
* publicView uses a separate illustrative predicate which can drift from admission,
including native preflight details. It is advisory, never authority. A production
view must derive availability from shared authoritative eligibility rules; these
fixtures do not prove complete UI refusal-reason coverage.
Admission fixtures assume authenticated channels and verified operation registries.
They do not produce those facts. Native byte limits, permissions, trusted signing,
durable transactions, full cursor/readback protocols, all delta/dialog forms,
process observation and actual UI behavior remain unverified. Never report a
passing synthetic fixture as security enforcement or all-seat acceptance.
B1/B2 retain exact Pi/Claude branch/image/protocol, trust/settings and tool-isolation
proof. B3/B4 retain every-ingress fencing, crash recovery, approved access mappings
and supervisor evidence. B5/Q22 retain piece-5/#1508/fleet ordering. B6 requires a
separately approved cutover/rollback envelope for each live target. #1507 remains
open for the all-seat demonstration and Jason's workday ruling.
+645
View File
@@ -0,0 +1,645 @@
// CHAT-01 R3: offline draft schemas and idealized lifecycle models only.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const here = path.dirname(fileURLToPath(import.meta.url));
const schema = JSON.parse(readFileSync(path.join(here, 'contracts.schema.json'), 'utf8'));
const f = JSON.parse(readFileSync(path.join(here, 'fixtures.json'), 'utf8'));
assert.equal(f.evidence, 'synthetic-draft-contract-only'); assert.equal(f.runtimeEnforcementProven, false);
function refs(v) { if (v && typeof v === 'object') { if ('$ref' in v) assert.ok(v.$ref.startsWith('#/$defs/')); Object.values(v).forEach(refs); } }
refs(schema);
const py = spawnSync('python3', ['-c', String.raw`
import json,sys
from importlib.metadata import version
from jsonschema import Draft202012Validator, FormatChecker
from datetime import datetime,timezone
s=json.load(open(sys.argv[1]));f=json.load(open(sys.argv[2]));Draft202012Validator.check_schema(s)
fc=FormatChecker()
@fc.checks('date-time',raises=(ValueError,TypeError))
def utc(v):
if not isinstance(v,str):return True
d=datetime.fromisoformat(v.replace('Z','+00:00'))
return v.endswith('Z') and d.utcoffset()==timezone.utc.utcoffset(d)
v=Draft202012Validator(s,format_checker=fc);bad=[];omissions=0
for c in f['shapeCases']:
if v.is_valid(c['value'])!=c['valid']:bad.append(c['id'])
for r in f['records']:
for key in s['$defs'][r['kind']]['required']:
x=dict(r);del x[key];omissions+=1
if v.is_valid(x):bad.append(r['kind']+' missing '+key)
print(json.dumps({'schemaCases':len(f['shapeCases']),'omissions':omissions,'validator':version('jsonschema'),'failures':bad}))
sys.exit(bool(bad))
`, path.join(here, 'contracts.schema.json'), path.join(here, 'fixtures.json')], { encoding: 'utf8', timeout: 30000, maxBuffer: 1024 * 1024 });
assert.equal(py.status, 0, py.stdout + py.stderr);
console.log('Schema', py.stdout.trim());
const clone = structuredClone;
const sort = v => Array.isArray(v) ? v.map(sort) : v && typeof v === 'object' ? Object.fromEntries(Object.keys(v).sort().map(k => [k, sort(v[k])])) : v;
const json = v => JSON.stringify(sort(v));
const hash = v => createHash('sha256').update(json(v)).digest('hex');
const equal = (a, b) => json(a) === json(b);
const without = (v, key) => Object.fromEntries(Object.entries(v).filter(([k]) => k !== key));
const bad = s => `refused:${s}`;
const record = (kind, fields) => ({ version: 2, kind, ...fields });
const targetOf = w => ({ conversation: w.binding.scope.conversation, branch: w.binding.branch, execution: w.binding.execution, controllerGeneration: w.binding.controllerGeneration });
const scopeMatch = (a, b) => a.conversation === b.conversation && a.branch === b.branch && a.execution === b.execution;
const operationHash = (r, payloadDigest) => hash({ target: r.target, command: r.command, ...(payloadDigest === undefined ? {} : { payloadDigest }) }); // Excludes transport; frozen digest included in durable admissions.
const own = (r, c, t) => r && r.actor === c.actor && r.conversation === t.conversation;
function patch(w, p) { const keys = p.path.split('.'); const last = keys.pop(); let n = w; for (const k of keys) n = n[k]; assert.ok(last in n, p.path); n[last] = clone(p.value); }
function emit(w, type, stop = null) {
const e = clone(f.records.find(r => r.kind === 'event'));
Object.assign(e, { id: `event-${w.events.length + 1}`, type, stop, target: targetOf(w), sequence: w.events.length + 1, request: null, entry: null, contentIndex: null, updateMode: 'none', content: [], message: null, role: null, part: null, lastPart: null, createdAt: w.now });
w.events.push(e);
}
function recoverQueue(w, selected = null) {
const ids = [], failed = [];
const pending = w.queue.filter(q => q.state === 'queued' && (!selected || q.id === selected));
// Classify the whole selection before committing dispositions. Corrupt bytes
// must not prevent control transfer or be reconstructed as invented drafts.
const usable = new Map(pending.map(q => {
const p = w.payloads.find(p => p.id === q.frozenPayload);
return [q.id, p && hash(without(p, 'digest')) === q.payloadDigest && p.digest === q.payloadDigest ? p : null];
}));
for (const q of pending) {
const payload = usable.get(q.id);
if (!payload) { q.state = 'recovery-failed'; q.failureReason = 'payload'; failed.push(q.id); emit(w, 'queue-changed'); continue; }
const draft = record('draft', { id: `recovered-${q.id}`, actor: q.actor, conversation: q.target.conversation, branch: q.target.branch, revision: 1, text: payload.text, attachments: payload.uploads.map(u => u.id), state: 'unsent', recoveredFrom: q.id, createdAt: w.now });
w.drafts.push(draft); q.state = 'recovered-as-draft'; q.recoveredDraft = draft.id; ids.push(draft.id);
}
return { drafts: ids, failed };
}
function reproject(w) {
for (const a of [...w.approvals]) {
const d = w.decisions.find(d => d.id === a.decision);
if (a.state !== 'pending' || d?.state !== 'pending') continue;
a.state = 'superseded';
w.approvals.push({ ...clone(a), id: `${a.id}-g${w.binding.controllerGeneration}`, target: targetOf(w), state: 'pending' });
}
}
function transfer(w, c, recovery) {
recoverQueue(w);
const old = w.connections.find(x => x.id === w.binding.controllerConnection);
if (old) old.mode = 'observer';
w.binding.controllerGeneration++; w.binding.controllerConnection = c.id; c.mode = 'controller';
reproject(w); emit(w, 'control-transferred', w.binding.stop);
// Recovery-control acquisition never opens work admission or launches.
return recovery ? 'recovery-control-acquired' : 'transferred';
}
function checkConfirmation(w, r, c, op) {
const x = w.confirmations.find(x => x.id === r.command.confirmation);
if (!x || x.state !== 'confirmed' || x.actor !== c.actor || x.connection !== c.id || x.connectionGeneration !== c.generation || x.operation !== op || !equal(x.target, r.target) || x.stop !== w.binding.stop || (op === 'recover' && x.stop !== r.command.stop) || x.intentDigest !== hash({ target: r.target, operation: op, stop: x.stop }) || Date.parse(x.expiresAt) <= Date.parse(w.now)) return false;
x.state = 'consumed'; return true;
}
function proof(w, id, kind, stopId) {
const p = w.proofs.find(p => p.id === id && p.kind === kind);
if (!p || !w.trustedAuthorities.includes(p.authority) || p.conversation !== w.binding.scope.conversation || p.execution !== w.binding.execution || p.cohortRef !== w.binding.cohortRef || p.stop !== stopId || Date.parse(p.observedAt) > Date.parse(w.now)) return null;
const digest = hash(without(p, 'verificationDigest'));
return digest === p.verificationDigest && w.trustedProofs[p.id] === digest ? p : null;
}
function effects(w, id, stopId) { const p = proof(w, id, 'effectReport', stopId); return p && p.invocations.every(i => ['completed', 'uncertain', 'not-started'].includes(i.disposition) && (i.disposition === 'not-started' || i.evidence)); }
function stopped(w, s) {
if (!s || s.state !== 'stopped' || w.binding.stop !== s.id || !scopeMatch(s.target, targetOf(w))) return false;
const p = proof(w, s.supervisorEvidence, 'cohortProof', s.id);
return p && p.membershipComplete && p.membershipEpoch === w.membershipEpoch &&
new Set(p.members.map(m => `${m.boot}:${m.pid}:${m.startTicks}`)).size === p.members.length &&
p.members.every(m => m.terminatedAt && Date.parse(m.terminatedAt) <= Date.parse(p.observedAt)) && effects(w, s.effectsEvidence, s.id);
}
function startStop(w, r, mode) {
w.binding.admission = 'closed';
const recovered = recoverQueue(w);
const predecessor = w.stops.find(s => s.id === w.binding.stop);
if (predecessor && !['stopped', 'turn-interrupted', 'superseded'].includes(predecessor.state)) predecessor.state = 'superseded';
const s = record('stop', { id: `stop-${w.stops.length + 1}`, request: r.id, target: clone(r.target), mode, state: 'fenced', queueDrafts: recovered.drafts, queueFailures: recovered.failed, revokedConnection: mode === 'revocation' ? r.connection : null, cohortRef: w.binding.cohortRef, supervisorEvidence: null, effectsEvidence: null, externalEffects: 'uncertain', createdAt: w.now, nativeQueue: 'pending', approvalDisposition: 'pending', turnEvidence: null, supersedes: w.binding.stop });
w.stops.push(s); w.binding.stop = s.id;
// A cancellation request is not proof that a native approval was cancelled.
if (mode !== 'revocation') {
for (const d of w.decisions) if (d.state === 'pending') d.state = 'uncertain';
for (const a of w.approvals) if (a.state === 'pending') a.state = 'uncertain';
}
if (mode === 'force-stop') w.binding.state = 'stopping';
if (mode !== 'revocation') emit(w, 'stopping', s.id);
return `${mode}-fenced`;
}
const caps = { observe: 'observe', prompt: 'send', takeover: 'take-control', 'edit-queued': 'send', 'cancel-queued': 'send', approval: 'approve', interrupt: 'interrupt', 'force-stop': 'force-stop', recover: 'recover', 'acquire-recovery-control': 'recover-control', 'list-drafts': 'draft', 'create-draft': 'draft', 'update-draft': 'draft', 'discard-draft': 'draft', 'begin-upload': 'upload', 'append-upload': 'upload', 'complete-upload': 'upload', 'discard-upload': 'upload', 'issue-confirmation': 'confirm', 'answer-confirmation': 'confirm' };
function uploadValid(w, u, c, t) { return own(u, c, t) && equal(u.recipientScope, w.binding.scope) && ['staged', 'delivered'].includes(u.state) && u.receivedBytes === u.size && (u.state !== 'delivered' || u.transferAck); }
function attachmentLimits(w, uploads) {
if (!uploads.length) return true;
const n = w.nativeLimits;
return n?.verified && uploads.length <= Math.min(10, n.attachments) && uploads.every(u => u.size <= Math.min(20 * 1024 * 1024, n.fileBytes)) && uploads.reduce((sum, u) => sum + u.size, 0) <= Math.min(100 * 1024 * 1024, n.messageBytes);
}
function freeze(w, d) {
const p = record('frozenPayload', { id: `payload-${w.payloads.length + 1}`, actor: d.actor, conversation: d.conversation, branch: d.branch, draft: d.id, draftRevision: d.revision, text: d.text, uploads: d.attachments.map(id => clone(w.uploads.find(u => u.id === id))), digest: '', createdAt: w.now });
p.digest = hash(without(p, 'digest')); w.payloads.push(p); return p;
}
function evaluate(w, r, phase = 'admission') {
const c = w.connections.find(c => c.id === r.connection), b = w.binding, t = r.target, cmd = r.command, op = cmd.operation;
if (!c || c.state === 'revoked' || (phase !== 'dispatch' && (c.state !== 'connected' || !w.authenticatedChannels.includes(c.authenticatedChannelRef)))) return bad('channel');
// Scheduler authority comes from durable admission and the retained claim;
// ordinary browser disconnect is not revocation or a new work fence.
if (c.transport === 'private-host') return bad('unsupported-capability'); // Q22/CHAT-04R held.
const g = w.grants.find(g => g.id === c.grant);
if (!g || g.actor !== c.actor || g.revision !== c.grantRevision || g.state !== 'active' || Date.parse(g.expiresAt) <= Date.parse(w.now)) return bad('grant');
if (!equal(g.scope, b.scope) || c.conversation !== b.scope.conversation) return bad('scope');
if (!w.approvedMappings.includes(b.sourceRootRef)) return bad('mapping');
if (!w.auditWritable) return bad('audit');
if (!g.capabilities.includes(caps[op])) return bad('capability');
if (!w.verifiedOperations.includes(op)) return bad('unsupported-capability');
if (t.conversation !== b.scope.conversation || t.execution !== b.execution) return bad('target');
if (op === 'observe') {
if (!w.viewBranches.includes(t.branch)) return bad('target');
if (cmd.cursor && (cmd.cursor !== w.cursor.id || w.cursor.actor !== c.actor || w.cursor.purpose !== 'history' || w.cursor.conversation !== t.conversation || !Number.isFinite(Date.parse(w.cursor.expiresAt)) || Date.parse(w.cursor.expiresAt) <= Date.parse(w.now) || w.cursor.branch !== t.branch || w.cursor.sourceEpoch !== w.sourceEpoch || w.cursor.snapshotDigest !== b.snapshotDigest)) return bad('cursor');
return 'observing';
}
if (t.branch !== b.branch) return bad('target');
if (phase === 'admission' && w.requestIndex && (w.requestIndex.id ?? 'client-request-1') === r.id) {
if (w.requestIndex.actor !== c.actor || w.requestIndex.digest !== operationHash(r)) return bad('conflicting-request');
const admitted = w.requests.find(a => a.clientRequest === r.id && a.actor === c.actor);
const queued = w.queue.find(q => q.request === admitted?.id);
return `existing:${queued?.state ?? w.requestIndex.state}`;
}
// Private draft/upload actions need visibility and their own grant, not control.
if (op === 'list-drafts') {
const drafts = w.drafts.filter(d => own(d, c, t) && d.branch === t.branch);
// Never silently truncate or invent an unreviewed private cursor protocol.
if (cmd.cursor || drafts.length > cmd.limit) return bad('readback-contract');
w.lastPrivateListing = clone(drafts); return 'drafts-listed';
}
if (op === 'create-draft') {
w.drafts.push(record('draft', { id: `draft-${w.drafts.length + 1}`, actor: c.actor, conversation: t.conversation, branch: t.branch, revision: 1, text: cmd.text, attachments: [], state: 'unsent', recoveredFrom: null, createdAt: w.now })); return 'draft-created';
}
if (['update-draft', 'discard-draft'].includes(op)) {
const d = w.drafts.find(d => d.id === cmd.draft);
if (!own(d, c, t) || d.branch !== t.branch || d.state !== 'unsent' || d.revision !== cmd.revision) return bad('draft');
if (op === 'discard-draft') { d.state = 'discarded'; d.revision++; return 'draft-discarded'; }
if (cmd.attachments.some(id => !uploadValid(w, w.uploads.find(u => u.id === id), c, t))) return bad('upload');
if (cmd.attachments.reduce((n, id) => n + w.uploads.find(u => u.id === id).size, 0) > 100 * 1024 * 1024) return bad('upload');
d.text = cmd.text; d.attachments = [...cmd.attachments]; d.revision++; return 'draft-updated';
}
if (op === 'begin-upload') {
if (w.privateStorageCapacity < cmd.size) return bad('capacity');
const serial = w.uploads.filter(u => u.id.startsWith('upload-new-')).length + 1;
const u = record('upload', { id: `upload-new-${serial}`, actor: c.actor, conversation: t.conversation, revision: 1, filename: cmd.filename, mimeType: cmd.mimeType, size: cmd.size, digest: cmd.digest, state: 'receiving', privateBlobRef: `blob-new-${serial}`, recipientScope: clone(b.scope), transferAck: null, createdAt: w.now, receivedBytes: 0 });
w.uploads.push(u); w.blobBytes[u.id] = ''; w.privateStorageCapacity -= u.size; return 'upload-receiving';
}
if (['append-upload', 'complete-upload', 'discard-upload'].includes(op)) {
const u = w.uploads.find(u => u.id === cmd.upload);
if (!own(u, c, t) || !equal(u.recipientScope, b.scope) || u.revision !== cmd.revision) return bad('upload');
if (op === 'discard-upload') {
if (w.payloads.some(p => p.uploads.some(x => x.id === u.id))) return bad('upload-in-use');
u.state = 'discarded'; return 'upload-discarded';
}
if (u.state !== 'receiving') return bad('upload');
if (typeof w.blobBytes[u.id] !== 'string') return bad('storage');
if (op === 'append-upload') {
const bytes = Buffer.from(cmd.dataBase64, 'base64');
if (bytes.toString('base64') !== cmd.dataBase64 || cmd.offset !== u.receivedBytes || bytes.length > 256 * 1024 || u.receivedBytes + bytes.length > u.size) return bad('upload');
const all = Buffer.concat([Buffer.from(w.blobBytes[u.id], 'base64'), bytes]);
w.blobBytes[u.id] = all.toString('base64'); u.receivedBytes = all.length; u.revision++; return 'upload-appended';
}
const bytes = Buffer.from(w.blobBytes[u.id], 'base64');
if (bytes.length !== u.size || createHash('sha256').update(bytes).digest('hex') !== u.digest) return bad('upload');
u.state = 'staged'; u.revision++; return 'upload-staged';
}
if (t.controllerGeneration !== b.controllerGeneration) return bad('generation');
if (op === 'issue-confirmation') {
const needed = caps[cmd.operationToConfirm];
if (!g.capabilities.includes(needed)) return bad('capability');
w.confirmations.push(record('confirmation', { id: `confirmation-${w.confirmations.length + 1}`, actor: c.actor, connection: c.id, connectionGeneration: c.generation, target: clone(t), operation: cmd.operationToConfirm, stop: b.stop, intentDigest: hash({ target: t, operation: cmd.operationToConfirm, stop: b.stop }), expiresAt: new Date(Date.parse(w.now) + 60000).toISOString(), state: 'pending' })); return 'confirmation-issued';
}
if (op === 'answer-confirmation') {
const x = w.confirmations.find(x => x.id === cmd.confirmation);
if (!x || x.state !== 'pending' || x.actor !== c.actor || x.connection !== c.id || x.connectionGeneration !== c.generation || !equal(x.target, t) || Date.parse(x.expiresAt) <= Date.parse(w.now)) return bad('confirmation');
x.state = cmd.answer === 'confirm' ? 'confirmed' : 'cancelled'; return `confirmation-${x.state}`;
}
if (op === 'takeover') {
if (c.id === b.controllerConnection) return bad('already-controller');
if (!g.capabilities.includes('observe')) return bad('capability');
if (b.state !== 'active' || b.admission !== 'open') return bad('fenced');
return transfer(w, c, false);
}
if (op === 'acquire-recovery-control') {
if (!g.capabilities.includes('observe')) return bad('capability');
if (b.admission !== 'closed') return bad('fenced');
const old = w.connections.find(x => x.id === b.controllerConnection);
if (old?.state === 'connected') return bad('controller-present');
if (!checkConfirmation(w, r, c, op)) return bad('confirmation');
return transfer(w, c, true);
}
if (c.id !== b.controllerConnection || c.mode !== 'controller') return bad('controller');
if (op === 'force-stop') {
if (!['active', 'stopping', 'uncertain'].includes(b.state)) return bad('fenced');
if (!checkConfirmation(w, r, c, op)) return bad('confirmation');
return startStop(w, r, op); // Safety escalation stays usable while work is fenced.
}
if (op === 'recover') {
const s = w.stops.find(s => s.id === cmd.stop);
if (b.state !== 'stopped' || b.admission !== 'closed' || !stopped(w, s)) return bad('stop-proof');
if (!checkConfirmation(w, r, c, op)) return bad('confirmation');
return 'recovery-eligible'; // No engine start and no admission reopening.
}
if (!w.trustVerified || !w.policyVerified) return bad('preflight');
if (b.state !== 'active' || b.admission !== 'open') return bad('fenced');
if (op === 'interrupt') return startStop(w, r, op);
if (op === 'approval') {
const a = w.approvals.find(a => a.id === cmd.approval), d = w.decisions.find(d => d.id === a?.decision);
if (d && ['resolved', 'resolved-natively', 'expired', 'cancelled'].includes(d.state)) return bad('decision-completed');
if (!a || !d || a.state !== 'pending' || d.state !== 'pending' || !equal(a.target, t) || !scopeMatch(d, t) || a.intentDigest !== cmd.intentDigest || d.intentDigest !== cmd.intentDigest || d.policyRevision !== b.policyRevision) return bad('approval');
if (a.dialogForm !== 'permission' || a.unsupportedReason || a.intentDisplay.visibility !== 'available' || !a.intentDisplay.complete) return bad('unsupported-capability');
if (a.deadline && Date.parse(a.deadline) <= Date.parse(w.now)) { a.state = 'uncertain'; d.state = 'uncertain'; return bad('approval-uncertain'); }
const choice = a.choices.find(x => x.id === cmd.choice);
if (!choice?.enabled || choice.effect === 'permission-change' || (choice.effect === 'cancel' && (choice.cancelSafety !== 'proven-non-allow' || !w.verifiedCancelMappings.includes(choice.mappingEvidence)))) return bad('choice');
d.state = a.state = 'resolved'; d.chosen = a.chosen = choice.id; d.resolvedBy = a.resolvedBy = r.id;
w.requests.push(record('request', { id: r.id, clientRequest: r.id, connection: c.id, actor: c.actor, grant: g.id, grantRevision: g.revision, target: clone(t), operationDigest: operationHash(r), command: clone(cmd), admittedAt: w.now, frozenPayload: null }));
w.outbox.push({ operation: 'native-approval', decision: d.id, nativeRequest: d.nativeRequest, request: r.id }); return 'approval-resolved';
}
if (phase === 'dispatch') {
const q = w.queue.find(q => q.request === w.requests.find(x => x.clientRequest === r.id)?.id), admitted = w.requests.find(x => x.id === q?.request);
if (!q || !admitted || q.state !== 'queued') return bad('queue-race');
if (q.actor !== c.actor || admitted.actor !== c.actor || admitted.grant !== g.id || admitted.grantRevision !== g.revision || !equal(q.target, t) || !equal(admitted.target, t) || q.policyRevision !== b.policyRevision || q.grantRevision !== g.revision) return bad('target');
const p = w.payloads.find(p => p.id === q.frozenPayload && p.id === admitted.frozenPayload);
if (!p || p.digest !== q.payloadDigest || hash(without(p, 'digest')) !== q.payloadDigest || p.actor !== c.actor || p.conversation !== t.conversation || p.branch !== t.branch) return bad('payload');
if (!attachmentLimits(w, p.uploads)) return bad('unsupported-capability');
if (admitted.operationDigest !== operationHash(admitted, p.digest) || !equal(admitted.command, cmd) || q.revision !== (cmd.operation === 'edit-queued' ? cmd.revision + 1 : 1)) return bad('payload');
if (p.text.trimStart().startsWith('/')) return bad('text-policy');
q.state = 'dispatched'; w.outbox.push({ operation: 'prompt', payload: p.id, actor: q.actor }); return 'dispatched';
}
if (['edit-queued', 'cancel-queued'].includes(op)) {
const q = w.queue.find(x => x.id === cmd.item);
if (!q || q.state !== 'queued' || q.revision !== cmd.revision || q.actor !== c.actor || !equal(q.target, t)) return bad('queue-race');
if (op === 'cancel-queued') { recoverQueue(w, q.id); return q.state; }
}
if (['prompt', 'edit-queued'].includes(op)) {
const d = w.drafts.find(x => x.id === cmd.draft);
if (!own(d, c, t) || d.branch !== t.branch || d.state !== 'unsent' || d.revision !== cmd.draftRevision) return bad('draft');
if (d.text.trimStart().startsWith('/')) return bad('text-policy');
const uploads = d.attachments.map(id => w.uploads.find(u => u.id === id));
if (uploads.some(u => !uploadValid(w, u, c, t)) || uploads.reduce((n, u) => n + u.size, 0) > 100 * 1024 * 1024) return bad('upload');
if (!attachmentLimits(w, uploads)) return bad('unsupported-capability');
const p = freeze(w, d); d.state = 'submitted';
const admitted = record('request', { id: `request-${w.requests.length + 1}`, clientRequest: r.id, connection: c.id, actor: c.actor, grant: g.id, grantRevision: g.revision, target: clone(t), operationDigest: operationHash(r, p.digest), command: clone(cmd), admittedAt: w.now, frozenPayload: p.id });
w.requests.push(admitted);
if (op === 'edit-queued') {
const q = w.queue.find(q => q.id === cmd.item);
q.request = admitted.id; q.draft = d.id; q.frozenPayload = p.id; q.payloadDigest = p.digest; q.revision++;
return 'queue-edited'; // Old admission and frozen bytes remain unchanged.
}
w.queue.push(record('queueItem', { id: `queue-${w.queue.length + 1}`, request: admitted.id, actor: c.actor, target: clone(t), revision: 1, draft: d.id, payloadDigest: p.digest, state: 'queued', nativeRequest: null, recoveredDraft: null, createdAt: w.now, frozenPayload: p.id, policyRevision: b.policyRevision, grantRevision: g.revision, failureReason: null }));
w.requestIndex = { id: r.id, actor: c.actor, digest: operationHash(r), state: 'queued' }; return 'queued';
}
return bad('operation');
}
function dispatch(w, queueId) {
const q = w.queue.find(q => q.id === queueId), a = w.requests.find(a => a.id === q?.request);
if (!a || q.state !== 'queued') return bad('queue-race');
// Scheduler resolves stored admission, never a new client/draft envelope.
const outcome = evaluate(w, { version: 2, kind: 'clientRequest', id: a.clientRequest, connection: a.connection, target: clone(a.target), command: clone(a.command) }, 'dispatch');
if (outcome.startsWith('refused:') && q.state === 'queued') {
q.state = 'dispatch-refused'; q.failureReason = outcome.slice('refused:'.length); emit(w, 'queue-changed');
}
return outcome;
}
// Server-origin transitions; no corresponding untrusted client command exists.
function server(w, op, fields = {}) {
if (op === 'revoke-connection') {
const c = w.connections.find(c => c.id === fields.connection); c.state = 'revoked'; c.mode = 'observer';
if (w.binding.controllerConnection === c.id) {
if (w.binding.state === 'active' && w.binding.admission === 'open') startStop(w, { id: null, connection: c.id, target: targetOf(w) }, 'revocation');
w.binding.controllerConnection = null; w.binding.controllerGeneration++; w.binding.admission = 'closed'; emit(w, 'control-transferred', w.binding.stop); }
return 'revoked';
}
if (op === 'reconcile-interrupt' || op === 'reconcile-revocation') {
const revocation = op === 'reconcile-revocation';
const s = w.stops.find(s => s.id === w.binding.stop), p = proof(w, fields.proof, 'turnProof', s?.id);
if (!s || s.mode !== (revocation ? 'revocation' : 'interrupt') || !p || p.turnState !== (revocation ? 'input-reconciled' : 'interrupted') || p.nativeQueue !== 'cleared' || p.nativePending.length || p.approvalDisposition === 'uncertain' || !effects(w, p.effectsEvidence, s.id)) return bad('reconciliation');
if (revocation && (p.approvalDisposition !== 'pending-reprojected' || !w.connections.some(c => c.id === s.revokedConnection && c.state === 'revoked') || w.decisions.some(d => d.state === 'pending' && !w.approvals.some(a => a.decision === d.id && a.state === 'pending' && equal(a.target, targetOf(w)))))) return bad('reconciliation');
const uncertain = w.decisions.filter(d => d.state === 'uncertain');
if (new Set(p.decisionOutcomes.map(x => x.decision)).size !== p.decisionOutcomes.length || uncertain.some(d => !p.decisionOutcomes.some(x => x.decision === d.id && x.nativeEvidence && ['cancelled', 'resolved-natively'].includes(x.state)))) return bad('reconciliation');
const c = w.connections.find(c => c.id === w.binding.controllerConnection), g = w.grants.find(g => g.id === c?.grant);
if (!c || c.state !== 'connected' || !g || g.state !== 'active' || g.actor !== c.actor || g.revision !== c.grantRevision || Date.parse(g.expiresAt) <= Date.parse(w.now) || !equal(g.scope, w.binding.scope) || !w.authenticatedChannels.includes(c.authenticatedChannelRef) || !w.approvedMappings.includes(w.binding.sourceRootRef) || !w.trustVerified || !w.policyVerified || !w.auditWritable) return bad('controller');
s.state = revocation ? 'control-reconciled' : 'turn-interrupted'; s.nativeQueue = 'cleared'; s.approvalDisposition = revocation ? 'pending' : p.approvalDisposition; s.turnEvidence = p.id; s.effectsEvidence = p.effectsEvidence;
for (const d of uncertain) {
const x = p.decisionOutcomes.find(x => x.decision === d.id);
if (!x) continue;
d.state = x.state; d.chosen = x.chosen; d.nativeEvidence = x.nativeEvidence;
for (const a of w.approvals.filter(a => a.decision === d.id && a.state === 'uncertain')) { a.state = x.state; a.chosen = x.chosen; }
}
w.binding.admission = 'open'; emit(w, 'reconciled', s.id); return 'reconciled';
}
if (op === 'advance-stop') {
const s = w.stops.find(s => s.id === w.binding.stop);
const next = { fenced: ['cancelling', 'stopping', 'uncertain'], cancelling: ['stopping', 'uncertain'], stopping: ['uncertain'], uncertain: [] };
if (!s || !next[s.state]?.includes(fields.state)) return bad('stop-transition');
s.state = fields.state; if (s.mode === 'force-stop') w.binding.state = fields.state === 'uncertain' ? 'uncertain' : 'stopping'; return fields.state;
}
if (op === 'confirm-stopped') {
const s = w.stops.find(s => s.id === w.binding.stop); if (!s || s.mode !== 'force-stop') return bad('stop-proof');
const trial = { ...s, state: 'stopped', supervisorEvidence: fields.proof, effectsEvidence: fields.effects };
if (!stopped(w, trial)) return bad('stop-proof');
Object.assign(s, trial); w.binding.state = 'stopped'; w.binding.admission = 'closed'; emit(w, 'stopped', s.id); return 'stopped';
}
return bad('server-operation');
}
function admitBinding(w, proposed) {
const conflicts = w.bindings.filter(b => b.scope.conversation === proposed.scope.conversation || (b.scope.host === proposed.scope.host && b.harness === proposed.harness && b.sourceRootRef === proposed.sourceRootRef && b.nativeSession === proposed.nativeSession));
if (conflicts.some(b => ['reserved', 'active'].includes(b.state))) return bad('already-active');
if (conflicts.some(b => b.state !== 'stopped')) return bad('unsafe-replacement');
if (conflicts.length && !stopped(w, w.stops.find(s => s.id === w.binding.stop))) return bad('unsafe-replacement');
return 'reservation-eligible'; // Never launches.
}
for (const c of f.stateCases) {
const w = clone(f.world); c.patches.forEach(p => patch(w, p));
assert.equal(c.phase === 'dispatch' ? dispatch(w, w.queue[0].id) : evaluate(w, c.request), c.expected, c.id);
if (c.phase === 'dispatch' && c.expected.startsWith('refused:')) assert.notEqual(w.queue[0].state, 'queued', c.id + ' disposition');
}
console.log(`Reference cases ${f.stateCases.length} pass`);
// Helpers below create only synthetic trusted-source observations for sequences.
let reqCounter = 10;
function command(w, op, connection = 'connection-1', fields = {}) { return { version: 2, kind: 'clientRequest', id: `seq-${++reqCounter}`, connection, target: targetOf(w), command: { ...clone(f.commands.find(c => c.operation === op)), ...fields } }; }
function confirmation(w, op, connection = 'connection-1') {
assert.equal(evaluate(w, command(w, 'issue-confirmation', connection, { operationToConfirm: op })), 'confirmation-issued');
const id = w.confirmations.at(-1).id;
assert.equal(evaluate(w, command(w, 'answer-confirmation', connection, { confirmation: id, answer: 'confirm' })), 'confirmation-confirmed'); return id;
}
function trustedProof(w, kind, stopId) {
const p = clone(f.records.find(r => r.kind === kind)); p.stop = stopId; p.id = `${kind}-${stopId}`;
if (kind === 'turnProof') p.effectsEvidence = `effectReport-${stopId}`;
p.verificationDigest = hash(without(p, 'verificationDigest')); w.proofs.push(p); w.trustedProofs[p.id] = p.verificationDigest; return p.id;
}
const results = [];
function sequence(name, fn) { fn(); results.push(name); console.log('PASS sequence', name); }
sequence('interrupt-reconcile-prompt', () => {
const w = clone(f.world); assert.equal(evaluate(w, command(w, 'interrupt')), 'interrupt-fenced');
assert.equal(evaluate(w, command(w, 'prompt')), bad('fenced'));
assert.equal(evaluate(w, command(w, 'takeover', 'connection-2')), bad('fenced'));
const s = w.stops.at(-1); trustedProof(w, 'effectReport', s.id); const p = trustedProof(w, 'turnProof', s.id);
assert.equal(server(w, 'reconcile-interrupt', { proof: p }), 'reconciled');
assert.equal(w.events.at(-1).stop, s.id); assert.equal(evaluate(w, command(w, 'prompt')), 'queued');
});
sequence('interrupt-confirmed-force-stop', () => {
const w = clone(f.world); assert.equal(evaluate(w, command(w, 'interrupt')), 'interrupt-fenced');
const id = confirmation(w, 'force-stop');
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), 'force-stop-fenced');
assert.equal(w.binding.admission, 'closed'); assert.equal(w.binding.state, 'stopping');
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), bad('confirmation'));
});
sequence('controller-revoked-recovery-control-recover', () => {
const w = clone(f.world); const id = confirmation(w, 'force-stop');
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), 'force-stop-fenced');
const s = w.stops.at(-1); const p = trustedProof(w, 'cohortProof', s.id), e = trustedProof(w, 'effectReport', s.id);
assert.equal(server(w, 'confirm-stopped', { proof: p, effects: e }), 'stopped');
server(w, 'revoke-connection', { connection: 'connection-1' });
assert.equal(w.binding.controllerConnection, null);
assert.equal(evaluate(w, command(w, 'recover', 'connection-2')), bad('controller'));
const recoveryControl = confirmation(w, 'acquire-recovery-control', 'connection-2');
assert.equal(evaluate(w, command(w, 'acquire-recovery-control', 'connection-2', { confirmation: recoveryControl })), 'recovery-control-acquired');
const resume = confirmation(w, 'recover', 'connection-2');
assert.equal(evaluate(w, command(w, 'recover', 'connection-2', { stop: s.id, confirmation: resume })), 'recovery-eligible');
assert.equal(w.outbox.length, 0); assert.equal(w.binding.admission, 'closed');
});
sequence('takeover-old-controller-new-approval', () => {
const w = clone(f.world); assert.equal(evaluate(w, command(w, 'takeover', 'connection-2')), 'transferred');
assert.equal(w.connections[0].mode, 'observer'); assert.equal(w.queue[0].actor, 'actor-1');
assert.equal(evaluate(w, command(w, 'prompt', 'connection-1')), bad('controller'));
const projection = w.approvals.at(-1); assert.notEqual(projection.id, 'approval-1');
assert.equal(evaluate(w, command(w, 'approval', 'connection-2', { approval: projection.id, choice: 'deny' })), 'approval-resolved');
assert.ok(w.decisions[0].resolvedBy); assert.equal(w.requests.at(-1).actor, 'actor-1');
assert.equal(evaluate(w, command(w, 'approval', 'connection-2', { approval: 'approval-1' })), bad('decision-completed'));
});
sequence('observer-draft-upload-send', () => {
const w = clone(f.world); const c = 'connection-2';
assert.equal(evaluate(w, command(w, 'create-draft', c)), 'draft-created'); const d = w.drafts.at(-1);
assert.equal(evaluate(w, command(w, 'begin-upload', c)), 'upload-receiving');
assert.equal(evaluate(w, command(w, 'append-upload', c)), 'upload-appended');
assert.equal(evaluate(w, command(w, 'complete-upload', c)), 'upload-staged');
assert.equal(evaluate(w, command(w, 'update-draft', c, { draft: d.id, revision: 1, text: 'hello file', attachments: ['upload-new-1'] })), 'draft-updated');
assert.equal(w.outbox.length, 0);
assert.equal(evaluate(w, command(w, 'list-drafts', c)), 'drafts-listed');
assert.ok(w.lastPrivateListing.some(x => x.id === d.id));
assert.equal(evaluate(w, command(w, 'takeover', c)), 'transferred');
const r = command(w, 'prompt', c, { draft: d.id, draftRevision: 2 });
assert.equal(evaluate(w, r), 'queued'); assert.equal(w.outbox.length, 0);
assert.equal(dispatch(w, w.queue.at(-1).id), 'dispatched'); assert.equal(w.outbox.length, 1);
});
sequence('duplicate-native-decision', () => {
const w = clone(f.world); assert.equal(evaluate(w, command(w, 'approval')), 'approval-resolved');
assert.equal(evaluate(w, command(w, 'approval')), bad('decision-completed')); assert.equal(w.outbox.length, 1);
});
sequence('second-launch-active-uncertain-stopping', () => {
const w = clone(f.world); assert.equal(admitBinding(w, w.binding), bad('already-active'));
for (const state of ['uncertain', 'stopping']) { w.bindings[0].state = state; assert.equal(admitBinding(w, w.binding), bad('unsafe-replacement')); }
});
{ // Asserted companion disposition only; NOT a model/lifecycle test.
// No invented delivery authority. Each remains a blocked companion-contract path.
const disposition = source => ['board-reply', 'tmux-paste', 'native-terminal', 'engine-queue', 'raw-rpc', 'launcher'].includes(source) ? bad('unmediated-ingress') : bad('unknown-ingress');
for (const source of ['board-reply', 'tmux-paste', 'native-terminal', 'engine-queue', 'raw-rpc', 'launcher']) assert.equal(disposition(source), bad('unmediated-ingress'));
}
sequence('stop-lifecycle-queue-draft-links', () => {
const w = clone(f.world); const id = confirmation(w, 'force-stop');
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), 'force-stop-fenced');
const s = w.stops.at(-1); assert.deepEqual(s.queueDrafts, [w.queue[0].recoveredDraft]); assert.equal(w.decisions[0].state, 'uncertain');
assert.equal(server(w, 'advance-stop', { state: 'cancelling' }), 'cancelling');
assert.equal(server(w, 'advance-stop', { state: 'stopping' }), 'stopping');
assert.equal(server(w, 'advance-stop', { state: 'uncertain' }), 'uncertain');
assert.equal(server(w, 'confirm-stopped', { proof: 'forged', effects: 'forged' }), bad('stop-proof'));
const p = trustedProof(w, 'cohortProof', s.id), e = trustedProof(w, 'effectReport', s.id);
assert.equal(server(w, 'confirm-stopped', { proof: p, effects: e }), 'stopped'); assert.equal(w.binding.state, 'stopped');
});
sequence('confirmation-reconnect-reconfirm', () => {
const w = clone(f.world); const id = confirmation(w, 'force-stop'); w.connections[0].generation++;
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), bad('confirmation'));
const newId = confirmation(w, 'force-stop'); assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: newId })), 'force-stop-fenced');
});
// Per-item cancellation must not cancel a neighbour. Bulk recovery must not
// partially mutate earlier items before discovering a corrupt later snapshot.
{
const w = clone(f.world);
w.queue.push({ ...clone(w.queue[0]), id: 'queue-neighbour' });
assert.equal(evaluate(w, command(w, 'cancel-queued')), 'recovered-as-draft');
assert.equal(w.queue[1].state, 'queued');
const broken = clone(f.world);
broken.queue.push({ ...clone(broken.queue[0]), id: 'queue-broken', frozenPayload: 'missing' });
const before = clone({ queue: broken.queue, drafts: broken.drafts });
const recovered = recoverQueue(broken);
assert.equal(recovered.drafts.length, 1); assert.deepEqual(recovered.failed, ['queue-broken']);
assert.equal(broken.queue[0].state, 'recovered-as-draft');
assert.equal(broken.queue[1].state, 'recovery-failed');
assert.equal(broken.drafts.length, before.drafts.length + 1);
}
{
const w = clone(f.world), original = clone(w.requests[0]);
assert.equal(evaluate(w, command(w, 'edit-queued')), 'queue-edited');
assert.deepEqual(w.requests[0], original);
assert.notEqual(w.queue[0].request, original.id);
assert.equal(dispatch(w, w.queue[0].id), 'dispatched');
}
// Public bootstrap never includes the private binding. Availability intersects
// authenticated grants, verified operations and current control/fence state.
function publicView(w, connection) {
const r = command(w, 'observe', connection, { cursor: null });
if (evaluate(w, r) !== 'observing') return null;
const c = w.connections.find(c => c.id === connection), g = w.grants.find(g => g.id === c.grant);
const v = clone(f.records.find(r => r.kind === 'clientView'));
v.connection = c.id; v.target = targetOf(w); v.controlMode = c.mode; v.revision = (w.viewRevision ?? 0) + 1; w.viewRevision = v.revision;
v.operations = f.commands.map(cmd => {
const op = cmd.operation;
let reason = !g.capabilities.includes(caps[op]) ? 'capability' : !w.verifiedOperations.includes(op) ? 'unsupported-capability' : null;
if (!reason && ['prompt', 'edit-queued', 'cancel-queued', 'approval', 'interrupt', 'force-stop', 'recover'].includes(op) && c.id !== w.binding.controllerConnection) reason = 'controller';
if (!reason && ['prompt', 'edit-queued', 'cancel-queued', 'approval', 'interrupt', 'takeover'].includes(op) && w.binding.admission !== 'open') reason = 'fenced';
if (!reason && op === 'takeover' && c.id === w.binding.controllerConnection) reason = 'already-controller';
if (!reason && op === 'force-stop' && !['active', 'stopping', 'uncertain'].includes(w.binding.state)) reason = 'fenced';
if (!reason && op === 'recover' && w.binding.state !== 'stopped') reason = 'stop-proof';
if (!reason && op === 'acquire-recovery-control' && (w.binding.admission !== 'closed' || w.connections.some(c => c.id === w.binding.controllerConnection && c.state === 'connected'))) reason = 'controller-present';
return { operation: op, enabled: !reason, reason };
});
// Synthetic trusted limits, not a native capability evidence producer.
v.limits = clone(w.nativeLimits);
return v;
}
{
const w = clone(f.world), before = publicView(w, 'connection-2');
assert.ok(before.operations.find(o => o.operation === 'takeover').enabled);
assert.equal(before.operations.find(o => o.operation === 'prompt').reason, 'controller');
assert.equal('cohortRef' in before, false); assert.equal('sourceRootRef' in before, false);
assert.equal(evaluate(w, command(w, 'takeover', 'connection-2')), 'transferred');
const after = publicView(w, 'connection-2');
assert.equal(after.target.controllerGeneration, before.target.controllerGeneration + 1);
assert.ok(after.revision > before.revision);
w.verifiedOperations = ['observe'];
assert.equal(publicView(w, 'connection-2').operations.find(o => o.operation === 'prompt').reason, 'unsupported-capability');
}
function consumeFinalParts(page, events) {
assert.equal(page.hasMore, page.nextCursor !== null);
const parts = new Map(page.entries.map(e => [`${e.message}:${e.part}`, clone(e)]));
const metadata = new Map(page.entries.map(e => [e.message, { role: e.role, entry: e.id }]));
const seen = new Map(); let sequence = page.throughSequence;
for (const e of events) {
assert.equal(e.streamEpoch, page.streamEpoch, 'reconcile epoch');
assert.equal(e.target.conversation, page.conversation); assert.equal(e.target.branch, page.branch);
const previous = seen.get(e.id);
if (previous) { assert.deepEqual(e, previous, 'conflicting event'); continue; }
seen.set(e.id, clone(e));
const key = `${e.message}:${e.part}`;
assert.ok(['user', 'assistant', 'tool', 'compaction', 'notice'].includes(e.role), 'missing role');
const identity = { role: e.role, entry: e.entry };
if (metadata.has(e.message)) assert.deepEqual(identity, metadata.get(e.message), 'conflicting message attribution');
else metadata.set(e.message, identity);
if (e.sequence <= page.throughSequence) {
if (e.type === 'message-end' && parts.has(key)) assert.deepEqual(parts.get(key).content, e.content, 'snapshot overlap conflict');
continue;
}
assert.equal(e.sequence, sequence + 1, 'reconcile gap'); sequence = e.sequence;
assert.equal(e.type, 'message-end'); assert.equal(e.updateMode, 'replace');
assert.ok(e.entry && e.message && Number.isInteger(e.part));
parts.set(key, { id: e.entry, message: e.message, role: e.role, part: e.part, lastPart: e.lastPart, content: clone(e.content) });
}
const messages = new Map();
for (const [message, identity] of metadata) {
const ordered = [...parts.values()].filter(p => p.message === message).sort((a, b) => a.part - b.part);
const blocks = new Map();
ordered.forEach((p, i) => {
assert.equal(p.part, i, 'missing final part');
assert.equal(p.id, ordered[0].id); assert.equal(p.message, ordered[0].message);
assert.equal(p.lastPart, i === ordered.length - 1, 'incomplete message');
for (const block of p.content) {
const fragments = blocks.get(block.block) ?? [];
assert.equal(block.fragment, fragments.length, 'missing block fragment');
if (fragments.length) { assert.equal(fragments.at(-1).lastFragment, false); assert.equal(block.type, fragments[0].type); assert.equal(block.call, fragments[0].call); }
fragments.push(block); blocks.set(block.block, fragments);
}
});
for (const fragments of blocks.values()) assert.equal(fragments.at(-1).lastFragment, true);
messages.set(message, { ...identity, blocks });
}
return messages;
}
{
const [page, a, b] = f.streamRecords;
const blocks = consumeFinalParts(page, [a, b, b]).get(a.message).blocks;
assert.equal(blocks.get(0).map(b => b.text).join(''), 'hello world');
assert.equal(blocks.get(1)[0].call, 'call-1');
assert.throws(() => consumeFinalParts(page, [{ ...b, sequence: 12 }]), /reconcile gap/);
assert.throws(() => consumeFinalParts(page, [{ ...b, streamEpoch: 'restarted' }]), /reconcile epoch/);
assert.throws(() => consumeFinalParts(page, [b, { ...b, content: [] }]), /conflicting event/);
assert.throws(() => consumeFinalParts(page, [{ ...a, content: [] }, b]), /snapshot overlap conflict/);
}
function reseal(w, id) {
const p = w.proofs.find(p => p.id === id); p.verificationDigest = hash(without(p, 'verificationDigest')); w.trustedProofs[id] = p.verificationDigest;
}
sequence('corrupt-payload-control-and-cancel', () => {
for (const recovery of [false, true]) {
const w = clone(f.world); w.payloads = [];
if (recovery) server(w, 'revoke-connection', { connection: 'connection-1' });
const op = recovery ? 'acquire-recovery-control' : 'takeover';
const fields = recovery ? { confirmation: confirmation(w, op, 'connection-2') } : {};
assert.equal(evaluate(w, command(w, op, 'connection-2', fields)), recovery ? 'recovery-control-acquired' : 'transferred');
assert.equal(w.queue[0].state, 'recovery-failed'); assert.equal(w.queue[0].recoveredDraft, null);
if (recovery) assert.deepEqual(w.stops.at(-1).queueFailures, [w.queue[0].id]);
}
const w = clone(f.world); w.payloads = [];
assert.equal(evaluate(w, command(w, 'cancel-queued')), 'recovery-failed');
});
sequence('revocation-nondestructive-reconciliation', () => {
const w = clone(f.world); server(w, 'revoke-connection', { connection: 'connection-1' });
const s = w.stops.at(-1); assert.equal(s.mode, 'revocation');
const id = confirmation(w, 'acquire-recovery-control', 'connection-2');
assert.equal(evaluate(w, command(w, 'acquire-recovery-control', 'connection-2', { confirmation: id })), 'recovery-control-acquired');
assert.equal(evaluate(w, command(w, 'prompt', 'connection-2')), bad('fenced'));
trustedProof(w, 'effectReport', s.id); const proofId = trustedProof(w, 'turnProof', s.id), p = w.proofs.find(p => p.id === proofId);
Object.assign(p, { turnState: 'input-reconciled', approvalDisposition: 'pending-reprojected', decisionOutcomes: [], nativePending: ['old-input'] }); reseal(w, proofId);
assert.equal(server(w, 'reconcile-revocation', { proof: proofId }), bad('reconciliation'));
p.nativePending = []; reseal(w, proofId);
assert.equal(server(w, 'reconcile-revocation', { proof: proofId }), 'reconciled');
assert.equal(w.binding.state, 'active'); assert.equal(w.binding.admission, 'open');
assert.equal(w.decisions[0].state, 'pending'); assert.equal(w.outbox.length, 0);
assert.equal(evaluate(w, command(w, 'prompt', 'connection-2')), 'queued');
});
sequence('admitted-disconnect-dispatch-receipt', () => {
const w = clone(f.world), r = command(w, 'prompt');
assert.equal(evaluate(w, r), 'queued'); const q = w.queue.at(-1);
w.connections[0].state = 'disconnected'; w.authenticatedChannels = ['channel-2'];
assert.equal(evaluate(w, command(w, 'prompt')), bad('channel'));
assert.equal(dispatch(w, q.id), 'dispatched'); assert.equal(w.outbox.length, 1);
assert.equal(dispatch(w, q.id), bad('queue-race')); assert.equal(w.outbox.length, 1);
assert.equal(evaluate(w, { ...r, connection: 'connection-2' }), 'existing:dispatched');
});
sequence('retry-after-transfer-current-disposition', () => {
const w = clone(f.world), r = command(w, 'prompt'); assert.equal(evaluate(w, r), 'queued');
assert.equal(evaluate(w, command(w, 'takeover', 'connection-2')), 'transferred');
assert.equal(evaluate(w, { ...r, connection: 'connection-2' }), 'existing:recovered-as-draft');
});
sequence('native-resolution-requires-evidence', () => {
const w = clone(f.world); evaluate(w, command(w, 'interrupt')); const s = w.stops.at(-1);
trustedProof(w, 'effectReport', s.id); const id = trustedProof(w, 'turnProof', s.id), p = w.proofs.find(p => p.id === id);
p.approvalDisposition = 'resolved'; p.decisionOutcomes = []; reseal(w, id);
assert.equal(server(w, 'reconcile-interrupt', { proof: id }), bad('reconciliation'));
p.decisionOutcomes = [{ decision: 'decision-1', state: 'resolved-natively', chosen: null, nativeEvidence: 'native-resolution-1' }]; reseal(w, id);
assert.equal(server(w, 'reconcile-interrupt', { proof: id }), 'reconciled');
assert.equal(w.decisions[0].state, 'resolved-natively'); assert.equal(w.decisions[0].nativeEvidence, 'native-resolution-1');
});
sequence('confirmation-predecessor-and-supersession', () => {
const w = clone(f.world), old = confirmation(w, 'force-stop');
evaluate(w, command(w, 'interrupt')); const predecessor = w.stops.at(-1);
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: old })), bad('confirmation'));
const id = confirmation(w, 'force-stop'); assert.equal(w.confirmations.at(-1).stop, predecessor.id);
assert.equal(evaluate(w, command(w, 'force-stop', 'connection-1', { confirmation: id })), 'force-stop-fenced');
assert.equal(predecessor.state, 'superseded');
});
sequence('different-actor-draft-privacy', () => {
const w = clone(f.world), g = { ...clone(w.grants[0]), id: 'grant-2', revision: 'grant-rev-2', actor: 'actor-2' };
w.grants.push(g); Object.assign(w.connections[1], { actor: g.actor, grant: g.id, grantRevision: g.revision });
assert.equal(evaluate(w, command(w, 'takeover', 'connection-2')), 'transferred');
assert.equal(evaluate(w, command(w, 'list-drafts', 'connection-2')), 'drafts-listed'); assert.deepEqual(w.lastPrivateListing, []);
assert.equal(evaluate(w, command(w, 'prompt', 'connection-2', { draft: w.queue[0].recoveredDraft, draftRevision: 1 })), bad('draft'));
});
sequence('empty-history-new-user-assistant-roles', () => {
const [page, user, assistant, overlapPage] = f.newMessageRecords;
const rows = consumeFinalParts(page, [user, assistant, assistant]);
assert.deepEqual([...rows.values()].map(r => r.role), ['user', 'assistant']);
assert.deepEqual(consumeFinalParts(overlapPage, [user, assistant]), rows);
assert.throws(() => consumeFinalParts(page, [user, assistant, { ...assistant, id: 'conflict-role', sequence: 3, role: 'user' }]), /conflicting message attribution/);
});
console.log('Additional regressions PASS: queue recovery, immutable queue edit, safe bootstrap and multipart snapshot/event reconstruction');
assert.deepEqual(results, f.sequences);
console.log(`CHAT-01 R3 PASS: ${f.shapeCases.length} shape cases, ${f.stateCases.length} reference cases, ${results.length} lifecycle sequences. All synthetic; runtime/authentication/protocol/cohort evidence producers NOT VERIFIED.`);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff