docs(spec): the dialect table's cron row names what fires a schedule — cron-parser is not in the product - #15877
Merged
os-project-manager merged 4 commits intoSep 5, 2026
Conversation
… cron-parser The `## Dialects` table in the Expression Protocol docblock named `cron-parser` as the cron engine. That package is not a dependency of any package in the repo; the library that fires a schedule is `croner`, reached only from `CronSchedule.expression` via `toBoundaryJobSchedule` and `CronJobAdapter`. No cron syntax is judged at parse time, the other cron-typed slots reach no engine, and formula's registered cron engine has no caller outside its package. The row and one sentence under the table now say so; the generated reference page follows in the regeneration commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
…ed dialect table Produced by `pnpm --filter @objectstack/spec gen:docs`; the only file the generator changed. The page's cron row and the sentence under the table now match the docblock. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
…on-dialect-row-names-croner
…on-dialect-row-names-croner
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 129 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 5, 2026
os-project-manager
marked this pull request as ready for review
September 5, 2026 12:41
os-project-manager
enabled auto-merge
September 5, 2026 12:41
os-project-manager
deleted the
claude/issue-15527-cron-dialect-row-names-croner
branch
September 5, 2026 13:10
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15527
What
The
## Dialectstable in the# Expression Protocoldocblock ofpackages/spec/src/shared/expression.zod.tstold authors thecronengine iscron-parser. That package is not a dependency of any package in this repo (grep -rn '"cron-parser"' --include=package.json,node_modulesexcluded: 0 hits;pnpm-lock.yaml: 0 hits; positive control"croner": 3 hits —services/service-job,triggers/trigger-schedule,plugins/plugin-reports, all^10.0.1). The row shipped to customers ascontent/docs/references/shared/expression.mdx:24.Not a find-and-replace (triage
5547891192): the column is headed engine, and for six of the seven cron ratchet keys there is no engine on either side. The row now says who fires and that no parse-time verdict exists, and one sentence under the table carries what a cell cannot.Before (
:22):croncron-parserAfter (
:22, plus the paragraph at:25-29):croncronerfires it at schedule time, on the one wired slot"No cron syntax is judged at parse time:
cronerevaluates a cron slot only whenCronSchedule.expressionis scheduled (toBoundaryJobSchedule→CronJobAdapter, where an invalid pattern is refused); every other cron-typed slot is parsed and reaches no engine, and@objectstack/formula's registeredcronengine has no caller outside that package."No other row changed; no schema, enum, accept set or behaviour moves (Clause-②: no). The
celandtemplaterows are untouched.Files
packages/spec/src/shared/expression.zod.ts— docblock rows:19-29only.content/docs/references/shared/expression.mdx— regenerated bypnpm --filter @objectstack/spec gen:docs(never hand-edited); the generator changed no other file (git statusafter the run: this one path)..changeset/cron-dialect-row-names-croner.md—@objectstack/specpatch.H1 — the facts the row rests on (measured on
origin/main89758ac82, re-checked at59953d5a3)Every
CronExpressionInputSchemaposition in the spec — 9 positions, 7 distinctfile:fieldratchet keys:system/job.zod.ts:expression(CronSchedule.expression)system/job.zod.ts:15croner—runtime/src/job-schedule.ts:35toBoundaryJobSchedule(called fromruntime/src/app-plugin.ts:1050) lowers the envelope to a bare string and checks dialect/source shape only; thenservices/service-job/src/cron-job-adapter.ts:130new Cron(schedule.expression, …)(import { Cron, scheduledJobs } from 'croner'at:3); an invalid pattern throws thereapi/export.zod.ts:cronExpression:576,:706ScheduledExportSchema/ScheduleExportRequestSchemahave no consumer outsidepackages/specautomation/execution.zod.ts:cronExpression:454ScheduleStateSchemahas no consumer outsidepackages/specintegration/connector.zod.ts:schedule:257SyncConfigSchemahit outside spec is turso's ownTursoSyncConfigSchemasystem/cache.zod.ts:schedule:156CacheWarmupSchemahas no consumer outsidepackages/specsystem/disaster-recovery.zod.ts:schedule:57,:238BackupConfigSchema/ the DRtestingblock have no consumer outsidepackages/specai/knowledge-source.zod.ts:cron:45service-knowledgereadsrefresh.onRecordChangeonly; the value is surfaced for an external scheduler (D7 ledger rowcron-knowledge-refresh)CronExpressionInputSchema(:110-113) isz.string().min(1)transformed to the envelope, orExpressionSchema; nothing reads the pattern.packages/lint/src/validate-expressions.ts:369skips every non-celdialect, so there is no lint-time verdict either.@objectstack/formula'scronEngine(packages/formula/src/cron-engine.ts:63) is registered atpackages/formula/src/registry.ts:43and referenced nowhere outsidepackages/formula:git grep -n -E 'cronEngine|cron-engine' -- . ':!packages/formula'returns prose only — a CHANGELOG line, the D7 ledger notes,ai/knowledge-source.zod.ts:37(a docblock that names it and never calls it) and that file's test comments. No non-test source outside spec/formula constructs or evaluates adialect: 'cron'envelope.croner10.0.1 behaviour, measured frompackages/services/service-job:'0 0 * * *','0 0 9 * * *'and'@yearly'accepted;'not a cron'and''throwCronPattern: invalid configuration format (...), exactly five, six, or seven space separated parts are required.cronerusers read plain strings, not a dialect slot:triggers/trigger-schedule/src/schedule-trigger.ts:103normalizeSchedule(a flow start node's schedule config) andplugins/plugin-reports/src/report-service.ts:609/819(input.cronExpression). The row's "one wired slot" is a statement about dialect slots.Sequencing with the two open cards
CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035 — that card's sentence "the syntax verdict belongs to the@objectstack/formulacron-engine when the expression is evaluated" is contradicted by the measurement above; what the row now says, and which fact it rests on, is stated there: [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 (comment). [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 stays open, unrelabelled, scope untouched.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 — its'not a cron'bullet repeats the same attribution; one-line note: [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 (comment). The row relies on the envelope arm being unrestricted, exactly as that card describes it. [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 stays open.ai/knowledge-source.zod.ts's docblock, shipped describe and test comments is outside this card's file surface: filed as 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, a sub-issue of [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. 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 is not addressed here.Pins
packages/spec/src/shared/expression-dialect-docs.pin.test.tsreads the table's first column againstExpressionDialect— 4/4 green on73a2adffb; its own docblock says wording is free, and no test inpackages/**/*.test.tspins the old wording (git grep cron-parser: only the D7 ledger comment atexpression-conformance.ledger.ts:280, which already says "croner, not cron-parser").check:docsgreen after regeneration;check:doc-authoringandcheck:doc-formula-expressionsgreen.Verification (final head
73a2adffb; every exit code captured before any pipe)pnpm --filter @objectstack/spec buildunder the verify lock (spec has no workspace deps; exit 0,check-dts-emitted34/34);formula,sdui-parserandlintbuilt to satisfy the lint-side gates' prerequisites.@objectstack/specsuite at236f6da97(before the secondorigin/mainmerge): 473 files / 12723 tests passed;typecheckgreen (tsc --noEmit,check:scripts-typecheck,check:test-typecheckOK). On73a2adffb, after merging 25 incoming commits that touchpackages/spec/src/dataandsrc/automationbut none of this PR's files: the pin test 4/4 (the only test file that importsshared/expression.zod). Declared narrowing: the full suite is not re-run on the merged head; CI's Test Core and the merge queue run it on the merged generation.node scripts/pm/dispatch-gates.mjs --commands(no paths) at73a2adffb: 89 families; 87 green; 2 NOT MEASURED, bothPREREQUISITE NOT METon unbuilt dists this card does not build (check:skill-exampleswantspackages/client-react/distdeclarations;check:dual-build-cjs-loadswants every package's dist) — declared to CI. The family the merged head added,pnpm check:dts-closure, run: exit 0 (40/40 declarations across the 4 built packages).check:generatedgreen (no stale artifact).pnpm check:nul-bytesgreen.pnpm exec eslint --no-inline-config --format jsonover the three changed files at73a2adffb— 3 files in the JSON;expression.zod.ts0 errors / 0 warnings; the.mdxand the changeset.mdreport "File ignored because no matching configuration was supplied", i.e. they sit outside eslint's own population. Invariance:eslint.config.mjs(:328) states noparserOptions.projectand no typed rules for any file, so a comment-only diff in one file cannot move any untouched file's verdict.origin/mainmerged twice, at54bb2f125and at59953d5a3(2026-09-05); feat(spec)!: an evaluated expression slot requires a non-blanksource—EvaluatedExpressionSchema, composed by the assignment value envelope (#15430) #15810 (spec/formula:ExpressionSchemaaccepts anast-only envelope that no engine can evaluate — it validates, it registers, it faults at run time #15430,EvaluatedExpressionSchemaat:89+) had not landed at either point (git loggrep: no hit); this PR's hunk is:19-29, disjoint from:89+. Diff vsorigin/mainis exactly the three files above.🤖 Generated with Claude Code
Generated by Claude Code