Skip to content

Delivery closure of #262: the reserved second correction is unreachable, and a failed render that returns normally still counts as delivered #263

Description

@nanami-0713

Version

0683e5f (main, after #262). Both gaps runtime-probed on this tree; probes are inline below. Related to but distinct from #259 (host-layer recovery) and #200.

Gap A — the reserved second correction is structurally unreachable

The module header (src/plugin/fence-feedback.ts:11-14) and the MAX_CORRECTIONS_PER_TURN comment (:45-52) reserve the second slot for one scenario: "the first [correction for a fence that failed to RENDER] is answered with another reasoning-only turn". In the current implementation that scenario can never produce a second correction:

  1. The turn's body contains the bad fence → deliveredBodyText (:338-348) counts any non-empty text block as delivery, so deliveredThisTurn sticks true (:431).
  2. Boundary 1 fires the render correction (kind render, slot 1).
  3. The model answers reasoning-only → body has no text → state.text = '', but deliveredThisTurn is sticky and stays true.
  4. Boundary 2: failures is empty, and the delivery-reminder gate :290 (input.deliveredThisTurn === true → null) silences the reminder. Slot 2 never fires; the turn ends with the raw JSON still on screen.

Difference probe (same turn: validate + no delivery; the only variable is whether an earlier message carried a fence-bearing body):

earlier body WITHOUT fence → boundary steers once (kind delivery)   ✓
earlier body WITH fence    → boundary steers nothing (gate :290)    ✗

The header's "Reasoning-only recovery" bullet (:15-17) documents a path the #236-review redesign removed (fences are now read from the body only, and the plan input no longer carries reasoning), so both header claims describe behavior that no longer exists. The only reachable slot-2 today is a second, different bad fence in the retry answer — not the documented one.

Question: is the sticky-delivered semantics intentional (then the header/comment/README/CHANGELOG claims need updating), or should the delivery reminder exempt a turn that already received a render correction (e.g. let kind-render corrections clear the sticky flag for reminder purposes)? The latter matches what :45-52 says the budget of two is for.

Gap B — a failed render that returns normally still counts as delivered

tool.ts:219-222: when the spec is unrecoverable, render_ui returns normally:

[genui-render]
status=invalid
error=invalid_spec
next=fix_and_retry

No throw → the tool/result arrives with isError: false and no data.error → the success check fence-feedback.ts:461 (data.error === undefined && block.isError !== true) sets deliveredThisTurn = true → the boundary stays silent, while the user sees only the fallback toolview line (toolview.tsx:53-55). Nothing was rendered.

This is precisely the outcome 4682c90 declares it fixes ("a failed render left the user with nothing while the boundary stayed silent forever") — closed for the throw path, still open for the normal-return status=invalid path, which the tool's own protocol (next=fix_and_retry) classifies as a failure to retry.

Probe: validate_dsh_ui → render_ui (unrepairable spec) → normal result → boundary steers 0; the identical sequence with an isError result steers 1 (status=nothing_delivered).

Direction (at your discretion): treat status=invalid in a render_ui result text as a non-delivery (the data is already in the result the plugin observes), or have tool.ts throw for the unrecoverable branch so the existing isError path covers it.

Minor note

With formal-event-only triggering, a turn where render_ui failed but validate_dsh_ui was never called also stays silent by design (:288) — same user-visible outcome as Gap B, so I'm flagging it only as a question, not a demand.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions