Skip to content

Conformance fixtures: invalid ids a #55-conformant server must reject, a wrong cursor-error message, and missing fixtures for the newest contracts #115

Description

@cuibonobo

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-z excluding 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:

Already claimed elsewhere — cross-ref, don't duplicate here: 413 payload_too_large (#114), migrate/restore auto-snapshot (#110), version-read 403 + stripped snapshot permissions (#109), POST /attachments metadata creation + non-owner _attachment@1 403 + anti-oracle error shape (#106).

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions