Skip to content

[finding] CronExpressionInputSchema / TemplateExpressionInputSchema fix the dialect only on the bare-string arm — the envelope arm accepts any declared dialect, so a cron-typed slot parses { dialect: 'cel', source } green #15028

Description

@claude

Recorded by the os-dev seat on #14825 (session session_0174WZTU6XcFcS7g2kykC53i, branch claude/issue-14825-knowledge-source-cron-schema) while measuring the refusal shape of the shared cron dialect. Observation class (finding), unassigned; out of #14825's scope — it is a property of shared/expression.zod.ts shared by every typed site.

What

packages/spec/src/shared/expression.zod.ts at origin/main 6392b9c2:

  • :110-113CronExpressionInputSchema = z.union([ z.string().min(1).transform(source => ({ dialect: 'cron', source })), ExpressionSchema ])
  • :120-123TemplateExpressionInputSchema, the same shape with dialect: 'template' on the string arm.
  • ExpressionSchema (:73-90) accepts dialect: 'cel' | 'cron' | 'template'.

So the "typed" schemas differ from ExpressionInputSchema only in what a BARE STRING becomes. The envelope arm is the unrestricted ExpressionSchema. Measured on zod 4.4.3 through z.object({ cron: CronExpressionInputSchema.optional() }):

Why it might matter

The docblock of CronExpressionInputSchema says "Use this for schedule / cronExpression fields". A scheduler that receives a cel envelope from a cron-typed slot has nothing to schedule; the failure surfaces at the consumer, which is the declared-but-unenforced shape ADR-0049 names. The same holds for a cron envelope in a prompt-template slot.

Deliberate or not is the open question: ExpressionInputSchema's docblock says "Engines that need other dialects must use the full envelope", which reads as intent for the UNTYPED schema and says nothing about the typed ones.

Likely resolution, not a decision

Either refine the envelope arm per typed schema (ExpressionSchema narrowed to dialect: z.literal('cron') / z.literal('template')) — an accept-set narrowing across the 8 cron and 3 template sites (needs:contract-review shape, Clause ②), with a migration measurement first — or state in each typed schema's describe that the envelope arm accepts any declared dialect, so declared = enforced the other way round. The spec lane's call.

Verified

Refs: #14825 · ADR-0049 · ADR-0058.

Generated by Claude Code


Generated by Claude Code

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