Skip to content

Object-level workflows: [...] (and any unknown ObjectSchema key) is silently stripped at build — no error/warning (ADR-0032 'no silent failure', metadata layer) #1535

@xuyushun441-sys

Description

@xuyushun441-sys

Summary

Object-level workflows: [...] authored on an ObjectSchema is silently dropped at build — no error, no warning, and tsc --noEmit passes. The result: declarative on-update automation that authors believe they shipped never runs, and nothing anywhere tells them.

This surfaced in objectstack-ai/templates: 15 object-level workflows across 8 objects / 6 packages (timestamp stamping, contract auto-activate/expire, order auto-receive, etc.) were authored, built green, and were completely absent from every built artifact — dead from day one. We have since migrated them to lifecycle hooks (templates-side fix), but the silent-drop behavior is the platform problem.

This is the metadata-key analogue of ADR-0032's "no silent failure" principle: today a misauthored expression is caught at build, but a misauthored schema key is silently discarded.

Root cause

ObjectSchemaBase is a plain z.object({...}) (Zod default = .strip()), and ObjectSchema.create() parses through it:

  • packages/spec/src/data/object.zod.ts:334const ObjectSchemaBase = z.object({ ... })
  • :524validations: z.array(ValidationRuleSchema).optional() ✅ (supported)
  • No workflows key anywhere in ObjectSchemaBase (grep for workflow in packages/spec/src/data/ returns only unrelated hits).
  • :740create: <const T extends z.input<typeof ObjectSchemaBase>>(config) => ObjectSchemaBase.parse(withDefaults) → unknown keys are stripped on parse.

So any unknown top-level key (workflows, or a typo'd validation/indexs/etc.) is silently removed. The generic create() signature also did not trigger a TypeScript excess-property error for the extra workflows key (the templates typechecked clean with it present).

Reproduction

import { ObjectSchema } from '@objectstack/spec/data';

const Obj = ObjectSchema.create({
  name: 'demo',
  fields: { status: { type: 'text' } },
  workflows: [{                       // ← not a real ObjectSchema field
    name: 'stamp', objectName: 'demo', triggerType: 'on_update',
    criteria: 'record.status == "done"', active: true,
    actions: [{ name: 'x', type: 'field_update', field: 'done_at', value: 'now()' }],
  }],
});

console.log('workflows' in Obj);   // → false  (silently stripped; no error, no warning)

objectstack build on a package full of these prints ✓ Build complete with no diagnostic; the composed artifact contains 0 object-workflows.

Why this is bad

  1. Silent no-op automation. Exactly the failure mode ADR-0032 set out to eliminate ("a malformed thing must come back to the author as a precise, fixable error, never a runtime no-op") — but for schema shape rather than expressions. An AI author (the ADR's design center) gets zero signal and ships dead metadata with full confidence.
  2. The platform's own guidance teaches the dropped shape. The objectstack-data skill's "CRM Schema Blueprint" table lists:

    Lifecycle workflow | src/objects/*.object.ts | Use workflows[] for field updates triggered by record changes
    So authors (and agents) are actively told to write workflows[] on objects — a field the spec doesn't accept.

Asks

  1. Don't strip silently. Reject unknown top-level keys on ObjectSchema.create() / at objectstack build (or at minimum emit a located build warning). Mirrors ADR-0032 §1c "no silent fallback", applied to metadata shape. A typo'd validation/hoooks/workflows should be a fixable build error, not a vanished field.
  2. Decide the object-workflow story. Either (a) make object-level declarative on-update automation a first-class ObjectSchema field, or (b) if lifecycle hooks and top-level record_change flows are the only supported paths, say so — and have the build flag workflows explicitly pointing authors at the supported mechanism.
  3. Fix the docs/skill. Remove/repair the objectstack-data CRM-blueprint row that teaches workflows[].

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions