Summary
classify_submit_confirmation (src-tauri/src/http/handlers/inject.rs:68-76) returns
Some(true) / sustained-post-submit-activity whenever the PTY output ring changes after the
submit write. It cannot tell the composer reacted to Enter apart from the receiver was already
producing output for unrelated reasons.
Against a busy receiver it therefore confirms submits that did not happen.
This is documented as a caveat in the receipt's own evidence_scope string ("An agent that was
already streaming output can produce a false positive"), but it is treated as a footnote. In
practice it is the common case, not the edge case.
Live evidence — session 7fb25633-4827-4ea1-9646-17d5c7852126, 2026-08-20
Four Queen-issued injects to three codex principals that were mid-indexing:
| Target |
payload_bytes |
submit_bytes |
submit_confirmed |
basis |
| worker-1 |
284 |
1 |
true |
sustained-post-submit-activity |
| worker-2 |
408 |
1 |
true |
sustained-post-submit-activity |
| worker-3 |
390 |
1 |
true |
sustained-post-submit-activity |
| worker-2 (addendum) |
~330 |
1 |
true |
sustained-post-submit-activity |
All four payloads were still sitting unsubmitted in the composers. The operator observed this
directly and reported it. A follow-up {"message":"","submit":true} bare-Enter flush to each
target submitted all three.
Why this observation is admissible under #241's evidence rule
#241 bars verifying an inject by watching whether the agent acts, because an operator at the
terminal cannot distinguish an auto-submit from their own keypress. That rule constrains
positive observations only. This is a negative observation — text visibly resident in a
composer — and no operator keypress can manufacture a non-submit. The asymmetry should be
written into docs/pty-submit-sweep.md: negative observations are valid evidence even when
uncontrolled; positive ones are not.
Why this matters more than a noisy field
The compensating retry in #259 is keyed on the signal this bug suppresses. That retry fires
strictly on Some(false) (a deliberate decision — the tri-state exists so an ambiguous buffer is
never upgraded). A false Some(true) bypasses it entirely.
So the two defects compose badly:
- Idle receiver, Enter swallowed ->
Some(false) -> retry fires. Handled.
- Busy receiver, Enter swallowed -> false
Some(true) -> no retry. Not handled.
And the busy case is the one that actually occurs, because inject exists precisely to interrupt an
agent that is working. An idle receiver is the rare state.
Secondary hypothesis worth measuring
A bracketed-paste payload followed by a discrete CR after the configured gap — the post-#256
design — still failed to submit against a busy codex composer. That points at a receiver state
problem (the TUI is not accepting keystrokes mid-generation) rather than the timing problem #256
addressed. If true, no sender-side gap tuning fixes it and the retry in #259 will also be
swallowed.
The busy-state column of the sweep matrix in docs/pty-submit-sweep.md is the right place to
settle this. Owned by #241.
Suggested directions
- Establish an activity baseline before the payload write, not just before the submit write,
and require a change in character rate or a composer-specific signature rather than any byte
delta. Downgrade to None when the receiver was already streaming.
- Consider whether a busy receiver should be classified
None unconditionally — an honest
"unknown" is more useful than a confident wrong answer, and None is already the value that
correctly means "do not act on this."
- Do not let the retry key widen to
None as a workaround. That reintroduces the
double-execution risk that bounding it to the confident negative was chosen to avoid.
Related: #241 (owns the sweep matrix), #259 (the retry this suppresses), #256, #257
Summary
classify_submit_confirmation(src-tauri/src/http/handlers/inject.rs:68-76) returnsSome(true)/sustained-post-submit-activitywhenever the PTY output ring changes after thesubmit write. It cannot tell the composer reacted to Enter apart from the receiver was already
producing output for unrelated reasons.
Against a busy receiver it therefore confirms submits that did not happen.
This is documented as a caveat in the receipt's own
evidence_scopestring ("An agent that wasalready streaming output can produce a false positive"), but it is treated as a footnote. In
practice it is the common case, not the edge case.
Live evidence — session
7fb25633-4827-4ea1-9646-17d5c7852126, 2026-08-20Four Queen-issued injects to three codex principals that were mid-indexing:
truesustained-post-submit-activitytruesustained-post-submit-activitytruesustained-post-submit-activitytruesustained-post-submit-activityAll four payloads were still sitting unsubmitted in the composers. The operator observed this
directly and reported it. A follow-up
{"message":"","submit":true}bare-Enter flush to eachtarget submitted all three.
Why this observation is admissible under #241's evidence rule
#241 bars verifying an inject by watching whether the agent acts, because an operator at the
terminal cannot distinguish an auto-submit from their own keypress. That rule constrains
positive observations only. This is a negative observation — text visibly resident in a
composer — and no operator keypress can manufacture a non-submit. The asymmetry should be
written into
docs/pty-submit-sweep.md: negative observations are valid evidence even whenuncontrolled; positive ones are not.
Why this matters more than a noisy field
The compensating retry in #259 is keyed on the signal this bug suppresses. That retry fires
strictly on
Some(false)(a deliberate decision — the tri-state exists so an ambiguous buffer isnever upgraded). A false
Some(true)bypasses it entirely.So the two defects compose badly:
Some(false)-> retry fires. Handled.Some(true)-> no retry. Not handled.And the busy case is the one that actually occurs, because inject exists precisely to interrupt an
agent that is working. An idle receiver is the rare state.
Secondary hypothesis worth measuring
A bracketed-paste payload followed by a discrete CR after the configured gap — the post-#256
design — still failed to submit against a busy codex composer. That points at a receiver state
problem (the TUI is not accepting keystrokes mid-generation) rather than the timing problem #256
addressed. If true, no sender-side gap tuning fixes it and the retry in #259 will also be
swallowed.
The busy-state column of the sweep matrix in
docs/pty-submit-sweep.mdis the right place tosettle this. Owned by #241.
Suggested directions
and require a change in character rate or a composer-specific signature rather than any byte
delta. Downgrade to
Nonewhen the receiver was already streaming.Noneunconditionally — an honest"unknown" is more useful than a confident wrong answer, and
Noneis already the value thatcorrectly means "do not act on this."
Noneas a workaround. That reintroduces thedouble-execution risk that bounding it to the confident negative was chosen to avoid.
Related: #241 (owns the sweep matrix), #259 (the retry this suppresses), #256, #257