Skip to content

feat: user.* namespace, core-computed verification TTL, user.created on mint - #65

Merged
OriginDevIT merged 3 commits into
mainfrom
feat/user-namespace-and-verification-ttl
Sep 1, 2026
Merged

feat: user.* namespace, core-computed verification TTL, user.created on mint#65
OriginDevIT merged 3 commits into
mainfrom
feat/user-namespace-and-verification-ttl

Conversation

@OriginDevIT

Copy link
Copy Markdown
Owner

Brings the claim submit path in line with spec v0.5.

  • adapter-kit gains the user.* namespace (user.created only)
  • core computes claim.verification_started.expires_at from an injected clock and per-method 9.5 TTL config; the caller no longer relays it. Out-of-bounds config throws, never clamps
  • user.created is emitted when claim.submit mints a users row, ahead of claim.submitted in the same transaction. Nothing on reuse
  • writeOutboxEvents takes a keyIndex so the idempotency key stays on the command's headline event

Tenant TTL config is threaded as a parameter; reading it from a settings table is #63.

Closes #54
Closes #58

OriginDevIT and others added 3 commits August 31, 2026 20:33
Adds `UserEventType` (`user.created` only) to the §3.3 catalogue and the
`EventType` union, per spec v0.5 §3.2/§3.3/§4.3. There is no `user.create`
command - a row is minted inside the command that needs one (today
`claim.submit`) and `user.created` is emitted. Tracks the header comment to
v0.5.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRhu5TK6KW3YyXFR8yDDJ1
… config

claim.submit no longer relays verification_expires_at from the caller. A new
pure resolver, resolveVerificationTtl(method, config?), returns the code
lifetime from the §9.5 per-method defaults and bounds; handleClaimSubmit adds
it to an injected clock (a resolved Date, never imported) to stamp
claim.verification_started.expires_at. manual and gbp_oauth have no OSDS-side
code, so expires_at is null.

The TTL config is optional and threaded like enabledMethods is today; when
absent, the §9.5 defaults apply. A stored TTL outside the bounds throws - it is
not clamped, and a malformed value never silently falls back to the default.
Reading the config from a tenant settings table is out of scope (issue filed) -
there is no home for it in the schema yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRhu5TK6KW3YyXFR8yDDJ1
When claim.submit resolves the claimant to a freshly minted users row, core now
emits user.created (§4.3) ahead of claim.submitted, in the same transaction. A
reused row emits nothing.

upsertClaimant keeps ON CONFLICT DO UPDATE (a no-op email write) and reads
(xmax = 0) AS inserted to tell a fresh INSERT from a conflict. DO UPDATE blocks
on a concurrent uncommitted insert of the same key and then returns the
surviving row; DO NOTHING + a follow-up SELECT would race - the other
transaction's row is not yet visible - and could mint two users.

writeOutboxEvents gains a keyIndex (default 0) so the idempotency key and the
returned event id stay on claim.submitted even though user.created is written
first. A multi-event command still has exactly one non-null idempotency_key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRhu5TK6KW3YyXFR8yDDJ1
@OriginDevIT
OriginDevIT merged commit 1f3a50a into main Sep 1, 2026
1 check passed
@OriginDevIT
OriginDevIT deleted the feat/user-namespace-and-verification-ttl branch September 1, 2026 01:50
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.

user.created is specified but not emitted or typed claim.submit relays verification_expires_at instead of computing it

1 participant