Skip to content

[finding] The shared cron dialect judges no cron syntax at parse time — after #14825 all 9 CronExpressionInputSchema sites accept 'not a cron' green; the verdict is the formula cron-engine's at evaluate time #15035

Description

@zhuangjianguo

Recorded by the domain:spec seat (session_0174WZTU6XcFcS7g2kykC53i, seat post #6017) during the contract review of PR #15029 (#14825). Bare, for triage; the landing file is packages/spec/src/shared/expression.zod.ts (spec lane) if anything lands at all.

What

packages/spec/src/shared/expression.zod.ts lines 110-113 on origin/main a4e4d2d7 (read 2026-09-03T18:00Z):

export const CronExpressionInputSchema = z.union([
  z.string().min(1).transform((source): Expression => ({ dialect: 'cron', source })),
  ExpressionSchema,
]);

The only parse-time refusals are an empty string, a non-string, and an envelope naming an undeclared dialect. Measured by the #14825 dev (M2, zod 4.4.3, through z.object({ cron: CronExpressionInputSchema.optional() })): '0 9 * * 1-5', the 6-field '0 0 9 * * *' and 'not a cron' all normalize to { dialect: 'cron', source }. No cron parser exists in the spec layer; the syntax verdict (5- or 6-field, or an @yearly@reboot alias) belongs to the @objectstack/formula cron-engine when the expression is evaluated.

Sites carrying the schema: 8 positions in 7 files before #14825 (api/export.zod.ts, automation/execution.zod.ts, integration/connector.zod.ts, system/cache, system/disaster-recovery twice, system/job, per the #14825 dev's count) plus ai/knowledge-source.zod.ts refresh.cron once PR #15029 lands — 9 in total.

Why it matters

#14825's headline symptom — "an AI author writes a cron, os validate accepts anything, and the failure surfaces at the scheduler" — is closed for that one field by making its describe promise exactly what the parse enforces, not by refusing bad syntax. The symptom stays true at every cron-typed site, by the shared dialect's design. PR #15029's pin file deliberately asserts the non-refusal so that a later change to the shared dialect surfaces there; this card is where that change, if any, gets decided.

Not a decision — three shapes for triage

Verified

Refs: #14825 · PR #15029 · #15028 · #15027 · ADR-0049 · ADR-0058.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions