Skip to content

fix(spec): KnowledgeRefreshPolicy.cron no longer attributes the syntax verdict to a cron engine that never sees it - #16527

Merged
huangyiirene merged 3 commits into
mainfrom
claude/issue-15867-knowledge-refresh-cron-attribution
Sep 7, 2026
Merged

fix(spec): KnowledgeRefreshPolicy.cron no longer attributes the syntax verdict to a cron engine that never sees it#16527
huangyiirene merged 3 commits into
mainfrom
claude/issue-15867-knowledge-refresh-cron-attribution

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #15867

Clause-②: no — no exported symbol moves, no authorable key moves, no accept-set moves. Parse behaviour is byte-for-byte unchanged. Both surface gates were confirmed at exit 0 and ablated to prove they can go red (below), so the green is a measurement rather than a bare reading.

What was false on main

packages/spec/src/ai/knowledge-source.zod.ts told authors that cron syntax on KnowledgeRefreshPolicy.cron "is the cron dialect engine's verdict when the expression is evaluated". Both halves were false, and the sentence is customer-facing — it is the cron row of the generated content/docs/references/ai/knowledge-source.mdx (lines 68 and 133).

Re-measured on this branch's base a5eccf9257, not inherited:

claim measurement
"when the expression is evaluated" names an event that happens git grep -n -E 'refresh\?\.\s*cron|refresh\.cron' outside packages/spec returns one hit, and it is a ledger note. service-knowledge's only refresh reads are source.refresh?.onRecordChange (knowledge-service.ts:396). Nothing evaluates this slot.
the named evaluator is the right one git grep -n -E 'cronEngine|cron-engine' outside packages/formula returns prose only — a CHANGELOG line, the D7 conformance ledger, and the three sites this PR fixes. Positive control: the same pattern inside packages/formula returns the definition (cron-engine.ts:63), the tests and the import sites.

The one evaluator any spec cron slot actually meets is croner, on CronSchedule.expression — a different slot.

The four sites, re-located by phrase and not by line number

The card's line numbers had already drifted once, so every site was re-found with the single-line-safe anchor dialect engine (the fuller phrase wraps across comment lines and matches on no single line).

site card said found on base a5eccf9257
docblock :35-38 :36
.describe() :45-49 :49
test comment 1 :17-20 :21
test comment 2 :96-98 :106

A deliberate multi-line scan (comment markers stripped, whitespace collapsed, whole tree) confirms the population is exactly these four source sites plus the two generated .mdx rows. packages/spec/CHANGELOG.md also carries the old wording and is deliberately left alone — it is a record of what shipped.

What they say now

All four now match the wording of the already-landed sibling correction in packages/spec/src/shared/expression.zod.ts:25-30: cron syntax is not judged at parse time and no engine evaluates this slotcroner judges a cron pattern only where a schedule is wired (CronSchedule.expression, a different slot), and @objectstack/formula's registered cron engine has no caller outside that package — so the verdict belongs to whatever external scheduler the author hands the value to.

Three boundaries held deliberately:

  1. The describe's first half is accurate and untouched. "The parse enforces a non-empty string or an expression envelope and normalizes to the envelope", and "not checked here", both survive verbatim.
  2. The pin test's behaviour is untouched. The parse does not refuse 'not a cron' and does not start to; only the two comments were wrong.
  3. The comment block at :21 is corrected, not deleted. It ends with a self-maintaining rule — that the pin is what keeps the describe honest, and that if the shared dialect ever gains parse-time syntax validation the pin flips and the describe must be rewritten in the same commit. That rule is true and is preserved verbatim; only the attribution half of the block changed.

This correction is safe to write now rather than obsolete on arrival: re-confirmed on base that expression.zod.ts:25 and :267 both still read No cron syntax is judged at parse time, so no parse-time verdict has landed. The parent card #15035 is no longer open, and the PR that resolved it fixed the envelope arm and blank-string refusal — not a syntax verdict.

Generated artifacts this change moves — measured, not estimated

Measured by generating from the base source, snapshotting, restoring to this branch's source (restore proven by blob hash equal to the HEAD blob and an empty git diff HEAD for that path), and generating again:

  • Tracked (1): content/docs/references/ai/knowledge-source.mdx — the only artifact check:generated proved stale; regenerated with check:generated --fix, never hand-edited.
  • Gitignored (3), all under packages/spec/json-schema/: ai/KnowledgeRefreshPolicy.json, ai/KnowledgeSource.json, objectstack.json.

Population of that gitignored tree is unchanged at 1576 files — three contents move, nothing is added or removed.

Verification

All readings below are from the final commit f7a9b0f901; exit codes were captured before any pipe.

Tests and types

  • pnpm --filter @objectstack/spec test483 test files, 13136 tests, all passed, VERDICT command-exit 0 from the shared verify lock.
  • pnpm --filter @objectstack/spec typecheck → exit 0. Its check:test-typecheck leg confirms the test layer really compiles (54 file(s) / 261 error(s) / 145 pinned signature(s) held in the shrink-only debt ledger), so the edited test file is genuinely type-checked and not silently excluded.

Clause-② gates, with ablations

gate on this branch ablated restored
check:api-surface exit 0 removed one export line from api-surface/ai.jsonexit 1, "0 breaking (removed/narrowed), 1 added." exit 0
check:authorable-surface exit 0 removed ai/KnowledgeRefreshPolicy:cron from authorable-surface/ai.jsonexit 1, + ai/KnowledgeRefreshPolicy:cron exit 0

Each ablation ran under a restore trap with absolute paths; each mutation was proven on disk (line count and blob hash both moved) and each restore proven by blob hash equal to the HEAD blob and an empty git diff HEAD.

Gate families

Derived mechanically with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (change set taken from the merge base, three-dot), then re-derived after the changeset existed, which added six more. All 97 runnable derived commands were run: 95 exit 0, 2 not measured (named below). The 95 include check:generated (15 artifacts), check:nul-bytes, check:api-surface, check:authorable-surface, check:docs, check:type-check-coverage, check:cross-package-test-inputs, check:test-source-alias, check:skill-examples (all three prose surfaces, after building @objectstack/client-react so its verdict is not a false green) and the four changeset families. Two roster-block gates were run on top: check-changeset-fixed.mjs and check-skills-token-ratchet.mjs, both exit 0.

pnpm lint (eslint . --no-inline-config) ran repo-wide rather than narrowed: 6273 files linted, 0 errors, 0 warnings, exit 0.

Not measured (never read as a pass and never as a finding): check:dual-build-cjs-loads exits 3, PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/; check:type-check-debt is a whole-farm aggregate that did not finish inside the foreground budget. Both belong to CI, which builds everything. check:react-declaration-parity cannot run here at all — its right-hand side is objectui's sdui.manifest.json.

Byte discipline: grep -naP over the edited files for control characters returns no hits, and check:nul-bytes is green.

Generated by Claude Code


Generated by Claude Code

…tax verdict to a cron engine that never sees it

The docblock, the shipped `.describe()` and the slot's two pin-test comments
all said cron syntax was "the `cron` dialect engine's verdict when the
expression is evaluated". Both halves were false: nothing evaluates
`refresh.cron` (`service-knowledge` reads `refresh.onRecordChange` and never
`refresh.cron`), and `@objectstack/formula`'s registered `cron` engine has no
caller outside that package. The claim shipped to authors through the
generated reference page.

All four sites now match the already-corrected Expression Protocol dialect
table: no engine evaluates this slot — `croner` judges a cron pattern only
where a schedule is wired (`CronSchedule.expression`, a different slot) — so
the verdict belongs to whatever external scheduler the author hands the value
to. The pin test's behaviour is untouched; only its comments were wrong, and
the self-maintaining rule that block ends with is preserved verbatim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ai tests tooling labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 3335bd35eaf47a5b2aed9120e217dafd242ff7e1packageMentionDocs.

Which tree this was computed on

This run read content/docs from ca3544942d328a9a7a909b7db7dd0aefa6631c74 — the merge of head f7a9b0f901101efa494602c48fc702d2bea1b7f6 into base 3335bd35eaf47a5b2aed9120e217dafd242ff7e1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ca3544942d328a9a7a909b7db7dd0aefa6631c74 && git checkout ca3544942d328a9a7a909b7db7dd0aefa6631c74
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3335bd35eaf47a5b2aed9120e217dafd242ff7e1 f7a9b0f901101efa494602c48fc702d2bea1b7f6 && git checkout -B drift-repro 3335bd35eaf47a5b2aed9120e217dafd242ff7e1 && git merge --no-ff f7a9b0f901101efa494602c48fc702d2bea1b7f6

node scripts/docs-audit/affected-docs.mjs --json 3335bd35eaf47a5b2aed9120e217dafd242ff7e1

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

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:ai size/s tests tooling

Projects

None yet

2 participants