Skip to content

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 into
mainfrom
claude/issue-15527-cron-dialect-row-names-croner
Sep 5, 2026
Merged

docs(spec): the dialect table's cron row names what fires a schedule — cron-parser is not in the product#15877
os-project-manager merged 4 commits into
mainfrom
claude/issue-15527-cron-dialect-row-names-croner

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #15527

What

The ## Dialects table in the # Expression Protocol docblock of packages/spec/src/shared/expression.zod.ts told authors the cron engine is cron-parser. That package is not a dependency of any package in this repo (grep -rn '"cron-parser"' --include=package.json, node_modules excluded: 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 as content/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):

dialect engine use
cron cron-parser job schedules

After (:22, plus the paragraph at :25-29):

dialect engine use
cron none at parse time — croner fires it at schedule time, on the one wired slot job schedules

"No cron syntax is judged at parse time: croner evaluates a cron slot only when CronSchedule.expression is scheduled (toBoundaryJobScheduleCronJobAdapter, where an invalid pattern is refused); every other cron-typed slot is parsed and reaches no engine, and @objectstack/formula's registered cron engine has no caller outside that package."

No other row changed; no schema, enum, accept set or behaviour moves (Clause-②: no). The cel and template rows are untouched.

Files

  • packages/spec/src/shared/expression.zod.ts — docblock rows :19-29 only.
  • content/docs/references/shared/expression.mdx — regenerated by pnpm --filter @objectstack/spec gen:docs (never hand-edited); the generator changed no other file (git status after the run: this one path).
  • .changeset/cron-dialect-row-names-croner.md@objectstack/spec patch.

H1 — the facts the row rests on (measured on origin/main 89758ac82, re-checked at 59953d5a3)

Every CronExpressionInputSchema position in the spec — 9 positions, 7 distinct file:field ratchet keys:

slot (ratchet key) file:line engine at parse engine at fire
system/job.zod.ts:expression (CronSchedule.expression) system/job.zod.ts:15 none cronerruntime/src/job-schedule.ts:35 toBoundaryJobSchedule (called from runtime/src/app-plugin.ts:1050) lowers the envelope to a bare string and checks dialect/source shape only; then services/service-job/src/cron-job-adapter.ts:130 new Cron(schedule.expression, …) (import { Cron, scheduledJobs } from 'croner' at :3); an invalid pattern throws there
api/export.zod.ts:cronExpression :576, :706 none none — ScheduledExportSchema / ScheduleExportRequestSchema have no consumer outside packages/spec
automation/execution.zod.ts:cronExpression :454 none none — ScheduleStateSchema has no consumer outside packages/spec
integration/connector.zod.ts:schedule :257 none none — the only SyncConfigSchema hit outside spec is turso's own TursoSyncConfigSchema
system/cache.zod.ts:schedule :156 none none — CacheWarmupSchema has no consumer outside packages/spec
system/disaster-recovery.zod.ts:schedule :57, :238 none none — BackupConfigSchema / the DR testing block have no consumer outside packages/spec
ai/knowledge-source.zod.ts:cron :45 none none — service-knowledge reads refresh.onRecordChange only; the value is surfaced for an external scheduler (D7 ledger row cron-knowledge-refresh)
  • Parse: CronExpressionInputSchema (:110-113) is z.string().min(1) transformed to the envelope, or ExpressionSchema; nothing reads the pattern. packages/lint/src/validate-expressions.ts:369 skips every non-cel dialect, so there is no lint-time verdict either.
  • @objectstack/formula's cronEngine (packages/formula/src/cron-engine.ts:63) is registered at packages/formula/src/registry.ts:43 and referenced nowhere outside packages/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 a dialect: 'cron' envelope.
  • croner 10.0.1 behaviour, measured from packages/services/service-job: '0 0 * * *', '0 0 9 * * *' and '@yearly' accepted; 'not a cron' and '' throw CronPattern: invalid configuration format (...), exactly five, six, or seven space separated parts are required.
  • The other two croner users read plain strings, not a dialect slot: triggers/trigger-schedule/src/schedule-trigger.ts:103 normalizeSchedule (a flow start node's schedule config) and plugins/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

Pins

  • packages/spec/src/shared/expression-dialect-docs.pin.test.ts reads the table's first column against ExpressionDialect — 4/4 green on 73a2adffb; its own docblock says wording is free, and no test in packages/**/*.test.ts pins the old wording (git grep cron-parser: only the D7 ledger comment at expression-conformance.ledger.ts:280, which already says "croner, not cron-parser").
  • check:docs green after regeneration; check:doc-authoring and check:doc-formula-expressions green.

Verification (final head 73a2adffb; every exit code captured before any pipe)

  • Build: pnpm --filter @objectstack/spec build under the verify lock (spec has no workspace deps; exit 0, check-dts-emitted 34/34); formula, sdui-parser and lint built to satisfy the lint-side gates' prerequisites.
  • Tests: the full @objectstack/spec suite at 236f6da97 (before the second origin/main merge): 473 files / 12723 tests passed; typecheck green (tsc --noEmit, check:scripts-typecheck, check:test-typecheck OK). On 73a2adffb, after merging 25 incoming commits that touch packages/spec/src/data and src/automation but none of this PR's files: the pin test 4/4 (the only test file that imports shared/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.
  • Gates, derived with node scripts/pm/dispatch-gates.mjs --commands (no paths) at 73a2adffb: 89 families; 87 green; 2 NOT MEASURED, both PREREQUISITE NOT MET on unbuilt dists this card does not build (check:skill-examples wants packages/client-react/dist declarations; check:dual-build-cjs-loads wants 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:generated green (no stale artifact). pnpm check:nul-bytes green.
  • ESLint, narrowed (the repo scan is CI's): pnpm exec eslint --no-inline-config --format json over the three changed files at 73a2adffb — 3 files in the JSON; expression.zod.ts 0 errors / 0 warnings; the .mdx and the changeset .md report "File ignored because no matching configuration was supplied", i.e. they sit outside eslint's own population. Invariance: eslint.config.mjs (:328) states no parserOptions.project and no typed rules for any file, so a comment-only diff in one file cannot move any untouched file's verdict.
  • Merge state (H5): origin/main merged twice, at 54bb2f125 and at 59953d5a3 (2026-09-05); feat(spec)!: an evaluated expression slot requires a non-blank sourceEvaluatedExpressionSchema, composed by the assignment value envelope (#15430) #15810 (spec/formula: ExpressionSchema accepts an ast-only envelope that no engine can evaluate — it validates, it registers, it faults at run time #15430, EvaluatedExpressionSchema at :89+) had not landed at either point (git log grep: no hit); this PR's hunk is :19-29, disjoint from :89+. Diff vs origin/main is exactly the three files above.

🤖 Generated with Claude Code


Generated by Claude Code

… 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
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/shared/expression.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/shared/expression.zod.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 129 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ef3a1388dd3b8e4eb326c4073f4dd1c29488ebc8packageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review September 5, 2026 12:41
@os-project-manager
os-project-manager added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 132742f Sep 5, 2026
36 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-15527-cron-dialect-row-names-croner branch September 5, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants