feat: write-verdict foundations — rejection spike, error taxonomy, uniform /health#11
Merged
Merged
Conversation
…d by code Consumers classify rejection codes through one exported registry instead of string-matching reason text. The pinned alpha emits exactly one code over the sync protocol (permission_denied, permanent); unknown codes classify transient so an unrecognized code can never trigger irreversible compensation.
…rivation, determinism The server adjudicates only stale-policy writes (the client runtime refuses current-policy violations synchronously), so the fixture tightens permissions while devices are offline. Pins three properties a client-side write journal needs: rejections correlate by batch id on both verdict surfaces, unacked writes re-sent on reconnect re-derive their rejection, and the verdict is stable across reconnects and independent runtimes. The fallback event fires once per runtime lifetime; re-derivation goes through wait().
Open mode: Jazz serves it on the public URL. Ticket mode: unauthenticated at the top level and under a ticket base; revocation 401s the ticket path while the top level stays 200. Peer mode: carried over the iroh tunnel. A dead store degrades to 502, never a hang.
New write-verdicts page: where verdicts come from, local refusal vs server adjudication, the three rejection properties, at-least-once implications for consumers, and the permanent/transient taxonomy. The HTTP surface page gains the /health contract per connection mode and the client-side composition into a connection observable.
This was referenced Jul 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the server-side groundwork for lofi's effect system. Closes #10.
Spike outcome — all three rejection properties hold (
tests/write_verdict_test.ts): a rejected write surfacespermission_deniedcorrelated to the originating batch id on both verdict surfaces (wait()→PersistedWriteRejectedError, and theonMutationErrorfallback carrying the batch record); an unacked write is re-proposed on reconnect and its rejection fires then; and the verdict is deterministic across reconnects and independent runtimes. No upstream jazz-napi ask is required for the client journal to record verdicts itself.Two findings shape the client contract: the runtime refuses current-policy violations synchronously at the call site (server adjudication only ever applies to stale-policy writes — accepted locally, denied by a store whose permissions tightened after the client last synced), and the fallback event fires once per runtime lifetime, so consumers persist verdicts keyed by batch id and re-derive on demand via
wait().src/verdict.ts, exported frommod.ts)permission_denied; unknown codes default to transient by design (never compensate on an uninterpretable code — the upstream registry ask is deferred until more codes exist)reason/healthverified available and cheap in open, ticket, and peer modes (no gate change needed — verification, tests, and the written contract)/healthacross modes/nodedocs:write-verdicts.md+/healthcontract inhttp-surface.md(bumpLOFI_NODE_REFfrom the lofi side after this lands)deno task checkclean;deno task test58/58;deno task publish:drysucceeds.