You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[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
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/main6392b9c2:
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() }):
{ dialect: 'cel', source: 'record.x' } — parses green, passed through unchanged, on a cron slot.
{ dialect: 'template', source: '{{x}}' } — parses green on a cron slot.
{ dialect: 'js', source: 'x' } — refused (invalid_union, the enum refusal), as expected.
'' — refused (invalid_union; the string arm's too_small).
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.
Recorded by the os-dev seat on #14825 (session
session_0174WZTU6XcFcS7g2kykC53i, branchclaude/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 ofshared/expression.zod.tsshared by every typed site.What
packages/spec/src/shared/expression.zod.tsatorigin/main6392b9c2::110-113—CronExpressionInputSchema = z.union([ z.string().min(1).transform(source => ({ dialect: 'cron', source })), ExpressionSchema ]):120-123—TemplateExpressionInputSchema, the same shape withdialect: 'template'on the string arm.ExpressionSchema(:73-90) acceptsdialect: 'cel' | 'cron' | 'template'.So the "typed" schemas differ from
ExpressionInputSchemaonly in what a BARE STRING becomes. The envelope arm is the unrestrictedExpressionSchema. Measured on zod 4.4.3 throughz.object({ cron: CronExpressionInputSchema.optional() }):{ dialect: 'cel', source: 'record.x' }— parses green, passed through unchanged, on a cron slot.{ dialect: 'template', source: '{{x}}' }— parses green on a cron slot.{ dialect: 'js', source: 'x' }— refused (invalid_union, the enum refusal), as expected.''— refused (invalid_union; the string arm'stoo_small).'not a cron'— parses green to{ dialect: 'cron', source: 'not a cron' }(cron syntax is the formula cron-engine's verdict at evaluate time, not the parse's — expected, and [finding]KnowledgeSourceSchema.cronis documented as a 5-field cron expression but typedz.string()— the spec's ownCronExpressionInputSchemais not used, so'not a cron'parses green #14825's describe says so).Why it might matter
The docblock of
CronExpressionInputSchemasays "Use this forschedule/cronExpressionfields". A scheduler that receives acelenvelope 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 acronenvelope 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 (
ExpressionSchemanarrowed todialect: z.literal('cron')/z.literal('template')) — an accept-set narrowing across the 8 cron and 3 template sites (needs:contract-reviewshape, 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
search_issueswith the [finding]KnowledgeSourceSchema.cronis documented as a 5-field cron expression but typedz.string()— the spec's ownCronExpressionInputSchemais not used, so'not a cron'parses green #14825 control hitting): nothing on the envelope arm's dialect.git grep -n CronExpressionInputSchema packages/spec/src— 8 positions in 7 files (api/export.zod.ts:576/706,automation/execution.zod.ts:440,integration/connector.zod.ts:257,system/cache.zod.ts:156,system/disaster-recovery.zod.ts:57/238,system/job.zod.ts:15);TemplateExpressionInputSchema— 3 (ai/model-registry.zod.ts:121/122,data/object.zod.ts:2083).Refs: #14825 · ADR-0049 · ADR-0058.
Generated by Claude Code
Generated by Claude Code