Uh oh!
There was an error while loading. Please reload this page.
feat(spec): type KnowledgeRefreshPolicy.cron with CronExpressionInputSchema — the describe promises what the parse enforces (#14825) - #15029
Conversation
…putSchema` (#14825) `refresh.cron` on a knowledge source was a bare `z.string()` under a doc comment promising a 5-field cron — a constraint nothing checked. It now carries the shared cron-dialect input the three sibling cron fields use, with a describe that promises exactly what the parse enforces (a non-empty string or an expression envelope, normalized to the envelope; syntax is the cron engine's verdict at evaluate time). `KnowledgeRefreshPolicyParsed` / `KnowledgeSourceParsed` name the parsed state (ADR-0122) and the two isomorphism pins they replace leave the registry. Pins measured, not assumed: `'not a cron'` normalizes rather than being refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174WZTU6XcFcS7g2kykC53i
…re the projection-only default move (#14825) `check:generated --fix` regenerated the three artifacts it proved stale (references doc, api-surface, export-origins — the two new `XParsed` exports). `gen:schema` moved `authorable-defaults/ai.json`: the runtime default of `refresh` is unchanged (a source omitting it still parses to `{}`, measured at base and head), but `ai/KnowledgeSource` now publishes as the input shape and zod's input-mode projection carries no `default` keyword for a `.default()` whose object holds a transform — declared in DEFAULT_CHANGES_BY_MAJOR per the gate's own instruction, and the `refresh` description states the materialized default in words. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174WZTU6XcFcS7g2kykC53i
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 356639891d7afeb80d8dde2fa789583a549efb43 && git checkout 356639891d7afeb80d8dde2fa789583a549efb43
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a4e4d2d78a42c0bd5602d55afc7089a763f76185 242a7be41f529d7ec86d83d454eb85cc255b0baf && git checkout -B drift-repro a4e4d2d78a42c0bd5602d55afc7089a763f76185 && git merge --no-ff 242a7be41f529d7ec86d83d454eb85cc255b0baf
node scripts/docs-audit/affected-docs.mjs --json a4e4d2d78a42c0bd5602d55afc7089a763f76185
|
zhuangjianguo
commented
Sep 3, 2026
Landing provenance (PM seat
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#14825
Clause ②: yes —
needs:contract-review(dual carrier with the card): values that parse green today stop parsing (an empty string), and the parsed shape of a published key changes (bare string becomes the{ dialect: 'cron', source }envelope).What
KnowledgeRefreshPolicySchema.cron(refresh.cronon a knowledge source) was a barez.string()under a doc comment promising a 5-field cron — a constraint nothing checked (ADR-0049 declared vs enforced). It now carriesCronExpressionInputSchema, the shared cron-dialect input the spec's other cron-shaped fields already use, with a describe that promises exactly what the parse enforces.KnowledgeRefreshPolicyParsed/KnowledgeSourceParsedname the parsed state (ADR-0122); the two isomorphism pins they replace leave the registry (831 to 829, receipt in the pin file). New pin filepackages/spec/src/ai/knowledge-source.test.ts(8 cases). Changeset@objectstack/specminor with the BREAKING banner and the ADR-0087 dispositionnot-required (no-migration-prescription).Measurement first (triage 5520834102 — the fork is decided by M1, not by preference)
refresh.cron.git grep -nE '\bcron\b'overexamples/**,packages/platform-objects/**,packages/qa/**,packages/**/*.test.ts,packages/services/service-knowledge/**,skills/**,content/docs/**,packages/spec/src/ai/**at base6392b9c2: 557 hits; positive control hit (knowledge-source.zod.ts:33). Every hit read: zero writers of acronvalue on a knowledge-sourcerefreshblock. The only references to the key are the schema line, the generated reference row, andskills/objectstack-ai/SKILL.md:232(describes the key, writes no value — still true after typing, untouched).service-knowledgereads onlyrefresh.onRecordChange;../objectuihas zero hits forrefresh.cron/KnowledgeRefreshPolicy. Result: TYPE IT.z.object({ cron: CronExpressionInputSchema.optional() })):'0 9 * * 1-5', the 6-field'0 0 9 * * *'AND'not a cron'all parse GREEN to{ dialect: 'cron', source };'',42,nulland{ dialect: 'js' }are refused withinvalid_unionat path['cron'], messageInvalid input(branch errorstoo_small/invalid_type/invalid_value). So H3 is falsified: the shared dialect validates no cron syntax at parse time — the syntax verdict is the@objectstack/formulacron-engine's (5- or 6-field, or an@alias) when the expression is evaluated, and nothing in this repo importscron-parser. The describe and the pins state exactly that (declared = enforced), including a deliberate pin that'not a cron'normalizes rather than being refused, so a later change to the shared dialect surfaces here.packages/lint/src/validate-expressions.tsreads an enumerated table of CEL predicate keys and returns early on non-CEL dialects; none of the existing cron sites has a row;page-envelope-audit.tsdiscovers positions behaviourally. No lint scope row is owed. The other site registry — the ADR-0058 D7 expression conformance ledger inpackages/qa/dogfood— discovers by schema name and lists onlyExpressionInputSchema/SettingsVisibilityInputSchema: its regex replicated verbatim against this tree discovers 24 surfaces, the new site is NOT among them (control: anExpressionInputSchemaslot matches), and none of the 8 existing cron sites is either. Filed as ADR-0058 D7 expression conformance ledger discovers onlyExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027 (out of scope: it touches the dogfood ledger and [finding]skills/objectstack-ai/SKILL.md:405-406calls a model-registrypromptTemplate.system/.user"a CEL predicate" — those keys are thetemplatedialect ({{var}}), and the AI domain has no CEL site at all #14797's prompt-template sites). Correction to the card: the enforce half ships at 8 positions in 7 files, not 3 (system/cache,system/disaster-recoveryx2,system/jobas well).PM mechanism hypotheses
check:generated --fixregenerated the 3 it proved stale (references doc, api-surface, export-origins — the two newXParsedexports).gen:schemamovedauthorable-defaults/ai.json:ai/KnowledgeSource:refresh {} → (none). The runtime default is unchanged — measured:KnowledgeSourceSchema.parseon a source that omitsrefreshyieldsrefresh: {}at base and head — butai/KnowledgeSourcenow publishes as the input shape and zod's input-mode projection drops thedefaultkeyword for a.default()whose object holds a transform (isolated repro: the same wrapper around a transform-free object keeps it). Declared inDEFAULT_CHANGES_BY_MAJORper the gate's own instruction (its reason is printed by every build), and therefreshdescribe states the materialized default in words — thedata/Field:uniqueprecedent.authorable-surfaceunchanged (the key already existed); noskills/**path moved;baseRevnote: the anchor lags, informational.minorunder the launch-window convention.check-changeset-no-majorgreen,check-adr-0087-registrationgreen withnot-required (no-migration-prescription)(no key removed/renamed/re-homed, bare strings keep parsing byte-identically, nothing forobjectstack migrate metato rewrite; the one newly refused input never named a schedule),check-empty-changesetgreen.Verification (final head
242a7be4)src/ai/knowledge-source.test.ts+src/type-alias-convention.pin.test.ts— 2 files, 11 tests passed.pnpm --filter @objectstack/spec build— VERDICT command-exit 0;typecheck(tsc + scripts + test layer) — VERDICT command-exit 0;tsc -p tsconfig.test.json --listFileslists the new test file, 0 errors on it.check:generated(no fix) at242a7be4after the rebuild: "All 15 generated artifacts are up to date."cron: z.string().optional()proven on disk by grep counts, 0/1; direction predicted RED on the new file): 7 of 8 cases red, pin file green; restore viagit checkout HEAD -- path,git diff HEADempty, blob hash equals HEAD's blob. The subject resolves fromsrc, so there is no dist leg to this ablation.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon the actual diff (77 commands): every measured gate green. NOT MEASURED (exit 3 prerequisite / unbuilt closure, CI's farm owns them):check:doc-formula-expressions,check:doc-security-posture,check:type-check-debt,check:dual-build-cjs-loads,check:test-completeness,check-dev-prereqs(66 unbuilt packages).Not touched
packages/spec/src/ai/model-registry.zod.ts(#14797's territory),packages/spec/src/shared/expression.zod.ts,skills/**,packages/lint.origin/mainmoved 3 commits during the run (none touch these files); no merge taken, the queue rebuilds on currentmain.Out of scope, filed
ExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027 — the ADR-0058 D7 ledger's discovery excludes everyCronExpressionInputSchema/TemplateExpressionInputSchemasite.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 (finding) — the typed input schemas fix the dialect only on the bare-string arm; the envelope arm accepts any declared dialect.🤖 Generated with Claude Code
https://claude.ai/code/session_0174WZTU6XcFcS7g2kykC53i
Generated by Claude Code
Generated by Claude Code