You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Conformance fixtures: invalid ids a #55-conformant server must reject, a wrong cursor-error message, and missing fixtures for the newest contracts #115
Three corrections to @haverstack/conformance-fixtures — the package the spec repeatedly names as the enforcement point for wire contracts (principle 7). Best done as one coordinated pass, since D1's id regeneration and D2's new fixtures touch the same file.
D1 — fixture data violates the spec's own ID rules
The fixtures use ids like rec-1, rec-2, rec-attachment-2 and fileIds like abc123/def456 (packages/conformance-fixtures/src/index.ts). Under #55 a conformant server "validates always": a client-supplied id must be exactly 12 lowercase Crockford base-32 chars or the create is 400, and DiskBlobAdapter.assertFileId rejects a non-64-hex fileId. So the enforcement suite and the rules it enforces cannot both pass — a #55-conformant server rejects the very fixtures meant to validate it.
Actively-rejected today: createRecordFixtures (POST /records with id: "rec-1", rec-attachment-2), error-conflict-duplicate-id (POST with rec-1), and the attachmentDownloadFixtures (fileId abc123/def456). PATCH/DELETE/associate fixtures that only carry the id in the path are looked-up-not-validated, so they merely 404 rather than 400 — but regenerate everything for consistency.
Fix: regenerate all record ids as valid 12-char lowercase Crockford (0-9, a-zexcluding i l o u — so mnemonic strings like note/file won't work; pick genuinely-valid ones or mint them with generateId()), and all fileIds as real 64-char lowercase hex.
D2 — the contracts decided latest have no fixtures
The spec says these mappings "are pinned by the shared conformance fixtures," but they aren't. Add fixtures for the ones not already owned by another open issue:
D3 — the malformed-cursor fixture pins a message the codec never produces
error-bad-request-malformed-cursor expects 'Invalid cursor: unknown sort field "not-a-valid-cursor"', but decodeCursor never reaches that branch: atob("not-a-valid-cursor") throws first (hyphens aren't base64), yielding 'Invalid cursor: malformed "not-a-valid-cursor"' (sqlite-shared/src/cursor.ts:21-27). A consumer comparing bodies fails against a correct implementation.
Fix: correct the expected message to the malformed-base64 one. Optionally add a second fixture for the decodable-but-unknown-sort-field path (a valid-base64 cursor encoding badfield|123|id) so both 400 branches are pinned — they're distinct code paths that both map to bad_request.
Refs
#55 (ID authority — D1), #52/#53 (fixtures + error taxonomy — D2/D3), #48/#68/#67/#49 (the unpinned contracts — D2). Cross-refs: #106, #109, #110, #114. From docs/design-assessment-2026-07.md §D1/§D2/§D3.
Three corrections to
@haverstack/conformance-fixtures— the package the spec repeatedly names as the enforcement point for wire contracts (principle 7). Best done as one coordinated pass, since D1's id regeneration and D2's new fixtures touch the same file.D1 — fixture data violates the spec's own ID rules
The fixtures use ids like
rec-1,rec-2,rec-attachment-2and fileIds likeabc123/def456(packages/conformance-fixtures/src/index.ts). Under #55 a conformant server "validates always": a client-supplied id must be exactly 12 lowercase Crockford base-32 chars or the create is 400, andDiskBlobAdapter.assertFileIdrejects a non-64-hex fileId. So the enforcement suite and the rules it enforces cannot both pass — a #55-conformant server rejects the very fixtures meant to validate it.Actively-rejected today:
createRecordFixtures(POST /records withid: "rec-1",rec-attachment-2),error-conflict-duplicate-id(POST withrec-1), and theattachmentDownloadFixtures(fileIdabc123/def456). PATCH/DELETE/associate fixtures that only carry the id in the path are looked-up-not-validated, so they merely 404 rather than 400 — but regenerate everything for consistency.Fix: regenerate all record ids as valid 12-char lowercase Crockford (
0-9,a-zexcludingi l o u— so mnemonic strings likenote/filewon't work; pick genuinely-valid ones or mint them withgenerateId()), and all fileIds as real 64-char lowercase hex.D2 — the contracts decided latest have no fixtures
The spec says these mappings "are pinned by the shared conformance fixtures," but they aren't. Add fixtures for the ones not already owned by another open issue:
If-Matchprecondition → 412version_conflict, including theversionConflict{ recordId, expectedVersion, actualVersion }payload anifVersionretry loop needs (Clarify whatversionis for, and add opt-in optimistic concurrency so concurrent writers can't corrupt version history #48)POST /typesnon-additive redefinition → 409schema_driftwith theschemaDrift{ typeId, violations }payload (Schema-drift guard: schemaHash is computed and stored but compared nowhere — defineType silently replaces types #68)_prefix onPOST /records(the duplicate-id 409 already exists — keep it, with a valid id per D1)_configprotection over the wire:DELETE /records/_config→ 409, andPATCH /records/_configchangingentityId→ 409 (_config is unprotected: update can change stack ownership, hard delete bricks the stack, query exclusion is a per-adapter convention #67)Already claimed elsewhere — cross-ref, don't duplicate here: 413
payload_too_large(#114), migrate/restore auto-snapshot (#110), version-read 403 + stripped snapshotpermissions(#109),POST /attachmentsmetadata creation + non-owner_attachment@1403 + anti-oracle error shape (#106).D3 — the malformed-cursor fixture pins a message the codec never produces
error-bad-request-malformed-cursorexpects'Invalid cursor: unknown sort field "not-a-valid-cursor"', butdecodeCursornever reaches that branch:atob("not-a-valid-cursor")throws first (hyphens aren't base64), yielding'Invalid cursor: malformed "not-a-valid-cursor"'(sqlite-shared/src/cursor.ts:21-27). A consumer comparing bodies fails against a correct implementation.Fix: correct the expected message to the malformed-base64 one. Optionally add a second fixture for the decodable-but-unknown-sort-field path (a valid-base64 cursor encoding
badfield|123|id) so both 400 branches are pinned — they're distinct code paths that both map tobad_request.Refs
#55 (ID authority — D1), #52/#53 (fixtures + error taxonomy — D2/D3), #48/#68/#67/#49 (the unpinned contracts — D2). Cross-refs: #106, #109, #110, #114. From
docs/design-assessment-2026-07.md§D1/§D2/§D3.