You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Reload adoption covers render fingerprints only: a delivery reminder's turn is not restored, so the same boundary can repeat it #267
0683e5f (main, after #262). Cross-checked against open PR #264 — its diff does not touch the adoption branch or the delivery ledger, so this applies on top of it as well.
Summary
The user/message adoption branch exists because the host re-observes our own correction messages after a plugin reload (src/plugin/fence-feedback.ts:466-467):
Our own correction (re-observed after a plugin reload): adopt its render-failure fingerprints so a second boundary cannot repeat it.
That promise holds for kind=render corrections (hex fingerprints → markersIn → correctedSpec dedupes at :279), but not for kind=delivery reminders:
missingBodyCorrectionText (:302) writes the synthetic marker [genui-fence-repair #turn-<N>]. markersIn extracts the literal string turn-7, which can never equal a fence fingerprint — so the adoption branch (:469-472) adds dead entries to correctedSpec and nothing else.
deliveryRemindedTurns is Set<number> (:221), and no code path parses turn-<N> back into it; correctionsTurn/correctionsThisTurn are not restored either. After a mid-turn reload the ledgers restart at zero.
Consequence
The reload+replay premise is the adoption branch's own: when the host replays the turn's events, a replayed validate_dsh_uitool/call re-sets validatedThisTurn (:438) and replayed render results re-set deliveredThisTurn. If the turn still has no delivery, the next boundary re-sends the same nothing-delivered reminder for the same turn — consuming the second, reserved correction slot with a duplicate (the new state counts it as attempt 1). Bounded by MAX_CORRECTIONS_PER_TURN = 2, so no storm; but it is exactly the repeat the adoption comment says cannot happen, and the reserved slot from #263 (Gap A) is what absorbs it.
The unit suite pins only the render side (tests/fence-feedback.spec.ts:503-518 "so a reload cannot repeat it").
Repro sketch on the repo harness
// twin of the existing adoption test, delivery flavor — currently steers:h.emitSession({type: 'user/message',data: {content: [{type: 'text',text: missingBodyCorrectionText(7)}],source: {kind: FEEDBACK_SOURCE_KIND,form: 'notice',summary: 'x'},}})h.emitSession({type: 'tool/call',data: {name: 'validate_dsh_ui'}})// replayed validateh.boundary({turn: 7,signal: newAbortController().signal})expect(h.steer).not.toHaveBeenCalled()// ← fails today: duplicate reminder, attempt counted as 1
Suggested direction
In the adoption branch, additionally recognize delivery reminders — parse #turn-(\d+) (or the status=nothing_delivered marker body) and:
add the parsed turn number to deliveryRemindedTurns (restores silence), and
restore correctionsTurn = N / correctionsThisTurn = 1 (budget symmetry, so the reload doesn't grant an extra slot).
correctedSpec should then skip the turn-N string, keeping the render ledger clean.
Activity