Skip to content

feat: adopt core 0.38's streamlined API - #130

Merged
cuibonobo merged 5 commits into
mainfrom
claude/api-changes-server-update-aqqhm3
Sep 25, 2026
Merged

cuibonobo merged 5 commits into
mainfrom
claude/api-changes-server-update-aqqhm3

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

Updates the server to core 0.38, wire-types 0.37, adapter-local 0.37, and commons and conformance-fixtures 0.32. Core 0.38 reshaped its public API around a single Actor type and gave each concept one name.

  • Code: requests are scoped with Stack.asActor(), which replaces forSession() and onBehalfOf. The other renames are Stack.open(), ownerEntityId on the adapter, StackBadRequestError, .capabilities, the object forms of defineType() and putAttachment(), and TokenSession imported from @haverstack/core/wire.
  • Wire changes (breaking for clients):
    • DELETE /records/:id?purge=true, which replaces ?hard=true.
    • The change feed's resume token is named cursor in the ready frame and on record frames.
    • The journal takes ?afterSeq=.
    • GET /records filters are createdBySubject / createdByPrincipal / attachmentLabel / attachmentFileId / referencesFileId.
    • Records carry createdBy / updatedBy objects.
    • POST /attachments/gc returns deletedFileIds.
    • Grantees and relationship targets use kind instead of scope.
  • POST /tokens takes { principalId?, subjectId? } instead of { entityId, onBehalfOf }. These are the same Actor field names the response and GET /tokens already use. principalId defaults to the owner, and subjectId defaults to the principal.
  • Bug fix in resumeBufferKey: it still read the removed ChangeFilter.entityId and didn't know about baseId or createdBy. As a result, two connections with different filters could have shared one resume buffer and received each other's frames. It now keys on every filter field and treats multi-value fields as sets. Tests are added.
  • Conformance: runs the renamed and new 0.32 fixtures: purge, exact-match typeId and baseId change-feed filters, and the attachment and referencesFileId queries. The query tests use real uploads so the filters are tested against records that should match and records that shouldn't.

Docs

docs/api.md is updated throughout: record filters, the tokens body, purge, cursor/afterSeq, the change-feed filters and ops (reshare, purge), the gc response, and kind on grantees and targets. The wire contract itself changed in haverstack/core (released as core 0.38 / wire-types 0.37 / conformance-fixtures 0.32).

Verification

pnpm run format:check && pnpm run lint && pnpm typecheck && pnpm test && pnpm build all pass: 29 test files, 582 tests.

Notes for reviewers

  • The commit uses feat:, not feat!:, following the precedent of earlier breaking core upgrades (0.10.0). Merging releases 0.11.0. With !, the release workflow would produce 1.0.0.
  • POST /tokens breaks existing clients of that endpoint. This was chosen so the request body uses the same field names as the response and core's Actor type.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XrHN9A5Uzz9FQN6gEeiuX8


Generated by Claude Code

Core 0.38 reshaped its public surface around one Actor type and one name
per concept; the server follows so its wire matches the spec it
implements.

- Scope requests with Stack.asActor(), open with Stack.open(), and pass
  ownerEntityId to the adapter.
- DELETE /records/:id takes ?purge=true; the change feed's resume token
  is `cursor` (ready frame and record frames); the journal bound is
  ?afterSeq=; POST /attachments/gc reports deletedFileIds.
- POST /tokens takes { principalId?, subjectId? }, the same Actor names
  the response and GET /tokens already report, instead of
  { entityId, onBehalfOf }.
- resumeBufferKey keys on every ChangeFilter field (baseId and both
  createdBy halves, as sets). It still read the removed entityId and
  knew nothing of baseId or createdBy, so two connections with different
  filters could have shared one buffer and received each other's frames.
- Dispatch the renamed and new 0.32 conformance fixtures (purge, exact
  typeId and baseId feed filters, attachment and referencesFileId
  queries).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XrHN9A5Uzz9FQN6gEeiuX8
A stale client's old names were silently dropped, widening its request:
POST /tokens with `entityId` minted an owner token, `?hard=true` did a
soft delete, and old filter names returned unfiltered results. Each now
answers 400 naming the replacement. Also key an empty `createdBy` change
filter the same as none.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018rAh3A4jeexocb6UcZRAwa
Replaces the list of renamed names with a general rule: every route the
server parses itself refuses a query param or body key it doesn't define
(400), boolean params take only true/false, and body values of the wrong
type are refused rather than dropped. Routes whose input core's wire
parsers read are left to those parsers. Keeps the empty-createdBy resume
buffer normalization.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018rAh3A4jeexocb6UcZRAwa
A missing required key stays 400; a key present with the wrong shape is
422, per docs/api.md. Also flattens the expiresAt parse in POST /tokens.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Xa2sc4PJtmLYiBT1QpTRyv
@cuibonobo
cuibonobo merged commit 0cfa1c8 into main Sep 25, 2026
3 checks passed
@cuibonobo
cuibonobo deleted the claude/api-changes-server-update-aqqhm3 branch September 25, 2026 20:13
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