Skip to content

Validation events: ['delete'] is a silent no-op — trim it from the spec #3184

Description

@os-zhuang

Summary

BaseValidationSchema admits events: ['insert', 'update', 'delete'] (packages/spec/src/data/validation.zod.ts:88), but the rule evaluator's runtime Mode is 'insert' | 'update' (packages/objectql/src/validation/rule-validator.ts:72) and engine.delete never invokes evaluateValidationRules. The events filter (rule-validator.ts:526) can therefore never match 'delete' — a validation rule declaring events: ['delete'] silently never runs.

This is the declared ≠ enforced tail split out of #3106 / PR #3160 (which fixed the multi-row-update call-site gap and left this one explicitly for a follow-up). It was also independently flagged by the June liveness audit.

Evidence

  • packages/spec/src/data/validation.zod.ts:88events: z.array(z.enum(['insert', 'update', 'delete'])).default(['insert', 'update']).
  • packages/objectql/src/validation/rule-validator.ts:72type Mode = 'insert' | 'update'; :526const events = r.events ?? ['insert', 'update']; return events.includes(mode); (mode is never 'delete').
  • docs/audits/2026-06-validationschema-property-liveness.md:10-21 — "🔴 events: ['delete'] is a silent no-op … Remove delete from the events enum (dead)."
  • The published skill skills/objectstack-data/rules/validation.md:268 actively teaches events: ['delete'] ("Only on delete"), so AI-authored metadata following the skill gets a delete guard that never fires.

Blast radius

Zero in practice. No example app, seed, template, fixture, or test declares a validation rule with 'delete' in events — the only repo-wide occurrences are documentation/skill text. Any hypothetical off-spec metadata fails loudly (ZodError at registry.validate / os validate), never silently.

Decision: trim (not enforce)

Remove 'delete' from the events enum (contract-first, AGENTS.md PD #12). Delete-time guards belong in beforeDelete lifecycle hooks, which the skills already teach correctly (skills/objectstack-data/references/data-hooks.md:448-464). Enforcement (wiring the evaluator into engine.delete) was considered and rejected: only script/cross_field would be meaningful on delete, and hooks already cover that need.

Scope

  • Narrow the enum + its three hand-written mirrors (validation.zod.ts:226 type, rule-validator.ts:79 BaseRule, packages/metadata-protocol/src/protocol.ts:231-235 JSON-schema mirror).
  • Fix the skill line and the hand-written doc table (content/docs/data-modeling/validation.mdx:72); regenerate content/docs/references/data/validation.mdx.
  • Correct the doc comments that framed the delete gap as a known exclusion.
  • Changeset + a negative test asserting events: ['delete'] is now rejected.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions