Skip to content

fix(spec): replace the croner-refused cron example in the DisasterRecoveryPlan docblock - #16415

Open
huangyiirene wants to merge 2 commits into
mainfrom
claude/issue-15945-backup-config-cron-example
Open

fix(spec): replace the croner-refused cron example in the DisasterRecoveryPlan docblock#16415
huangyiirene wants to merge 2 commits into
mainfrom
claude/issue-15945-backup-config-cron-example

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #15945

One character-level edit to a TSDoc @example, plus its changeset. The interesting part is the measurement, and one thing the card got wrong.

The measurement the card asked for

Triage was explicit that it had not run croner, so the card's central claim was inherited, not measured. Measured here against the croner 10.0.1 copy installed for @objectstack/service-job — the same library CronJobAdapter hands every scheduled expression to (packages/services/service-job/src/cron-job-adapter.ts:130, new Cron():

pattern croner 10.0.1 note
'0 0/6 * * *' REFUSED the example shipped before this change
'0 0,6,12,18 * * *' ACCEPTED the example shipped after this change
'0 */6 * * *' ACCEPTED croner-accepted, but see the trap below
'0 2 * * *' ACCEPTED positive control — the sibling example at :19 / :58

The refusal, quoted:

TypeError: CronPattern: Syntax error, stepping with numeric prefix ('0/6') is not
allowed. Use wildcard (*/step) or range (min-max/step) instead.

The control matters: without an ACCEPTED result in the same run, a REFUSED reading is indistinguishable from a probe that cannot construct any job at all. Both spellings of six-hourly were measured to fire at identical instants — 2026-09-06T06:00Z, 12:00Z, 18:00Z, 2026-09-07T00:00Z — so this is an equivalent, not an approximation.

The trap: the spelling croner itself recommends is not writable here

croner's error message suggests */step, and that is the spelling disaster-recovery.test.ts:168 and integration/connector.test.ts:238 use. It cannot be written at this site: the example lives inside a /** ... */ block comment, and the wildcard-step separator is the comment terminator. Writing it closed the docblock mid-example and the file stopped parsing — measured, not reasoned:

gen:schema -> Transform failed with 1 error:
  packages/spec/src/system/disaster-recovery.zod.ts:204:26: ERROR: Unexpected "*"

That is why this PR ships the enumerated form '0 0,6,12,18 * * *' — the card's own second option — rather than the wildcard-step form. It is croner-accepted, fires identically, and carries no comment-terminating sequence. The failure is loud (the build refuses it), so this is an authoring hazard rather than a latent defect, but it is worth knowing before anyone "tidies" this example toward the more idiomatic spelling.

Anchors: every one re-read on current main, and they had drifted

Base fd75728bc6.

card anchor actual on this base matched?
disaster-recovery.zod.ts:187 — the defect :204 NO — drifted 17 lines
disaster-recovery.zod.ts:19 — sibling example '0 2 * * *' :19 yes
disaster-recovery.zod.ts:57 — the .describe() :58 NO — drifted 1 line
disaster-recovery.zod.ts:238 — the DR-drill schedule :255 NO — drifted 17 lines
disaster-recovery.test.ts:168 — the wildcard-step spelling :168 yes
expression.zod.ts:25 — "No cron syntax is judged at parse time" :25 yes

One card claim measured FALSE

The card and the triage comment both state the docblock "lands in customer-facing generated docs (content/docs/references/system/disaster-recovery.mdx)". It does not. The string 0 0/6 occurs exactly once in the whole tracked repository — at the source line itself. The generated page carries only the .describe() strings, which already spelled the good example (cron + 0 2 * * *) at rows 33 and 140.

The premise survives anyway, because the docblock publishes somewhere else — the shipped type declarations:

packages/spec/dist/system/index.d.ts:495
 *     schedule: '0 0/6 * * *',

That is the text an editor shows on hover for every consumer of @objectstack/spec, which is why this change is user-visible and carries a changeset. packages/spec/scripts/build-docs.ts never reads TSDoc @example at all — it synthesises its import examples from the real export surface, deliberately — so the mdx omission is by design and is not filed as a finding.

The N/M sweep, re-taken with its control lit

Re-taken rather than inherited, and widened from triage's packages/spec/src to the whole packages/spec package (1587 files), then to packages · examples · content · skills · apps · scripts:

  • Stage A (the lit control)N/M-shaped string literals: 35 in packages/spec, 248 repo-wide. These are true-positive non-cron hits — a date '7/28/2026', CIDRs '192.168.1.0/24' and '10.0.0.0/8', prose like '404/405/501', test names like 'SEAM 2/3'. The matcher demonstrably returns hits.
  • Stage B — of those, cron-shaped: 1, repo-wide. This one.
  • Stage C — of those, carrying numeric-prefix stepping: 1. This one.
  • Classifier control — the cron-shape classifier was itself lit separately: it finds 77 cron-shaped literals of any form in packages/spec ('0 2 * * *', '*/15 * * * *', '0 0 * * 0', …), so the "exactly 1" in stage B is a reading and not a broken classifier.

Triage's finding is confirmed and now holds over a wider corpus: there is no sweep owed, and nothing else in the repo is a sibling of this defect.

Clause ②: no

A docblock example. No schema, no export, no accept-set movement, no cron parsing added anywhere; CronExpressionInputSchema is untouched. Measured rather than asserted: rebuilding @objectstack/spec and diffing all 64 emitted .d.ts files against the pre-edit build gives 1 file changed, 1 line changed, and that line is the comment. No dependent package can observe a type-level difference.

Deliberately out of scope, per the dispatch: the wider formula cronEngine vs croner grammar divergence (5 of 32 patterns) is reported on #15035 and is the spec lane's to rule on. It is not addressed here.

Verification

Repository objectstack-ai/objectstack, head 0e1867d18a, base fd75728bc6.

  • pnpm --filter @objectstack/spec build — exit 0 (check-dts-emitted: 34/34 declaration file(s) present)
  • pnpm --filter @objectstack/spec typecheck — exit 0 (check:test-typecheck: OK)
  • pnpm --filter @objectstack/spec test — exit 0, 482 files / 13102 tests passed
  • pnpm --filter @objectstack/spec check:generated — exit 0, All 15 generated artifacts are up to date
  • pnpm lint (eslint . --no-inline-config) — exit 0, whole repository, not narrowed, 64s
  • Gate families derived mechanically with scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack against the real change set: 67 commands, 66 exit 0 and 1 declared to CI. Two came back exit 3 PREREQUISITE NOT MET — a code that measures nothing and is not a finding. check:doc-formula-expressions was re-run green (exit 0) after building the two prerequisites it named; check:dual-build-cjs-loads needs all 86 packages built and is declared to CI, which builds fresh.
  • pnpm check:nul-bytes exit 0, plus a direct control-character scan of both edited files — clean, with its positive control lit (the same expression finds planted BEL and ESC bytes).

Test Core (5/6) is being killed at its 30-minute wall on a large fraction of runs right now (#16173). If it is red, read that shard's duration first.

Not for merging

Draft, by instruction. No governed surface in the diff (packages/spec/src/** and .changeset/** only), but the merge call is the PM's regardless.


Generated by Claude Code

…overyPlan docblock

The `DisasterRecoveryPlanSchema` `@example` spelled its six-hourly backup
schedule `'0 0/6 * * *'`. `0/6` is Quartz-style stepping, and `croner` — the
only cron parser the platform runs, reached via `CronJobAdapter` -> `new Cron()`
— refuses it:

    TypeError: CronPattern: Syntax error, stepping with numeric prefix ('0/6')
    is not allowed. Use wildcard (*/step) or range (min-max/step) instead.

Measured against the croner 10.0.1 copy installed for
`@objectstack/service-job`, with the sibling example `'0 2 * * *'` as the
positive control (accepted). The replacement `'0 */6 * * *'` is accepted and
fires at the same instants; it is the spelling this schema's own tests already
use.

Comment-only: no schema, no export, no accept-set movement. The docblock does
publish into the shipped `dist/system/index.d.ts`, so the change is
user-visible and carries a changeset.

Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
Co-authored-by: Claude <noreply@anthropic.com>
… closes the docblock

The wildcard-step form croner's own error message suggests cannot be written
inside a `/** ... */` block comment: the step separator is the comment
terminator, so the file stops parsing. Measured — esbuild refused
disaster-recovery.zod.ts at the example's own line, column 26, during
`pnpm --filter @objectstack/spec build`.

The enumerated equivalent carries no such sequence, is accepted by the same
croner 10.0.1 copy, and was measured to fire at the identical instants
(00:00, 06:00, 12:00, 18:00).

Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/s label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/system/disaster-recovery.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/system/disaster-recovery.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 — 130 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 dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tooling labels Sep 6, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review September 6, 2026 21:24
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 protocol:system size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec/system: the BackupConfig docblock ships a cron example '0 0/6 * * *' that croner — the only cron parser the platform actually runs — refuses

2 participants