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
spec/ai: KnowledgeRefreshPolicy.cron's docblock, shipped describe and test comments attribute the cron syntax verdict to @objectstack/formula's cron-engine "when the expression is evaluated" — nothing evaluates that slot, and that engine has no caller outside formula #15867
Recorded by the os-dev seat on #15527 (session session_01M59rPZZFzqhfMUPFqqZTkf, branch claude/issue-15527-cron-dialect-row-names-croner) while rewriting the Expression Protocol dialect table. Out of #15527's scope: that card's file surface is the docblock rows of packages/spec/src/shared/expression.zod.ts and the page generated from it. Filed as a sub-issue of #15035 because #15035 is where "where does the cron syntax verdict live" gets decided, and this describe has to say whatever that answer turns out to be. Unassigned, no labels, for triage.
What
packages/spec/src/ai/knowledge-source.zod.ts on origin/main59953d5a3 (2026-09-05):
:35-38 (docblock): "It does NOT judge cron syntax: that verdict is the cron dialect engine's (@objectstack/formula cron-engine — 5- or 6-field, or an @yearly…@reboot alias) when the expression is evaluated"
:45-49 (the .describe() on cron): "cron syntax (5- or 6-field, or an @ alias) is the cron dialect engine's verdict when the expression is evaluated, not checked here."
packages/spec/src/ai/knowledge-source.test.ts:17-20 and :96-98 repeat the same attribution in comments.
Measured (the same walk as #15527; details in #15035 comment 5551551797):
cronEngine (packages/formula/src/cron-engine.ts:63) is registered only in formula's own registry (packages/formula/src/registry.ts:43). git grep -n -E 'cronEngine|cron-engine' -- . ':!packages/formula' returns prose only: this file, a CHANGELOG line, and the D7 ledger notes. No non-test source outside spec/formula constructs or evaluates a dialect: 'cron' envelope, and packages/lint/src/validate-expressions.ts:369 skips every non-cel dialect.
refresh.cron itself is evaluated by nothing: service-knowledge reads refresh.onRecordChange and never refresh.cron (the D7 ledger row cron-knowledge-refresh records this, and the describe's own second half says the value is only surfaced for an external scheduler).
So "when the expression is evaluated" names an event that does not occur, and names the wrong evaluator if it did — the only evaluator any spec cron slot meets is croner, on CronSchedule.expression, not this slot.
Why it matters
The .describe() is customer-facing: it is the cron row of content/docs/references/ai/knowledge-source.mdx (:68 and :133 on the same commit). An author is told which engine will judge their syntax and when; both halves are wrong — the same shape #15527 corrected in the dialect table. The first half of the describe ("not checked here": a non-empty string or an envelope, normalized to the envelope) is accurate and should stay.
Likely resolution, not a decision
Rewrite the attribution in the docblock, the describe and the two test comments to what is true today — no engine evaluates refresh.cron; syntax is judged by whatever external scheduler the author hands the value to — then pnpm --filter @objectstack/spec gen:docs and a @objectstack/spec patch changeset. If #15035 lands a parse-time or lint-time verdict, the same sentence changes again, which is why this sits under that card. The pin test's behaviour (the parse does not refuse 'not a cron') is correct as it stands; only its comments are wrong.
Recorded by the os-dev seat on #15527 (session
session_01M59rPZZFzqhfMUPFqqZTkf, branchclaude/issue-15527-cron-dialect-row-names-croner) while rewriting the Expression Protocol dialect table. Out of #15527's scope: that card's file surface is the docblock rows ofpackages/spec/src/shared/expression.zod.tsand the page generated from it. Filed as a sub-issue of #15035 because #15035 is where "where does the cron syntax verdict live" gets decided, and this describe has to say whatever that answer turns out to be. Unassigned, no labels, for triage.What
packages/spec/src/ai/knowledge-source.zod.tsonorigin/main59953d5a3(2026-09-05)::35-38(docblock): "It does NOT judge cron syntax: that verdict is thecrondialect engine's (@objectstack/formulacron-engine — 5- or 6-field, or an@yearly…@rebootalias) when the expression is evaluated":45-49(the.describe()oncron): "cron syntax (5- or 6-field, or an@alias) is thecrondialect engine's verdict when the expression is evaluated, not checked here."packages/spec/src/ai/knowledge-source.test.ts:17-20and:96-98repeat the same attribution in comments.Measured (the same walk as #15527; details in #15035 comment 5551551797):
cronEngine(packages/formula/src/cron-engine.ts:63) is registered only in formula's own registry (packages/formula/src/registry.ts:43).git grep -n -E 'cronEngine|cron-engine' -- . ':!packages/formula'returns prose only: this file, a CHANGELOG line, and the D7 ledger notes. No non-test source outside spec/formula constructs or evaluates adialect: 'cron'envelope, andpackages/lint/src/validate-expressions.ts:369skips every non-celdialect.refresh.cronitself is evaluated by nothing:service-knowledgereadsrefresh.onRecordChangeand neverrefresh.cron(the D7 ledger rowcron-knowledge-refreshrecords this, and the describe's own second half says the value is only surfaced for an external scheduler).So "when the expression is evaluated" names an event that does not occur, and names the wrong evaluator if it did — the only evaluator any spec cron slot meets is
croner, onCronSchedule.expression, not this slot.Why it matters
The
.describe()is customer-facing: it is thecronrow ofcontent/docs/references/ai/knowledge-source.mdx(:68and:133on the same commit). An author is told which engine will judge their syntax and when; both halves are wrong — the same shape #15527 corrected in the dialect table. The first half of the describe ("not checked here": a non-empty string or an envelope, normalized to the envelope) is accurate and should stay.Likely resolution, not a decision
Rewrite the attribution in the docblock, the describe and the two test comments to what is true today — no engine evaluates
refresh.cron; syntax is judged by whatever external scheduler the author hands the value to — thenpnpm --filter @objectstack/spec gen:docsand a@objectstack/specpatch changeset. If #15035 lands a parse-time or lint-time verdict, the same sentence changes again, which is why this sits under that card. The pin test's behaviour (the parse does not refuse'not a cron') is correct as it stands; only its comments are wrong.Verified
issues?state=open&since=2026-09-04, 5 pages, 394 open issues, grepped locally for knowledge-source / KnowledgeRefresh / refresh.cron; controls [finding] The Expression Protocol dialect table tells authors thecronengine iscron-parser— that package is not a dependency of this repo at all; the library iscroner, and the table ships to customers via generated docs #15527, [finding] The shared cron dialect judges no cron syntax at parse time — after #14825 all 9CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035 and [finding]CronExpressionInputSchema/TemplateExpressionInputSchemafix 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 all present in the read): the only hits are [finding] The shared cron dialect judges no cron syntax at parse time — after #14825 all 9CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035 and [finding]CronExpressionInputSchema/TemplateExpressionInputSchemafix 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 themselves, and neither names this file's describe. [finding] The Expression Protocol dialect table tells authors thecronengine iscron-parser— that package is not a dependency of this repo at all; the library iscroner, and the table ships to customers via generated docs #15527's own dedup of 2026-09-04 (search_issues, ADR-0058 D7 expression conformance ledger discovers onlyExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027/[finding]CronExpressionInputSchema/TemplateExpressionInputSchemafix 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 control) stands for the earlier window.packages/formulareturns the definition, the registry registration and the index export.Refs: #15527 · #15035 · #15028 · #14825 · ADR-0049 · ADR-0058.