Skip to content

Record why bounded recall fell back to free hot memory - #13

Merged
jbwinters merged 5 commits into
mainfrom
fix/recall-latency-20261106
Nov 7, 2026
Merged

jbwinters merged 5 commits into
mainfrom
fix/recall-latency-20261106

Conversation

@jbwinters

@jbwinters jbwinters commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Bounded hosted recall now records sanitized failure reasons and distinguishes confirmed zero-charge failures from unknown commit outcomes. Unknown commits require a retry with the same idempotency key. Local-first sessions continue to skip automatic paid recall.

Validation at 3c6a523: typecheck, boundary check, build, and 431/431 local tests passed. Node 22/24 CI, reproducible build, native-host Docker and coding-host journeys passed. Regression cases cover deadlines, timeouts, unknown commits, contradictory charge claims and verified-hot-memory fallback. Server counterpart: onlybots MR !31.

The fallback action now carries a classified failure (timeout, aborted,
HTTP status and error code, or an error name) so local logs distinguish a
client timeout from a server error or a recall deadline. Messages, URLs and
bodies are never copied.
@jbwinters

Copy link
Copy Markdown
Contributor Author

Independent review: approve with nits

The classifier is correct: AbortSignal.timeout produces TimeoutError, and messages, URLs and bodies aren't copied. Focused tests pass (9/9).

Nits (fixes coming):

  • error.code from the server body is copied without validation. Apply the same ^[A-Za-z][A-Za-z0-9_]{0,63}$ check that name gets.
  • No lifecycle-level test asserts that the fallback action records details.failure.
  • The fallback warning ("a timed-out paid request may still settle") could use the failure class. A 503 recall_deadline_exceeded means nothing was charged.

… charged

The recorded failure code passes the same identifier check as the error name.
The fallback warning now follows the failure class: a server deadline that
reports nothing charged says so, a 4xx names the rejection, and only timeouts,
network failures and unconfirmed server errors keep the may-still-settle caution.
A lifecycle test asserts that the fallback action records details.failure.
@jbwinters

Copy link
Copy Markdown
Contributor Author

Review nits are fixed in 782f02b.

  • error.code validation: describeHostedFailure() now applies the same ^[A-Za-z][A-Za-z0-9_]{0,63}$ check to the server's code as to name. A code that fails the check, including one over 64 characters, becomes null, and the tests cover both cases.
  • Lifecycle-level test: tests/runtime/hosted-lifecycle-security.test.ts drives submitLifecycleEvent with recallPolicy: "bounded" in two cases: a 503 recall_deadline_exceeded and a fetch TimeoutError. Both assert that the failed context_pack action records details: { freeFallback: true, failure } with the expected class, and that verified hot memory still reaches the prompt.
  • Warning by failure class: the new describeBoundedRecallFallback() picks the warning.
    • 503 recall_deadline_exceeded, or any error with charged_sats: 0: "...missed its response budget (HTTP 503) and charged nothing; a retry is safe."
    • Other 4xx: names the rejection, with no settle claim.
    • Timeouts, aborts, network errors and 5xx without confirmation: keep "may still settle; inspect billing before retrying". Those are the cases where the server may have committed after the client gave up, and a gateway 5xx can follow a commit.

Verification: typecheck 0, build 0, focused tests 15/15, full suite 402/402.

@jbwinters
jbwinters merged commit fa59154 into main Nov 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant