PERF GUARD: require revision, snapshot, mutation and stream contract tests (#663) #795

Open
opened 2026-10-02 13:10:56 +00:00 by kayg · 1 comment
Owner

PERF GUARD: require revision, snapshot, mutation and stream contract tests (#663)

Finding and context

origin/dev MailView.svelte:384 fetches detail on open. Queued #665 adds the
revision cache and conditional_json; #666 adds viewSnapshots; #667 adds
mutations and durable receipts. Those helpers do not prove every surface uses
them. #668 owns the stream. This issue owns a reusable adoption test runner,
not replacements for those primitives or their Tab adoption issues.

Exact detection and executable contracts

Resolve hot read call sites in registered surfaces. Reject raw fetch/apiFetch
for a classified cached body endpoint unless its call site is the registered
revision-cache adapter; unknown computed URLs require a scoped exception.
Require User/item/revision/representation fields at the typed adapter boundary
and numeric byte/entry/pending limits. Raw downloads are a separate class.
Require a test for each read/surface pair, not only the generic cache library.

Read contracts: first GET returns a strong ETag; matching If-None-Match returns
304 with no body; every changed representation field changes the validator.
Authorization occurs before 304, including revoke/plugin disable. Warm opening
uses the exact retained object, sends zero requests and publishes no replacement
model. Changed revision misses; stale in-flight results cannot paint after
User switch/sign-out/revoke. Overflow evicts by bytes without unlimited pending
or prefetch work. Lists supply the detail header before lazy body completion.

Snapshot contracts: leave/return while refresh is held pending; rows, selection
and scroll restore immediately with <=1 bounded refresh. Resident bytes/rows
stay within declared limits across 100 different views. Session end clears
snapshots before the next User can paint. Reuse #666, not a private route map.

Mutation contracts: hold response pending and assert item/selection/next item
change in one synchronous observable transaction. Requests carry a client ID.
Same-User duplicate delivery before and after restart applies once; another
User cannot replay it. A timeout is unknown, not a rollback. A definite
rejection rolls back. Undo uses the durable receipt inverse and cannot overwrite
a concurrent edit silently. Reuse #667 test fixtures and helper.

Stream contracts: 100 invalidations within 100 ms cause one capped delta read;
unchanged object revisions retain identity. One shared per-User connection,
no private surface polling stream. Disconnect/cursor reset and session/access
change cannot leak old data. Enforce row/byte caps on deltas and test authorization
again when reading them. Background polling for external ingestion is separate.

Tests and gates

The registry requires these exact test exports for each applicable surface;
missing/skipped exports fail CI. Fake clock/network tests run under bun run test;
server receipt and HTTP contracts under per-crate cargo test. Add negative
runner fixtures for a surface using raw fetch, a missing test and a test suite
that only covers the primitive. Access contract failures cannot be allow-listed.

Scope and exception contract

Specification only, from #663 perf-guards. Implement in a later job. Reuse the
shared guard registry and ledger described in the coverage issue. Until it
exists, use this exact contract: an exception has rule ID, repo-relative file,
symbol or route/surface ID, normalized syntax hash (or exact build metric),
owner issue, reason, replacement test and UTC expiry date. No directory globs,
line-number-only entries or ignore all comments. Fail on expired, changed,
duplicate and unused exceptions. Existing debt needs an explicit entry and a
non-growing limit. Print exceptions in CI. An exception does not waive access,
session clearing or accessibility tests. Generated and test fixtures are
excluded by explicit source class, not substring matching. Tests must prove
both violation detection and valid exceptions. No product fix belongs here.

The static/contract command exits 1 for violations, 2 for invalid configuration
or parse/build errors, and 0 only for complete passing coverage. Output includes
rule ID, file:line, symbol/route, actual value and required value. No User content.
Measured host timing stays periodic under CLAUDE.md. Deterministic guards fail
required CI; the dedicated perf-budget runner reports measured budget failures
with nonzero status but is not a required merge check.

# PERF GUARD: require revision, snapshot, mutation and stream contract tests (#663) ## Finding and context origin/dev MailView.svelte:384 fetches detail on open. Queued #665 adds the revision cache and conditional_json; #666 adds viewSnapshots; #667 adds mutations and durable receipts. Those helpers do not prove every surface uses them. #668 owns the stream. This issue owns a reusable adoption test runner, not replacements for those primitives or their Tab adoption issues. ## Exact detection and executable contracts Resolve hot read call sites in registered surfaces. Reject raw fetch/apiFetch for a classified cached body endpoint unless its call site is the registered revision-cache adapter; unknown computed URLs require a scoped exception. Require User/item/revision/representation fields at the typed adapter boundary and numeric byte/entry/pending limits. Raw downloads are a separate class. Require a test for each read/surface pair, not only the generic cache library. Read contracts: first GET returns a strong ETag; matching If-None-Match returns 304 with no body; every changed representation field changes the validator. Authorization occurs before 304, including revoke/plugin disable. Warm opening uses the exact retained object, sends zero requests and publishes no replacement model. Changed revision misses; stale in-flight results cannot paint after User switch/sign-out/revoke. Overflow evicts by bytes without unlimited pending or prefetch work. Lists supply the detail header before lazy body completion. Snapshot contracts: leave/return while refresh is held pending; rows, selection and scroll restore immediately with <=1 bounded refresh. Resident bytes/rows stay within declared limits across 100 different views. Session end clears snapshots before the next User can paint. Reuse #666, not a private route map. Mutation contracts: hold response pending and assert item/selection/next item change in one synchronous observable transaction. Requests carry a client ID. Same-User duplicate delivery before and after restart applies once; another User cannot replay it. A timeout is unknown, not a rollback. A definite rejection rolls back. Undo uses the durable receipt inverse and cannot overwrite a concurrent edit silently. Reuse #667 test fixtures and helper. Stream contracts: 100 invalidations within 100 ms cause one capped delta read; unchanged object revisions retain identity. One shared per-User connection, no private surface polling stream. Disconnect/cursor reset and session/access change cannot leak old data. Enforce row/byte caps on deltas and test authorization again when reading them. Background polling for external ingestion is separate. ## Tests and gates The registry requires these exact test exports for each applicable surface; missing/skipped exports fail CI. Fake clock/network tests run under bun run test; server receipt and HTTP contracts under per-crate cargo test. Add negative runner fixtures for a surface using raw fetch, a missing test and a test suite that only covers the primitive. Access contract failures cannot be allow-listed. ## Scope and exception contract Specification only, from #663 perf-guards. Implement in a later job. Reuse the shared guard registry and ledger described in the coverage issue. Until it exists, use this exact contract: an exception has rule ID, repo-relative file, symbol or route/surface ID, normalized syntax hash (or exact build metric), owner issue, reason, replacement test and UTC expiry date. No directory globs, line-number-only entries or `ignore all` comments. Fail on expired, changed, duplicate and unused exceptions. Existing debt needs an explicit entry and a non-growing limit. Print exceptions in CI. An exception does not waive access, session clearing or accessibility tests. Generated and test fixtures are excluded by explicit source class, not substring matching. Tests must prove both violation detection and valid exceptions. No product fix belongs here. The static/contract command exits 1 for violations, 2 for invalid configuration or parse/build errors, and 0 only for complete passing coverage. Output includes rule ID, file:line, symbol/route, actual value and required value. No User content. Measured host timing stays periodic under CLAUDE.md. Deterministic guards fail required CI; the dedicated perf-budget runner reports measured budget failures with nonzero status but is not a required merge check.
Author
Owner

F4 — P2: valid cache adapters and mutations still require raw-read exceptions

Evidence: scripts/perf_guards/rules.py:171 flags every call whose final name
is fetch or apiFetch. browser_rules does not take the registry or test
exports. It cannot accept the registered adapter, distinguish a mutation from
a cached read, or accept a raw download. This contradicts #795's separate
classes. Adding the required contract tests cannot remove this finding.

At scripts/perf_guards/core.py:180, a cache variant is checked only for four
nonempty strings and three positive integers. The strings are not bound to
the actual adapter. This checks a declaration, not use of a revision key.

Bind read sites to parsed adapter symbols and operation classes. Accept the
real adapter and valid mutation/download paths. Check its typed identity
fields. Add positive fixtures that pass without debt and a negative fixture
whose declared revision key is not used by the adapter. Owner: #795.

## F4 — P2: valid cache adapters and mutations still require raw-read exceptions Evidence: `scripts/perf_guards/rules.py:171` flags every call whose final name is `fetch` or `apiFetch`. `browser_rules` does not take the registry or test exports. It cannot accept the registered adapter, distinguish a mutation from a cached read, or accept a raw download. This contradicts #795's separate classes. Adding the required contract tests cannot remove this finding. At `scripts/perf_guards/core.py:180`, a cache variant is checked only for four nonempty strings and three positive integers. The strings are not bound to the actual adapter. This checks a declaration, not use of a revision key. Bind read sites to parsed adapter symbols and operation classes. Accept the real adapter and valid mutation/download paths. Check its typed identity fields. Add positive fixtures that pass without debt and a negative fixture whose declared revision key is not used by the adapter. Owner: #795.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#795
No description provided.