RFC 0015: normalized provider→channel stream grammar - #16
RFC 0015: normalized provider→channel stream grammar#16Marvinthebored wants to merge 11 commits into
Conversation
Emit always, archive always, gate only at presentation. Digests the companion spec (openclaw-provider-stream-spec) and references the stacked reference-impl PRs. Extends the agent event I/O contract (#92216) with four [AMENDS BASE] points. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex review: needs real behavior proof before merge. Reviewed August 11, 2026, 9:36 PM ET / August 12, 2026, 01:36 UTC. ClawSweeper reviewWhat this changesThis PR adds RFC 0015 plus two in-tree Markdown contracts defining normalized provider events, session archival, and presentation-gated channel output. Merge readinessKeep open: the RFC and its sidecars are not on current main, and accepting its archive-reader rule is a security and product decision rather than routine documentation cleanup. Priority: P2 Review scores
Verification
How this fits togetherProvider adapters turn vendor-specific streaming responses into common events; those events are retained in session history and then filtered by each channel’s presentation settings before delivery. flowchart LR
A[Provider streams] --> B[Provider adapters]
B --> C[Normalized event grammar]
C --> D[Session archive]
C --> E[Presentation gate]
E --> F[Channel output]
Decision needed
Why: The PR documents a privacy-sensitive boundary for future adapters and channel projections; repository policy requires a maintainer acceptance decision for a draft RFC, and the runtime implementation was not available here to independently validate the asserted behavior. Before merge
Findings
Agent review detailsSecurityNeeds attention: The documentation patch makes a privacy-sensitive archive access rule normative and needs explicit core security acceptance. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Adopt one core-owned stream grammar only after maintainers explicitly approve the archived-reasoning access boundary or revise the RFC to require a separate raw-thinking authorization scope. Do we have a high-confidence way to reproduce the issue? Not applicable: this is a documentation-only RFC. Its claimed runtime behavior needs core-source validation, not a reproduction within the RFC repository. Is this the best way to solve the issue? Unclear: vendoring the contract follows the repository’s sidecar pattern, but the readable-reasoning authorization boundary needs explicit maintainer acceptance before this is the best durable solution. AGENTS.md: not found in the target repository. Codex review notes: model internal, reasoning high; reviewed against af708f0ddb6f. LabelsLabel justifications:
EvidenceSecurity concerns:
What I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (15 earlier review cycles; latest 8 shown)
|
Add the agent event I/O contract that RFC 0008's [AMENDS BASE] points extend, as the in-tree sidecar rfcs/0008/agent-event-io-contract.md, and repoint the RFC's base-contract reference from the dead docs/channels/agent-event-io-contract.md path to it. The contract was extracted from openclaw/openclaw #92216; the upstream maintainer asked that it be split out of that behavior PR so it could be owned and edited separately from runtime code. This RFC sidecar is that separately-ownable home, and gives the four [AMENDS BASE] points a real, pinned target instead of a path that 404s on openclaw main. Resolves the two base-contract review findings (wrong/missing base path; normative contract living in a mutable external repo) for the base half.
|
The base contract now has a home in the RFC tree ( I collaborated with @Marvinthebored on the preamble/commentary normalization work behind this RFC, and we agreed the base contract it amends should live alongside the RFC so it can be owned and developed here instead of referenced from elsewhere. That base contract is The PR into this RFC's branch is Marvinthebored#1 (clean, mergeable). It does two things:
This targets the two P2 findings directly:
Two items I deliberately left to @Marvinthebored rather than guess: folding the four |
rfc(0008): vendor agent event I/O contract as pinned base
…act; sync harness count Addresses ragesaq's two follow-ups on #1 and clawsweeper's P3: - Fold §3.2 (thinking emission unconditional, never dropped), §3.3 (item start at earliest id+name), §5.1 (thinking mirrored on its own stream, not as commentary), §7.3 (stable/composite idempotency ids, no synthesized counters) into rfcs/0008/agent-event-io-contract.md, each marked inline "[RFC 0008 amendment]" so the as-split #92216 baseline stays distinguishable. - Repoint the RFC Unresolved note: amendments folded (no longer pending). - Sync harness count 35 -> 37 tests / 28 goldens to match companion STATUS.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Renumber file + sidecar dir + all internal refs (0008-context-engine-runtime-settings was accepted on main in the interim) - Pin the companion spec repo at 0d354d9ed075 - Replace 'stacked draft PRs' reference-impl section with the landed-on-main implementation ledger (#92216 #93343 #95283 #97875 #96106 #98907, #99401 open) - Add explicit 'Reasoning privacy & archival boundary' section (review P1) - Move resolved unresolved-questions (deepseek boundary → #95283; F5 catalogue; base-contract behavior-half → #92216) to a 'Resolved since draft' list Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All seven cited implementation slices are now on main. The privacy-boundary section's open acceptance question for the CLI envelope was answered by the merge; restated as shipped policy awaiting RFC-level ratification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses the two P2 review findings: - spec preamble: base-contract path repointed to the in-tree sidecar; stale 'F5 catalogue pending' superseded (completed 2026-06-18) - §7 tier taxonomy unified on the RFC's T0/T1/T2 (legacy A→T1, B→T2, C→T2-with-declared-clamp, D→T0; truth table, worked projections, glossary relabeled; the unrelated 'RATIFIED at T2' decision label disambiguated) Reasoning archival boundary unchanged. Sync delta recorded in the vendoring provenance header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses the standing P1 review finding. The RFC previously said the archive tap "inherits" the session-record store's access control without naming that model, which left the privacy-sensitive half of "emit always, archive always, gate only at presentation" unspecified. - Name the authorized readers: archive reads require `operator.read` (or a scope satisfying it) on the session-history RPC/HTTP surface, authorized by negotiated operator scope. - State the enforcement point as a constraint on the projection path rather than on the identity of the surface. Some rendering surfaces legitimately hold `operator.read` — the Control UI and CLI connect as `operator` clients — so a role-based boundary would be false. Projection renders from mirrored events and retrieves no archived content; §7.3's crash-orphaned draft settle reads lifecycle markers only and remains required. - Disclose two inherited limits rather than assume them: `operator.read` is not a per-user or hostile multi-tenant privacy boundary, and shared-secret bearer auth restores the full default operator scope set on session-history endpoints even when the caller declares narrower scopes. - State the trade accurately: no new reader identity or endpoint, but every existing session-history reader is newly authorized over raw reasoning that previously was not retained at all. - Retention: no new class or knob; archived thinking ages, compacts, and is deleted with its containing session record. - Answer the question as posed, and say where it was reframed: there is no channel-session scope separable from `operator.read`, so the provable property is that the projection path retrieves no archived content. - Record the alternative (a distinct raw-thinking scope) as a separate RFC rather than foreclosing it. Sidecars updated to match: normative bullets in the event I/O contract, and §8.2 conformance rewritten into falsifiable cases stating (route, auth mode, effective scopes, expected result), including shared-secret HTTP as a deliberately allowed case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… ratification Refines the access-control precondition added in the previous commit, after a further review pass. - Lead with what already shipped: the seven merged PRs (F1, F1e, F2, codex envelope, Discord, Telegram) already implement this model. The subsection documents landed behavior rather than proposing policy. - Two classes of thinking content, not three. §3.2 labels three variants by provenance, but the normative truth table in §4.3 already collapses raw and summary into one row with one behavior. Where a provider withholds chain-of-thought and offers a summary instead, that summary is the readable content and is handled identically (Gemini `includeThoughts`, §6 F5). - Where a provider encrypts its own chain-of-thought, the privacy decision was made upstream at the API and is not ours to gate or to undo. - Cite the two-store invariant (§2, AS5-02) directly: opaque material never leaves the adapter provider-native transcript, and the archive tap holds displayable content and markers only. Opaque material in the archive is a contract violation, conformance-tested at §8.2 — stated as an invariant, not as an impossibility guarantee. - Scope the exposure statement accordingly: encrypted reasoning is unaffected; what changes is that an existing session-history reader may retrieve readable thinking for turns rendered with `/reasoning off`. - State the two nested privilege systems that bound who that is: slash commands and directives are honored only for authorized senders (channel allowlists/pairing plus `commands.useAccessGroups`), while the history read requires `operator.read`. A participant who cannot re-enable `/reasoning` is not an operator and cannot reach the archive at all. - Keep two caveats visible rather than let a reviewer find them: an empty or "*" channel allowlist leaves directives effectively open, and shared-secret bearer auth is upgraded to the default operator scope set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed The RFC previously deferred to "inherits the session-record store's access control" without naming that model. It now names the authorized readers ( On the finding as worded: there is no channel-session scope separable from Two inherited limits are disclosed in the text rather than left implicit: an empty or |
Summary
Normalize every provider wire format into one event grammar — final text, thinking/reasoning, narration/commentary, tool activity, usage, errors — emit always, archive always, gate only at presentation. The RFC is the design narrative; the pinned companion spec is the contract; the vendored sidecar (
rfcs/0015/agent-event-io-contract.md, from openclaw/openclaw#92216) is the base contract with four explicit[AMENDS BASE]points.Since this was drafted (2026-06-15), the framework has been substantially ratified by landed commits: its core behaviors were implemented piecewise on
mainthrough maintainer-reviewed PRs. This RFC thus seeks to provide the canonical documentation for future or peripheral development, and the normative grammar for the parts not yet upstream.Real behavior proof — the landed-implementation ledger
The proof for this contract is
mainitself. Each row is a merged, maintainer-reviewed PR implementing a slice of the grammar (each carries its own live proof in its PR thread):/reasoning onphase: commentarytagging at the parser, display gated downstream/reasoning off|stream|onstreamed-lane gate + core-owned durable commentary (closed openclaw/openclaw#90962)Update 2026-07-05: openclaw/openclaw#99401 has merged — every implementation slice in the ledger is now on
main. Its merge also answers, in practice, the reasoning archival/privacy acceptance question for the CLI envelope: the same emit-to-bus / gate-at-presentation model the RFC's privacy-boundary section states.Update 2026-07-06 (addressing the review's remaining decision points):
rfcs/0015/stream-grammar-spec.md(from the previously-pinned companion commit0d354d9ed075) — the companion repo now holds only evidence goldens + the executable harness. Resolves the "pinned personal repository as normative source" concern; maintainers may still choose a different final home.Update 2026-07-07 (docs repair per the review's two P2 findings): the vendored spec is now internally consistent with the RFC — base-contract reference repointed to the in-tree sidecar, stale "F5 catalogue pending" superseded, and the §7 tier taxonomy unified on the RFC's T0/T1/T2 table (legacy A→T1, B→T2, C→T2-with-declared-clamp, D→T0; truth table, worked projections, and glossary relabeled). The reasoning archival boundary is unchanged; the sync delta is recorded in the vendoring provenance header for auditability against the pinned original.
Update 2026-08-12 (resolves the standing P1, Define the archive access-control precondition):
The finding asked who may read archived reasoning, given that archival is display-independent. The RFC previously deferred to "inherits the session-record store's access control" without naming that model, which left the privacy-sensitive half of emit always, archive always, gate only at presentation unspecified. A new subsection, Archive access-control precondition, names it. Nothing in it proposes new behavior — the seven landed PRs above already work this way.
operator.read(or a scope satisfying it) on the session-history RPC/HTTP surface, authorized by negotiated operator scope, independent ofclient.id/client.mode.operator.read: the Control UI and CLI connect asoperatorclients, and that scope coverschat.history/sessions.list/sessions.subscribe. A role-based boundary would therefore be false. The rule is that projection renders from mirrored events subject to the presentation gate and retrieves no archived content; §7.3's crash-orphaned draft settle reads lifecycle markers only and remains required.rawandsummaryinto a single row with a single behavior. Where a provider withholds chain-of-thought and offers a summary instead, that summary is the readable content and is handled identically (GeminiincludeThoughts, §6 F5). Encrypted or withheld reasoning is archived as a content-free marker; per the two-store invariant (§2,AS5-02) opaque material never leaves the adapter provider-native transcript, and the archive tap holds displayable content and markers only. Where a provider encrypts its own chain-of-thought, that privacy decision was made upstream at the API and is not OpenClaw's to gate or undo.commands.useAccessGroups), while the history read requiresoperator.read. A channel participant who cannot turn/reasoningback on is not an operator either, and cannot reach the archive at all. That is the distinction the contract preserves — an operator inspecting a session record versus a chat participant from whom output was deliberately gated."*", directives are effectively open, so the nesting above is a property of a configured deployment rather than a guarantee of the design; and shared-secret bearer auth is upgraded to the full default operator scope set on session-history endpoints even when the caller declares narrower ones.On the question as posed. The review asked for proof that channel-session scopes cannot read raw thinking. There is no channel-session scope separable from
operator.read, so a proof of the literal claim would be either vacuous or an assertion about a boundary the Gateway does not implement. The RFC answers the provable form — the projection path retrieves no archived content — and records the literal boundary (gating raw-thinking fields behind a distinct scope) as a separate RFC rather than foreclosing it.Sidecars updated to match: normative bullets in the vendored event I/O contract, and §8.2 conformance rewritten into falsifiable cases stating (route, auth mode, effective scopes after negotiation, expected result), with shared-secret HTTP marked as a deliberately allowed case rather than a refusal assertion that would encode a boundary the Gateway does not have.
Commits:
9db11f4,df1b792. AI-assisted; both revisions went through adversarial review passes, which corrected three material errors in the first draft (a false role-based claim, an over-broad prohibition that would have failed §7.3 crash recovery, and two overstated guarantees).What changed in this revision (2026-07-04)
0008-context-engine-runtime-settingswas accepted onmainin the interim (review finding). File, sidecar dir, and all internal refs updated. (The head branch keeps its original name; branch names are cosmetic.)0d354d9ed075— the normative grammar no longer floats (review finding).Marking ready for review: the remaining open questions are maintainer-preference items, not design blockers. Frontmatter stays
status: draftper the template — acceptance is the maintainers' act at merge.🤖 Generated with Claude Code