Skip to content

fix(core): polish batch — owner entityId, timezone, date validation, null content filter (#69)#103

Merged
cuibonobo merged 1 commit into
mainfrom
claude/issue-69-design-review
Jul 18, 2026
Merged

fix(core): polish batch — owner entityId, timezone, date validation, null content filter (#69)#103
cuibonobo merged 1 commit into
mainfrom
claude/issue-69-design-review

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

Closes #69 — four small correctness/consistency fixes from the design-review backlog.

  • Owner entityId stamping via ScopedStack. The spec says owner-created records carry no entityId, but ScopedStack.create() stamped it unconditionally — so the same owner writing through asEntity(ownerEntityId) (any server path) produced records shaped differently from writing directly through Stack.create(). Fixed by omitting entityId when the requester is the owner. Added a test pinning that a stranger's -own grant still can't match an owner-authored record now that it carries no entityId (it can't, since the owner already bypasses grants entirely via checkAccess's owner shortcut).
  • timezone required at init, consumed by nothing. Made optional end to end — ConfigContent.timezone, StackRecordAdapter.timezone, the _config@1 schema, every adapter's init options, and discovery's timezone field are now string | undefined with no default. Previously readStackConfig/APIAdapter.open() defaulted a missing value to 'UTC', which asserts knowledge the stack was never actually given.
  • date fields validated with bare Date.parse. Date.parse accepts engine-dependent non-ISO formats ("March 1 2020", "3/1/2020") that the "Expected ISO 8601 date string" error message already claimed to reject. Now validated against an ISO 8601 shape (regex) with Date.parse running afterward only as a calendar sanity check (e.g. rejects month 13).
  • content: { x: null } filter silently matched nothing. SQL = NULL and JS === against a possibly-absent key are both never-true for a missing field, so a null content filter silently returned zero records — a caller writing "field absent" got an empty result with no signal. Fixed to mean "field absent or stored as null" (IS NULL / missing-path semantics) in the shared SQL query builder (record-adapter-sqlite + record-adapter-sqljs) and in MemoryAdapter.

Test plan

  • pnpm -r build — all packages build
  • pnpm -r typecheck — clean
  • pnpm -r lint — clean
  • pnpm -r test — full suite green across every package
  • New/updated tests: owner-via-asEntity entityId omission + -own grant non-regression, timezone-undefined-by-default (adapter-api and core), ISO 8601 shape rejection of previously-accepted lenient formats, null-content-filter matching for both absent and explicit-null fields (sqlite, sqljs, and MemoryAdapter)
  • docs/spec.md updated to document all four behavior changes

Generated by Claude Code

…null content filter (#69)

- ScopedStack.create() omits entityId when the requester is the owner,
  matching Stack.create() — previously the owner writing through
  asEntity(ownerEntityId) produced a differently-shaped record than
  writing directly, contradicting the spec's "owner records carry no
  entityId" invariant.
- timezone becomes optional passthrough app metadata end to end
  (ConfigContent, StackRecordAdapter, discovery, every adapter's init
  options) — no more defaulting to 'UTC', which claimed knowledge the
  stack was never actually given.
- date-kind fields now validate against an ISO 8601 shape (regex +
  Date.parse sanity check) instead of bare Date.parse, which accepted
  engine-dependent non-ISO formats the error message already claimed
  to reject.
- A content filter value of null now means "field absent or null"
  (IS NULL / missing-path semantics) instead of silently matching
  nothing, in both the shared SQL query builder and MemoryAdapter.

Spec and tests updated to match.
@cuibonobo
cuibonobo merged commit f0e75b4 into main Jul 18, 2026
5 checks passed
@cuibonobo
cuibonobo deleted the claude/issue-69-design-review branch July 18, 2026 13:18
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.

Polish batch: owner entityId stamping, unused timezone, lenient date validation, silent null content filter

2 participants