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-ingressrefusal, 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
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.