Skip to content

[finding] The Expression Protocol dialect table tells authors the cron engine is cron-parser — that package is not a dependency of this repo at all; the library is croner, and the table ships to customers via generated docs #15527

Description

@os-litant

Recorded by the os-dev seat on #15027 (session session_01D47qPfEWVPmhguWgBZCi5N, branch claude/issue-15027-expression-ledger-cron-template), measured while hunting the reader of each CronExpressionInputSchema slot to classify it in the ADR-0058 D7 ledger. Unassigned, bare, for triage. Out of #15027's scope: that card changes packages/qa/dogfood/test/** only.

What

packages/spec/src/shared/expression.zod.ts:22, in the # Expression Protocol docblock's dialect table:

| `cron`     | `cron-parser` | job schedules |

Measured at origin/main 5023630b178:

  • grep -rn '"cron-parser"' --include=package.json over the repo (excluding node_modules): zero hits. It is not a dependency of any package, direct or otherwise.
  • The cron library this repo actually uses is croner (^10.0.1), declared by three packages: packages/services/service-job, packages/triggers/trigger-schedule, packages/plugins/plugin-reports.
  • The one cron slot with a live path reaches it as croner: system/job.zod.ts CronSchedule.expressionruntime/job-schedule.ts toBoundaryJobScheduleservice-job/cron-job-adapter.ts CronJobAdapterCron from croner.

Why it matters

⭐ This is not an internal comment. The docblock is the SOURCE of a customer-facing page: the identical row appears in content/docs/references/shared/expression.mdx:24, generated from it. So an author asking "what evaluates my cron expression" is told the name of a package that is nowhere in the product, and the name they would need in order to check syntax rules, alias support, or second-field semantics against real behaviour is not given anywhere.

The two engines differ in ways an author would actually trip on — field counts and alias vocabularies are not identical across cron implementations — so this is not a cosmetic naming slip. The table's other two rows are accurate (cel names @objectstack/formula, template describes the interpolation), which makes the wrong one read as equally load-bearing.

A second inaccuracy in the same row, measured on the same walk

The column is headed engine, and for cron there are two candidates in the tree — neither of which is cron-parser:

  • @objectstack/formula cronEngine (packages/formula/src/cron-engine.ts) — validates 5/6-field patterns and the @yearly@reboot aliases. Measured: zero consumers outside packages/formula. Nothing routes any spec cron slot through it.
  • croner — the library that actually fires schedules, reached only by the job path above.

⇒ For 6 of the 7 distinct cron ratchet keys in the spec there is no engine on either side, and for the 7th it is croner. ⚠️ This bears directly on #15035, which is open and says the syntax verdict "is the formula cron-engine's at evaluate time": that evaluation does not happen for these slots, because nothing calls that engine. Worth reading the two together — this card does not change #15035's scope, and #15035 remains open.

Likely resolution, not a decision

One line in the docblock, then regenerate the reference page (the .mdx is generated, so ⛔ do not hand-edit it). The honest row probably names croner for firing and states that no syntax verdict is reached at parse time, but the wording interacts with #15035 and #15028, so it wants writing once with those in view rather than a find-and-replace.

Verified

Refs: ADR-0058 · ADR-0032 · #15035 · #15028.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions