返回 CodeWhale
4468-output-presentation-filters.md
根目录 / docs / rfcs / 4468-output-presentation-filters.md
1 # RFC: Output presentation filters without receipt mutation
2
3 **Issue:** #4468
4
5 **Status:** Accepted for the v0.9.2 product boundary
6
7 **Date:** 2026-07-26
8
9 ## Decision
10
11 Codewhale will not run an arbitrary user script between a model response and
12 the canonical session record. Model-native assistant and thinking blocks remain
13 the durable audit, replay, cache-accounting, debugging, and provider-signature
14 source of truth.
15
16 Output compression belongs to an explicit **presentation/export** layer. The
17 first supported controls are the existing safe surfaces:
18
19 - TUI `show_thinking = false` hides thinking from the rendered transcript but
20 does not delete it from the canonical message/receipt path.
21 - `codewhale exec --output-format text|stream-json` selects a documented output
22 encoding. Structured output retains block identity so downstream tools can
23 select `thinking` or `text` without Codewhale rewriting either.
24 - Exports may add a future `--view canonical|response-only|thinking-only`
25 selector. A filtered export must label itself as a derived view and retain a
26 canonical session reference; it must never overwrite the session.
27
28 This addresses the accessibility and automation need behind the proposed
29 CIPHER filter without turning untrusted scripts into invisible transcript
30 editors.
31
32 ## Why
33
34 ### Receipt fidelity
35
36 The session is an audit record. Replacing content before persistence would make
37 it impossible to prove what the provider emitted, would corrupt signed
38 Anthropic thinking blocks, and could make usage/cost receipts disagree with the
39 visible record. Storing only the transformed form is rejected. Storing both
40 forms by default doubles sensitive data and creates ambiguous replay authority,
41 so it is also rejected.
42
43 ### Prompt cache and replay
44
45 Presentation filters do not change request-side token use. In particular,
46 `reasoning_replay_tokens` and provider-specific signed-thinking replay must use
47 the canonical form. A compression claim must separately measure:
48
49 1. terminal/export bytes;
50 2. local storage bytes;
51 3. request-side replay tokens;
52 4. provider cache-hit behavior.
53
54 Only the first is affected by the accepted v0.9.2 boundary.
55
56 ### Streaming
57
58 `HookEvent::ResponseDelta` is observer-only and arrives incrementally. A
59 block-level transformation would require buffering until block end, adding
60 latency and changing cancellation semantics. Presentation consumers may buffer
61 for their own output, but the engine continues to emit and persist canonical
62 deltas.
63
64 ### Trust and failure
65
66 No new arbitrary command execution is added. An external consumer may read
67 `stream-json` and apply its own bounded transform outside Codewhale. Its failure
68 cannot corrupt, delay, or replace the session. The canonical record therefore
69 provides the fail-open source automatically.
70
71 ## Structured-output contract
72
73 `stream-json` is the accessibility and integration surface:
74
75 - events are JSON lines;
76 - response/thinking block identity remains explicit;
77 - tools may omit a block from their derived view, but must not describe that
78 view as the canonical session;
79 - no environment map, provider credential, hidden tool payload, or unrelated
80 transcript content is added for filtering;
81 - downstream tools should bound input, output, and processing time themselves.
82
83 Example response-only presentation:
84
85 ```sh
86 codewhale exec --output-format stream-json "..." \
87 | jq -r 'select(.type == "message_delta") | .text // empty'
88 ```
89
90 The exact event names are versioned runtime output and callers should inspect a
91 fixture from their installed version rather than infer fields from this RFC.
92
93 ## Rejected alternatives
94
95 1. **Pre-persistence output hook.** Rejected: mutates audit/replay authority.
96 2. **Mutate only thinking.** Rejected: signed thinking and request replay still
97 require fidelity.
98 3. **Store canonical plus transformed by default.** Rejected: duplicate
99 sensitive content and unclear authority.
100 4. **Prompt the model to abbreviate.** The reporter measured 0% adoption and it
101 is not a reliable mechanical contract.
102 5. **A separate `[hooks.output_filter]` table.** Rejected: duplicates the hook
103 schema while failing to solve the trust and receipt problems.
104
105 ## Future additive work
106
107 A future derived-export API may accept a declarative, non-executable selector
108 and write a receipt containing the source session id, source content hash,
109 selector, and derived output hash. Arbitrary executable transforms remain an
110 external pipeline unless a later security review defines sandboxing,
111 disclosure, latency, and dual-form retention semantics.
112
113 ## Acceptance checks
114
115 - Canonical session persistence and reasoning replay remain unchanged.
116 - `show_thinking` is documented as display-only.
117 - `stream-json` is documented as the safe machine-readable filter boundary.
118 - No hook stdout gains response-mutation authority.
119 - No new script, shell, credential, or network capability is introduced.
120
121 Credit: the CIPHER measurements and the bounded stdin/stdout/fail-open proposal
122 came from @eugenicum in #4468. The v0.9.2 decision preserves that integration
123 use case while keeping Codewhale's canonical receipts trustworthy.
124
124 lines MARKDOWN