Skip to content

Core sync: adopt the shared conformance fixtures; fix the PATCH body shape; add the intent-split endpoints (core #52) #32

Description

@cuibonobo

Server side of haverstack/core#52. That issue affirms the spec's PATCH contract (content-only RFC 7396 merge patch), splits the adapter's updateRecord grab-bag into intent-revealing operations with a 1:1 wire mapping, and — most importantly for this repo — creates a shared conformance fixture suite run by both adapter-api and this server. Nearly every other design-review decision (#53 errors, #55 IDs, #48 concurrency, #65/#66 attachments, #67/#68 invariants) names those fixtures as its enforcement point, so this is the contract backbone for the whole sync batch.

Server divergence that exists today

The PATCH body shape is already wrong. Spec §Records: "PATCH /records/:id accepts a partial content object" — the body is the patch. The handler reads an envelope instead (src/routes/records.ts:224):

const patch = (body.content ?? {}) as Record<string, unknown>;

A spec-conforming client sending { "title": "New" } gets body.content === undefined → empty patch → a silent no-op update that still bumps version and snapshots. Worst-case wire dishonesty: 200, new version, nothing changed. (Our own tests send the envelope, which is how this survived.) Decide the canonical shape with the spec/core — core #52's resolution keeps the spec's raw-patch semantics — and fix whichever side is wrong, then let the fixtures pin it.

Note the null-deletion consequence: with the raw patch traveling, { "title": null } must delete the field server-side (RFC 7396). The route already delegates merge semantics to Stack.update(), so this mostly follows from the body fix.

Work items

  • Fix PATCH body shape to the spec'd raw content patch (coordinate with core #52's shared merge helper; server merges against its own current state)
  • Add POST /records/:id/migrate (migration commit — currently has no wire representation at all, so migrateAll() cannot work over adapter-api)
  • Verify PUT /records/:id/permissions and POST /records/:id/restore/:version match the intent-split contract 1:1 (they exist; fixtures will pin bodies/status codes)
  • Import and run the core #52 conformance fixtures in CI once published; treat them as the acceptance gate for the rest of the sync issues
  • Pagination fixture per core #50's spec language (absent limit = one default page, not "everything")

Refs haverstack/core#52, haverstack/core#47 (migration commit), haverstack/core#48 (expectedVersion on patch), haverstack/core#50.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions