Skip to content

fix(spec): collapse hook event taxonomy 18 → 8; findOne fires find hooks#3206

Merged
os-zhuang merged 1 commit into
mainfrom
claude/validation-multi-row-updates-kqxgg4
Jul 18, 2026
Merged

fix(spec): collapse hook event taxonomy 18 → 8; findOne fires find hooks#3206
os-zhuang merged 1 commit into
mainfrom
claude/validation-multi-row-updates-kqxgg4

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Closes #3195. The HookEvent enum declared 18 events but the engine only ever dispatches 8. The 10 dead ones (beforeFindOne/afterFindOne, beforeCount/afterCount, beforeAggregate/afterAggregate, beforeUpdateMany/afterUpdateMany, beforeDeleteMany/afterDeleteMany) mirrored the engine method table rather than domain events, so a hook subscribing to them registered fine and then silently never fired — the same declared ≠ enforced failure mode as the validation events: ['delete'] gap (#3184). The skills even shipped a copy-pasteable afterFindOne masking example that no-ops.

Design decision (from the issue's review): collapse the taxonomy, don't build out dispatch. Peer platforms don't expose per-method read triggers or a single/bulk split — Salesforce/Postgres/Rails treat reads as one materialization event and writes as bulk-first. The 18-value enum copied Mongoose's per-method shape (its most notorious footgun); the dead events' jobs already have correct homes (read authz → RLS/permissions, masking → field metadata, bulk → the singular write events).

Changes

  • packages/spec/src/data/hook.zod.tsHookEvent narrowed to 8 (beforeFind/afterFind + insert/update/delete pairs); HookContext.input doc formalizes that bulk multi:true writes fire the singular events with the predicate in input.ast.
  • packages/objectql/src/engine.tsfindOne now fires beforeFind/afterFind (mirrors find(); one read event covers both). registerHook warns when a hook subscribes to a non-dispatched event (a DISPATCHABLE_HOOK_EVENTS set is the single source of truth, kept in lockstep with the triggerHooks call sites).
  • Skills (objectstack-data/rules/hooks.md, references/data-hooks.md) — teach 8 events + a responsibility table; fixed the double-subscribe ['afterFind','afterFindOne'] masking example to a single afterFind and pointed static masking at field metadata.
  • Docs (kernel/events.mdx, api/data-flow.mdx, protocol/objectql/schema.mdx) — corrected; regenerated content/docs/references/data/hook.mdx.
  • .changeset/ — patch (spec + objectql).

Behavior change

  • findOne now runs beforeFind/afterFind hooks (previously it ran none). Fail-closed direction: a read hook that authors expected to fire on single-record reads now does. No existing hook is negatively affected.
  • Authoring a hook on a removed event now fails at parse/validate time instead of registering a dead hook. Verified zero shipped hooks or authored metadata used any removed event; the only repo-wide references were docs/skills text (all updated).

Tests

  • engine.test.tsfindOne fires beforeFind+afterFind in order; an afterFind hook can transform the findOne result; registerHook warns for a non-dispatched event and stays quiet for a dispatchable one.
  • hook.test.ts — enum accepts the 8, rejects all 10 removed values.

Verified: spec 253 files / 6774 tests green, objectql 71 files / 944 tests green, check:docs 253 files in sync, spec+objectql builds green, eslint clean. No source consumer references the removed event names.

Lineage

Third fix in the PD #10 events-enum audit line: #3160 (validation on multi-row updates) → #3189 (trim validation delete) → this. Sibling issues still open: #3196 (webhook undelete/api), #3197 (schema-only event surfaces).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VCUSMJBsX14C3RQdWFw7N7


Generated by Claude Code

…oks (#3195)

The HookEvent enum declared 18 events but the engine only ever dispatches 8.
The 10 dead ones (beforeFindOne/afterFindOne, beforeCount/afterCount,
beforeAggregate/afterAggregate, beforeUpdateMany/afterUpdateMany,
beforeDeleteMany/afterDeleteMany) mirrored the engine method table rather than
domain events, so a hook subscribing to them registered fine and silently
never fired — the same declared≠enforced failure mode as the validation
delete-event gap (#3184), and the skills even shipped a copy-pasteable
afterFindOne masking example that no-ops.

Design decision (see issue): collapse the taxonomy, don't build out dispatch.
Peer platforms (Salesforce, Postgres, Rails) don't expose per-method read
triggers or single/bulk splits — reads are one materialization event, writes
are bulk-first. So:

- findOne now fires the SAME beforeFind/afterFind hooks as find (the read
  event attaches to record materialization, not the method) — one subscription
  covers every read shape.
- Bulk (multi:true) writes already fire the singular before/after write events
  with the row-scoping predicate in ctx.input.ast; documented, no *Many event.
- Removed the 10 dead enum values. Read authz/row-filtering is the RLS/
  permission layer; field masking is field metadata — not per-author hooks.
- engine.registerHook warns when a hook subscribes to a non-dispatched event,
  so enum-vs-dispatch drift can't recur silently.

No shipped hook or authored metadata used any removed event. Skills and docs
(hooks.md, data-hooks.md, kernel/events.mdx, api/data-flow.mdx,
protocol/objectql/schema.mdx) updated to teach 8 events + the declarative
alternatives; regenerated content/docs/references/data/hook.mdx. Added tests
for findOne firing find hooks and the registration guard.

Closes #3195

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCUSMJBsX14C3RQdWFw7N7
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 18, 2026 11:31am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jul 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/objectql, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 18, 2026 11:38
@os-zhuang
os-zhuang merged commit a3823b2 into main Jul 18, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/validation-multi-row-updates-kqxgg4 branch July 18, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hook event taxonomy is over-specified: collapse 18 events → 8, make findOne fire find hooks, formalize bulk semantics

2 participants