Skip to content

Close the If-Match gap on delete/restore/associations/permissions; make total:null explicit - #67

Merged
cuibonobo merged 1 commit into
mainfrom
claude/report-records-issues-284k58
Aug 19, 2026
Merged

Close the If-Match gap on delete/restore/associations/permissions; make total:null explicit#67
cuibonobo merged 1 commit into
mainfrom
claude/report-records-issues-284k58

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

This started as a sweep of five open "records" issues (#32, #34, #35, #36, #57). Reading each one against the current code (post-#51/#65) showed most of the batch had already landed:

That left one real gap and one piece of defensive hardening, both fixed here:

  • Core sync: If-Match optimistic concurrency + the one-versioning-rule wire surface (core #48, #61, #62) #35 (If-Match / optimistic concurrency)PATCH, undelete, and migrate already honored If-Match, but the spec's endpoint list is longer: DELETE /records/:id, POST /records/:id/restore/:version, POST /records/:id/associations, POST /records/:id/associations/delete, and PUT /records/:id/permissions didn't read the header at all, even though ScopedStack (@haverstack/core 0.10.0) has accepted an ifVersion option on every one of those methods since the dependency bump. The conformance-fixtures package (v0.2.0) only ships an If-Match fixture for PATCH so far, so nothing in CI was catching this. Wired If-MatchifVersion through all five remaining handlers.
  • Query response envelope: total must be null over the wire, and cursor is the only end-of-results signal #57 (total envelope)ScopedStack.query() already guarantees total: null, and it was already forwarded correctly, but the route trusted result.total rather than asserting the wire guarantee explicitly. Hardcoded total: null in both query handlers (GET /records, POST /records/query) so the guarantee holds independent of which query path produced the result. The versions/types bare-array envelope question this issue also raised was already answered correctly — no change needed there.

Changes

  • src/routes/records.ts: thread If-Match through DELETE, restore/:version, associations (POST + delete), and PUT /permissions; hardcode total: null in both query handlers.
  • tests/routes/records.test.ts, tests/routes/associations.test.ts, tests/routes/versions.test.ts: match/mismatch (412 version_conflict) tests for each newly-wired endpoint.
  • docs/api.md: document the full If-Match endpoint list and the total/empty-page envelope rules.

No code changes were needed for #34 or #36 — they're already done.

Test plan

  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm test — 225/225 passing (10 new tests, no regressions)

Generated by Claude Code

…issions; make total:null explicit (#35, #57)

Issue #35's spec-cited endpoint list for If-Match/ifVersion is longer than
what the routes actually wired up: PATCH, undelete, and migrate honored the
header, but DELETE /records/:id, POST /records/:id/restore/:version,
POST /records/:id/associations[/delete], and PUT /records/:id/permissions
did not, even though ScopedStack has accepted ifVersion on all of them since
the 0.10.0 dependency bump. Thread the header through the remaining five
handlers and pin each with a match/mismatch test.

Also hardcode `total: null` at the query-response serialization boundary
(#57) rather than forwarding result.total — ScopedStack.query() already
guarantees null, but the issue's point is that the wire guarantee shouldn't
depend on which query path produced the result. Document both the full
If-Match endpoint list and the total/empty-page envelope rules in
docs/api.md.

#32, #34, and #36 turned out to already be fully implemented, tested, and
documented by earlier work (#51, #65) — no code changes needed for those.
@cuibonobo
cuibonobo merged commit 142c8e9 into main Aug 19, 2026
4 checks passed
@cuibonobo
cuibonobo deleted the claude/report-records-issues-284k58 branch August 19, 2026 12:49
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.

2 participants