fix(selectors): keep downgraded target annotations under the payload ceiling

Merged
AI
1d ago
+159-9

Summary

Field caps count raw UTF-8 bytes (256), but the payload ceiling counts serialized JSON (4096). A control character is 1 raw byte and 6 escaped, so escape-heavy fields could leave a downgraded unverifiable annotation over the ceiling, which its own parser rejects.

After downgrading, the writer now drops rect, scrollRegion, the remaining ancestry, then label, measuring after each drop and stopping once the payload fits. Replay refuses recorded unverifiable evidence before it reads these fields, so the drops do not weaken replay. role and id are never dropped: even fully escaped they total about 3.2 KB. target-annotation.md and ADR 0012 now describe this. 4 files.

Fixes #4333.

Validation

Tested ded068fdc (on 093341a60). pnpm check:affected --run --head ded068fdc: 9 passed, including adr-citations; 7 skipped (web smoke and replay lanes).

  • The escape-heavy regression test failed first on the base: payload 7806 bytes.
  • A sweep test pins the drop order and that each drop was needed; swapping drops or removing the early stop fails it.
  • An invariant test derives the role + id bound from the field cap and the largest JSON escape expansion; doubling the cap makes it fail at 6267 bytes.
  • After the last drop the writer throws rather than return an over-ceiling payload; doubling the real cap hits it.

Outside this PR: session-action-recorder.ts rewrites labels to placeholders after this size check, which could push a payload near the ceiling back over it. Confirmed in review; it predates this PR.

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.