Repository navigation
[finding] KnowledgeSourceSchema.cron is documented as a 5-field cron expression but typed z.string() — the spec's own CronExpressionInputSchema is not used, so 'not a cron' parses green #14825
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Sep 3, 2026 Triage — graded.
bug·priority:p3·pm:queue·domain:spec·needs:contract-review. Adopted as filed.Verified at
origin/main✅
knowledge-source.zod.ts:33—cron: z.string().optional(),, against a doc comment promising a 5-field cron.
✅CronExpressionInputSchemaexists inshared/expression.zod.tsand is already used by three sibling surfaces —api/export.zod.ts,automation/execution.zod.ts,integration/connector.zod.ts. ⇒ This is not a missing capability; it is the one cron-shaped field in the spec that does not use the schema the rest of the spec uses for cron-shaped fields.⭐ That is what makes it a clean ADR-0049 instance rather than a wish: the enforce half already ships, three times over.
needs:contract-review— Clause ②, and not reflexiveTyping the field narrows an accept set on a published schema: values that parse green today would stop parsing. That is the tier's own trigger, independent of anything new being exported.
⚠️ ⛔ Which is also why the migration question is the first step, not a follow-up: measure whether any example app, fixture or test writes a non-5-field value into this key today. The card names the check and it is load-bearing. If something does, ⛔ do not narrow and then fix the caller in the same PR — report it, because "who is relying on the loose typing" changes whether this is a typing fix or a breaking change with a migration.The fork — the spec lane's, and I am not pre-empting it
The card offers: type it, or stop documenting a shape nothing checks. ⛔ I am not ruling that here — unlike #14828, this one genuinely turns on the migration measurement above, which nobody has. ⇒ Take the measurement first; it decides the fork rather than colouring it.
⚠️ If you type it: check whetherpackages/lint's expression rule needs a scope row for the new site. ⛔ A newly-typed expression field that the lint does not know about is a second declared-but-unenforced surface replacing the first.⛔ Out of scope: the skills-side sentence that used to cite this cron as an expression site — already corrected by PR #14819, and it depended on neither outcome. ⛔ Do not touch
model-registry.zod.ts's prompt-template keys (#14797's territory,pm:dispatched).Dedup
⚠️ search_issuesreturned 0 results and the card records no positive control for it. Given #14743 — free-text false zeros withincomplete_results: false— a bare zero is not evidence of absence. ⛔ Not treating this as a dedup failure: the filer's grep-based reachability check (ExpressionInputSchema|ExpressionSchema|PredicateSchemaunderpackages/spec/src/ai/, returning onlymodel-registry.zod.ts) is deterministic and is what actually found the card. But⚠️ the claimer should re-run the dedup with a control before opening a PR. Seven cards this shift have now worked around that instrument; a zero from it means nothing without one.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsClaim: PM loop,
domain:specseat (seat post #6017), R2 of this shift
Session:session_0174WZTU6XcFcS7g2kykC53i(GitHubzhuangjianguo)
Branch:claude/issue-14825-knowledge-source-cron-schema
Worktree:objectstack-issue-14825
Domain:domain:spec
File surface:packages/spec/src/ai/knowledge-source.zod.ts:33(cron: z.string().optional()→CronExpressionInputSchema.optional()with a describe, CONDITIONAL on the measurement below) · its test file (pins: a 5-field expression parses;'not a cron'is refused with the issue path atcronand the dialect's own message; a positive control) ·packages/lint's expression rule scope row only if that rule enumerates typed sites (dev measures — a newly typed expression field the lint does not know is a second declared-but-unenforced surface) · generated artifactscheck:generatedproves stale (json-schema / authorable-surface / references) ·.changeset/*.md@objectstack/spec(a narrowing of a published accept set — the level and the ADR-0087 disposition are what the changeset gates say; the dev follows them and reports). Measurement first: does any example app, fixture, test, platform object or dogfood corpus write a non-5-field value into this key today? Non-empty ⇒ STOP,blockedreport with the sites (triage 5520834102: "who is relying on the loose typing" changes the card's class). ⛔model-registry.zod.tsuntouched (#14797's territory). Stop on breach; explain in the report.
Container & model: S,mode:subagent,model: fable(CONTRACT_REVIEW_TIER).node scripts/pm/dispatch-gates.mjs --tier packages/spec/src/ai/knowledge-source.zod.tsat77a532d6(17:00Z): "no path-derived mandate" + "Clause ② SUSPECT surface" (packages/spec/src/**); tier decided from content — the accept set narrows.
Clause-②: yes — values that parse green today stop parsing (triage: "Typing the field narrows an accept set on a published schema").
Serial constraints cleared: fresh open-PR file scan at 17:00Z (27 open PRs) — none touchesknowledge-source.zod.tsorshared/expression.zod.ts; last commit on the fileb8fc7280(2026-09-02) — no same-day churn; lane siblings (#14791packages/lint, #14847stack.zod.ts) file-disjoint; H17 index: no row.Decision re-read (all comments read, one — triage 5520834102): the fork (type it vs stop documenting a shape nothing checks) is deliberately not ruled and turns on the migration measurement; if typed, the lint scope-row check is owed; the dedupe zero has no positive control — the dev re-runs it with a control before opening a PR. Premise re-read on
origin/main77a532d6(17:00Z):knowledge-source.zod.ts:33cron: z.string().optional()with the 5-field doc comment at:28-30;CronExpressionInputSchemain use atapi/export.zod.ts:576/706,automation/execution.zod.ts:440,integration/connector.zod.ts:257— the enforce half ships three times. Premise holds.
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsDispatch (R2 of this shift, 2026-09-03T17:03Z) —
domain:specseat,session_0174WZTU6XcFcS7g2kykC53i, seat post #6017.mode:subagent,model: fable(CONTRACT_REVIEW_TIER), size S, Clause ② yes. The dev leaves its ownClaim:comment below; the assignee stays the PM's.Dispatch text handed to the dev, in brief (triage 5520834102's measurement-first order, lint scope-row check and the
model-registry.zod.tsfence are quoted verbatim and marked non-renegotiable):- Measurement first: M1 every writer of
cronon a knowledge source across the corpus (with the schema line as the positive control) — any non-5-field writer ⇒ STOP with ablockedreport; M2CronExpressionInputSchema's refusal shape and whether the dialect admits 6-field expressions (the describe states what is enforced); M3 whether the lint's expression rule enumerates typed sites (scope row only if it does). - Zero writers ⇒ type the field with the shared dialect (the enforce half already ships three times), pins asserting the issue
code+ path + first sentence, generated artifacts viacheck:generated --fix, changeset level per the gates (report which fired). - Landing: draft PR with
Fixes #14825+needs:contract-reviewon the PR in the same stroke (the card already carries it — dual carrier); ⛔ no ready flip. In-seat contract review at tier by this seat on delivery.
Generated by Claude Code
- Measurement first: M1 every writer of
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsClaim: os-dev subagent, dispatched by the
domain:specseat (seat post #6017, PM claim comment 5529216886)
Session:session_0174WZTU6XcFcS7g2kykC53i(GitHubzhuangjianguo),mode:subagent,model: fable(CONTRACT_REVIEW_TIER)
Branch:claude/issue-14825-knowledge-source-cron-schema(pushed empty as the write-route probe)
Worktree:../objectstack-issue-14825at base6392b9c2
Clause-②: yes — typing the field narrows the accept set on a published schema (triage 5520834102).File surface (complete; everything past the first row is CONDITIONAL on the M1 measurement — a non-empty writer set stops the run with a
blockedreport and no PR):packages/spec/src/ai/knowledge-source.zod.ts—KnowledgeRefreshPolicySchema.crontyped withCronExpressionInputSchemaplus a describe stating what is actually enforced.packages/spec/src/ai/knowledge-source.test.ts— new pin file (5-field parses to the cron envelope; refusal shape as measured, with issuecode+ path; absent stays absent; positive control).- Generated artifacts that
check:generatedproves stale, regenerated with--fixonly:packages/spec/authorable-surface/ai.json,packages/spec/authorable-surface.base.json,packages/spec/json-schema.manifest/ai.json,packages/spec/api-surface/ai.json(+api-surface-signatures.json),packages/spec/declaration-map/ai.json,packages/spec/export-origins/ai.json,packages/spec/authorable-defaults/ai.json,packages/spec/llms.txt,content/docs/references/ai/knowledge-source.mdx(+references/ai/index.mdx/meta.jsonif the generator moves them). .changeset/knowledge-source-cron-expression.md—@objectstack/spec, level percheck-changeset-no-major/check-adr-0087-registration.packages/lintexpression-rule scope row ONLY if M3 shows the rule enumerates typed sites by path (measuring now).- Not touched:
packages/spec/src/ai/model-registry.zod.ts([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 territory),packages/spec/src/shared/expression.zod.ts,skills/**.
Measurement first (M1 writers of
refresh.cronon a knowledge source acrossexamples/**,packages/platform-objects/**,packages/qa/**,packages/**/*.test.ts,packages/services/service-knowledge/**,skills/**,content/docs/**, with the schema's own line as the positive control; M2 the dialect's refusal shape on'not a cron', an empty string and a 6-field expression; M3 the lint rule's site discovery) — results land in theos-dev-reportcomment.Generated by Claude Code
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 14825, "status": "done", "branch": "claude/issue-14825-knowledge-source-cron-schema", "pr": "https://github.com/objectstack-ai/objectstack/pull/15029", "premise_still_valid": true, "summary": "M1 measured ZERO writers of a cron value on a knowledge-source refresh block (557 corpus hits read, positive control knowledge-source.zod.ts:33 hit; service-knowledge reads only refresh.onRecordChange; objectui zero hits), so per the ruling the field is TYPED: KnowledgeRefreshPolicySchema.cron now carries CronExpressionInputSchema with a describe that promises exactly what the parse enforces, KnowledgeRefreshPolicyParsed / KnowledgeSourceParsed name the parsed state (ADR-0122; two isomorphism pins retired 831 to 829 with a receipt), a new pin file knowledge-source.test.ts (8 cases), a minor changeset with the BREAKING banner and ADR-0087 not-required (no-migration-prescription), and the 3 regenerated artifacts. The card's stated facts hold; its causal claim does not: M2 shows the shared cron dialect refuses only an empty string, a non-string or an undeclared-dialect envelope (invalid_union at path cron), while '0 9 * * 1-5', a 6-field expression AND 'not a cron' all normalize to the cron envelope — no cron-parser exists in the repo, syntax is the formula cron-engine's verdict at evaluate time. H3 is therefore falsified and the pins assert the measured behaviour (including the deliberate non-refusal of 'not a cron'). M3: packages/lint enumerates CEL predicate keys only and skips non-CEL dialects, no cron site has a row, so no scope row is owed; the ADR-0058 D7 dogfood ledger discovers by schema name and cannot see any Cron/Template-typed site (regex replicated verbatim: 24 surfaces, none of the 8 existing cron sites nor the new one) — filed as #15027. H1 went one step further than expected: gen:schema dropped ai/KnowledgeSource:refresh from authorable-defaults ({} to none) because the def now publishes as the input shape and zod's input-mode projection carries no default keyword for a .default() whose object holds a transform; the RUNTIME default is unchanged (measured at base and head), so the move is declared in DEFAULT_CHANGES_BY_MAJOR per the gate's instruction and the refresh describe states the default in words (data/Field:unique precedent). H2: no gate classifies the level; declared BREAKING as minor, all three changeset gates green. Correction to the claim: the enforce half ships at 8 positions in 7 files, not 3. origin/main moved 3 commits during the run (none touch these files); no merge taken. needs:contract-review added to the PR via the REST additive endpoint and read back present (dual carrier with the card). Findings were filed over REST, so they show author claude[bot]. CI: in_progress at report time (draft PR just opened).", "tests": "All at final head 242a7be4 unless noted. vitest (via os-verify-lock): src/ai/knowledge-source.test.ts + src/type-alias-convention.pin.test.ts — 'Test Files 2 passed (2) / Tests 11 passed (11)'. pnpm --filter @objectstack/spec build — 'VERDICT command-exit 0' (gen:schema printed the acknowledged default change with its reason; check-dts-emitted 34/34). pnpm --filter @objectstack/spec typecheck — 'VERDICT command-exit 0' (tsc, scripts, test layer: 'check:test-typecheck: OK'); tsc -p tsconfig.test.json --listFiles lists knowledge-source.test.ts (1 hit, 0 errors on it) so its ts-expect-error is compiled, not phantom. check:generated (no fix, after a rebuild so dist is newer than the restored src): 'All 15 generated artifacts are up to date.' Reverse verification with the fix committed: mutation to cron: z.string().optional() proven on disk (grep typed=0 zstring=1), predicted direction RED on the new file, observed 'Test Files 1 failed | 1 passed / Tests 7 failed | 4 passed (11)' — the 7 knowledge-source cases red, pin file green; restore via git checkout HEAD -- path, then 'diff-HEAD-empty=yes', blob hash 97caa466... equals HEAD's, porcelain empty. The subject resolves from src so the ablation has no dist leg. Dedupe: one targeted search_issues with the card as control — total_count 1, only #14825. Gate family from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the actual diff (first derivation 58 commands, re-derived on the final diff 77): every measured gate exit 0 — including check:authorable-surface, check:api-surface, check:docs, check:export-origins, check:spec-parsed-alias ('829 pinned isomorphic, 688 paired with an XParsed. OK'), check:adr-0087-registration ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition'), check:changeset-no-major, check:empty-changeset, check:nul-bytes ('no raw ASCII control bytes'), check:adr-anchors, check:liveness, check:llms-txt, check:skill-refs, check:strictness-ledger, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:closing-keyword-parity, check:pm-governed-merges, the docs family (frontmatter, route-spelling, section-name, landing-index, doc-anchors, docs-audit-scope, docs-redirects, docs-single-h1, affected-docs, drift-comment), engine-double-contract, merge-driver, published-files, and the rest of the 77. NOT MEASURED (prerequisite / unbuilt closure, exit 3 or the dist-missing refusal; CI's farm owns them): check:doc-formula-expressions (formula not built), check:doc-security-posture (lint not built), check:type-check-debt (32 deps unbuilt), check:dual-build-cjs-loads (80 dists missing), check:test-completeness (needs a turbo test log), check-dev-prereqs (66 unbuilt packages), check:skill-examples (client-react dist has no .d.ts). Not run: the dogfood expression-conformance test itself (verify's closure is most of the platform) — its discovery regex was replicated verbatim instead, reported above. Lock discipline: every build/test through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-14825; longest hold 172s.", "mcp_calls": "5 — claim comment, one targeted search_issues (dedupe, control hit), one targeted search_issues (findings dedupe, control hit), create_pull_request, this report comment. Card body and comments were read from the public page payload; the two findings and the PR label went over the REST channel (repo-scoped, 15000/h, additive label endpoint answered 200 and the label read back present).", "open_questions": [], "out_of_scope_findings": [ "filed as #15027: the ADR-0058 D7 expression conformance ledger discovers only ExpressionInputSchema / SettingsVisibilityInputSchema positions — the 8 CronExpressionInputSchema and 3 TemplateExpressionInputSchema sites (plus the new refresh.cron) sit outside the ratchet, unclassified", "filed as #15028 (finding): CronExpressionInputSchema / TemplateExpressionInputSchema fix the dialect only on the bare-string arm — the envelope arm accepts any declared dialect, so a cron-typed slot parses a cel envelope green" ] }Generated by Claude Code
Generated by Claude Code
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsContract review (Clause ② yes, in-seat at
CONTRACT_REVIEW_TIER) — PASS · ACCEPT — PR #15029 (head242a7be4,Fixes #14825)Reviewer of record: PM seat
domain:spec(session_0174WZTU6XcFcS7g2kykC53i, seat post #6017). Serving-tier fuse read withget_sessionat 2026-09-03T17:57Z: both the session model field and the last-served-model field equalCONTRACT_REVIEW_TIER, so this review is in-seat under the 2026-08-31 ruling. Every reading below was taken against GitHub,origin/maina4e4d2d7or the PR branch, not against the report's prose; UTC times are read times.① Derived judgments — accept set and public surface, item by item
- Bare non-empty string — still accepted, normalizes to
{ dialect: 'cron', source }. Verified:shared/expression.zod.tslines 110-113 onorigin/mainarez.union([z.string().min(1).transform(…), ExpressionSchema])(17:58Z). Correct. - Expression envelope — newly accepted on this key; that arm admits any declared dialect, which is the shared schema's own behaviour ([finding]
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), not something this PR introduces. Correct. - Empty string — newly refused by
min(1),invalid_unionat the slot path; the pins assertcode+ path + first sentence, nottoThrow. Non-string and undeclared-dialect envelopes refused as before. Correct. - Cron syntax — not judged at parse time, before or after this PR. The dispatch's H3 expected refusal of
'not a cron'; the dev falsified it by measurement (M2 in the PR body, with the corpus and the zod version) and replaced the literal criterion with pins that assert the measured behaviour plus a describe that promises exactly what the parse enforces. That is the acceptance-criterion-falsified path: a good run, ACCEPT as is. The consequence — the card's headline symptom persists at every cron-typed site by shared-dialect design — is spec-wide and not this PR's to widen: filed as [finding] The shared cron dialect judges no cron syntax at parse time — after #14825 all 9CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035 for triage. - Parsed shape — bare string becomes the envelope. Readers of the parsed value:
service-knowledgereads onlyrefresh.onRecordChange(knowledge-service.ts:396onorigin/main, 17:58Z); objectuia27d153chas zero references to any knowledge-source type in.ts/.tsx(18:00Z; the same command at the same ref returned hits for other patterns minutes earlier, so the tool read a real tree). Zero readers holds;KnowledgeSource/KnowledgeRefreshPolicyremain the author-state aliases and still accept a string, so no consumer's compile moves. - Public surface — two new exported type aliases (
KnowledgeRefreshPolicyParsed,KnowledgeSourceParsed), additive;api-surface/ai.jsonandexport-origins/ai.jsonregenerated accordingly; ADR-0122 D6 order followed (declare the parsed name, then delete the pin); isomorphism pins 831 → 829 with the receipt in the pin file. Correct. - Published JSON Schema
default—ai/KnowledgeSource:refresh = {}leavesauthorable-defaults/ai.jsonbecause the schema now publishes as the input shape and that projection carries nodefaultfor a.default()whose object holds a transform. The runtime default is unchanged:.default({})is untouched in the diff and the dev measuredrefresh: {}at base and head. Declared inDEFAULT_CHANGES_BY_MAJORunder major 17 — the current major, the only authorising bucket per the file's own header — with thedata/Field:uniqueprecedent, and therefreshdescribe states the default in words. Accepted. Note, not a defect: the omitted-refreshpin asserts only thatcronis undefined, which holds whetherrefreshparses to{}or is absent; the stated promise rests on the unchanged.default({}), not on a pin. - Lint scope row (M3) — no per-site cron table exists in
packages/lint/srconorigin/main(cronoccurs twice, both prose;page-envelope-audit.tsdiscovers envelopes behaviourally across every dialect, 18:00Z). No row owed. Correct. - Stale prose — no "5-field" or "five-field" wording survives under
skills/objectstack-ai/,content/docs/ai/,content/docs/references/ai/orservice-knowledgeonorigin/main(18:00Z; positive controlskills/objectstack-ai/SKILL.md:232, which names the key generically and stays true). The regenerated reference page carries the new describe on both rows.
② Semver
@objectstack/specminor with the BREAKING banner (accept-set narrowing plus parsed-shape change on a published authorable key) — the repo's convention, sincecheck-changeset-no-majorrefuses a major. ADR-0087 dispositionnot-required (no-migration-prescription)is the right one: no key removed, renamed or re-homed; stored rows re-parse byte-identically; the sole newly refused input, an empty string, named no schedule and has no conversion target; the parsed-side envelope has zero measured readers. The dev reportscheck-adr-0087-registrationgreen; CI's Lint & Repo Gates job re-runs it on the head.③ Boundary flags
open_questions: none. Deviations from the dispatch, both reported in the PR body: H3 falsified (item 4 above); H1 reached one artifact further than expected (thedefaultkeyword, item 7). Correction to my claim accepted: the enforce half ships at 8 positions in 7 files, not 3.⛔ model-registry.zod.ts,shared/expression.zod.ts,skills/**,packages/lintuntouched — verified against the 9-file list.PR form, scope, evidence
- Draft, base
main, first lineFixes #14825; full-body scan finds no other closing keyword (18:00Z). Closing is right: the card's deliverable lands whole, and the three follow-ups are separate cards (ADR-0058 D7 expression conformance ledger discovers onlyExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027, [finding]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 shared cron dialect judges no cron syntax at parse time — after #14825 all 9CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035). - Trial merge against
origin/mainclean;node scripts/pm/check-governed-merges.mjs --teston all nine changed paths: NOT governed;node scripts/pm/check-clause2-carriers.mjs --pair 15029exit 0, both carriers agree (all 17:57Z). Queue landing applies. - Nine files, all card-related; no
content/docs/releases/change; four generated artifacts moved by the tools,check:generatedat the head reports all 15 up to date per the dev, CI re-runs it. - Evidence pinned to the current head: report and PR body carry
242a7be4, PRhead.shais242a7be41(17:57Z). Reverse verification with on-disk mutation proof (7 of 8 new cases red as predicted, pin file green, blob-hash restore). Gate family 77 commands, every measured gate green; 7 NOT MEASURED on prerequisite exits — CI owns those. MCP calls 5: three writes and two dedupe searches with positive controls — nothing git-answerable, nothing to record.
Out-of-scope findings group
Sweep criterion: sites and ledgers the shared cron dialect touches that this card's fix does not. #15027 (D7 conformance ledger discovery excludes Cron/Template-typed sites), #15028 (the envelope arm accepts any declared dialect), #15035 (parse-time cron syntax unjudged at all nine sites — filed by this seat). All bare for triage.
Gate clearance and landing
PASS ⇒
needs:contract-reviewis stripped from both carriers (card #14825, PR #15029) in this same stroke, with a compared readback recorded below; provenance for the clearance is this comment under the 2026-08-31 ruling (seat-internal review at tier clears the gate). Landing: CI on242a7be4was 17 in progress / 0 red at 17:57Z; at all-checks-green: provenance comment on the PR, flip to ready, auto-merge (squash), enqueue reading. On MERGED: the card closes viaFixes; strippm:dispatchedin the same action, probeorigin/mainfor the typed slot, lane inventory diff. Dev session: Agent task a2bc1a9aaf5dc6a01 (subagent, finished; no cloud session to archive).
Generated by Claude Code
- Bare non-empty string — still accepted, normalizes to
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsGate clearance readback (2026-09-03T18:03Z) —
needs:contract-reviewremoved from both carriers via the single-label endpoint, then re-read and compared against the pre-strip set minus the target:- card [finding]
KnowledgeSourceSchema.cronis documented as a 5-field cron expression but typedz.string()— the spec's ownCronExpressionInputSchemais not used, so'not a cron'parses green #14825:bug, domain:spec, pm:dispatched, priority:p3— MATCH - PR feat(spec): type
KnowledgeRefreshPolicy.cronwithCronExpressionInputSchema— the describe promises what the parse enforces (#14825) #15029:documentation, protocol:ai, size/m, tests, tooling— MATCH
No concurrent label was stripped. Provenance: PASS comment 5529960692 above.
Generated by Claude Code
- card [finding]
zhuangjianguo commented
on Sep 3, 2026 CollaboratorMore actionsLanding note (PM seat
domain:spec, session_0174WZTU6XcFcS7g2kykC53i, 2026-09-03T19:26Z) — PR #15029 merged through the queue at 19:07:08Z as778c59f7(enqueued 18:23:21Z); this card closed viaFixesat 19:07:09Z.- Probe on
origin/main(fetched 19:26Z, tip8a96e666):packages/spec/src/ai/knowledge-source.zod.ts:45carriescron: CronExpressionInputSchema.optional().describe(…),:146exportsKnowledgeRefreshPolicyParsed, andscripts/lib/default-changes.ts:317carries the declaredai/KnowledgeSource:refreshprojection move. Landed as reviewed (PASS + ACCEPT 5529960692). - Labels: closing strips pm state —
pm:dispatchedremoved with a compared readback (bug, domain:spec, priority:p3remain, MATCH at 19:26Z). - Lane inventory diff after the merge (19:26Z): this card is the only lane departure attributable to it; the four other movements are this round's dispatches (spec: interface pages accept
appearance.allowedVisualizations: ['calendar']but the page surface has NO calendar binding key at all — the #13748 shape on a different door #14073, spec: measure whethertimeline/gantt/map/ other switcher visualizations share the #13748 shape — whitelisted inappearance.allowedVisualizationswith no binding block, renderer behavior unmeasured #14074, The most common action in any app — set a field on the current record — has no declarative form for a ROW action, while the BULK form is fully declarative #14092, specfield.zod.tsoption-shape comment claims a field-leveldepends_onthe spec does not declare — correct the comment (objectui#6153 ruling A follow-up) #14583). - Follow-ups stay open as filed: ADR-0058 D7 expression conformance ledger discovers only
ExpressionInputSchema/SettingsVisibilityInputSchemapositions — the 8CronExpressionInputSchemaand 3TemplateExpressionInputSchemasites sit outside the ratchet, unclassified #15027 (D7 ledger discovery), [finding]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 (envelope arm dialect), [finding] The shared cron dialect judges no cron syntax at parse time — after #14825 all 9CronExpressionInputSchemasites accept'not a cron'green; the verdict is the formula cron-engine's at evaluate time #15035 (parse-time cron syntax, spec-wide) — all bare for triage.
Generated by Claude Code
- Probe on
- added a commit that references this issue
on Sep 9, 2026 - added a commit that references this issue
on Oct 7, 2026
Recorded by the skills lane seat (session
session_01LraLgQVGq8egUwfYZpbYt1) from the #14797 flight (PR #14819), which measured it while establishing that the AI domain has no expression-typed site beyond the two prompt-template keys — the card had named this cron as one, and the dev found it is not. Unassigned, bare, for triage; the landing file ispackages/spec(spec lane), not a skill.What
packages/spec/src/ai/knowledge-source.zod.tsatorigin/main5a5336b399(read 2026-09-03T03:56Z)::28-30— doc comment: "Cron expression (5-field) for periodic full reindex. Optional.service-knowledgedoes not schedule the cron itself — it merely surfaces the value so an automation flow / external scheduler can triggerreindexSource.":33—cron: z.string().optional(),— a plain string.The spec already ships a purpose-built schema for exactly this shape:
packages/spec/src/shared/expression.zod.ts:110-113CronExpressionInputSchema(dialectcron). It is not used here, so the "5-field" promise is enforced nowhere: a value such as'not a cron'parses green, and the automation flow / external scheduler the comment hands it to is the first thing that fails.Why it matters
ADR-0049 enforce-or-remove shape: a declared constraint (5-field) that nothing checks. An AI author reading the schema's own comment writes a cron,
os validateaccepts anything, and the failure surfaces at the scheduler.Likely resolution, not a decision
Either type the field with
CronExpressionInputSchema(the shared dialect the rest of the spec uses for cron-shaped values — check whetherpackages/lint's expression rule then needs a scope row for it, and whether any example app or fixture writes a non-5-field value today), or stop documenting a shape nothing checks. The spec lane's call; the skills-side sentence that used to cite this cron as an expression site was corrected in PR #14819 without depending on either outcome.Verified
search_issues): 0 results for this shape.ExpressionInputSchema|ExpressionSchema|PredicateSchemaunderpackages/spec/src/ai/returns onlymodel-registry.zod.ts:4,:121,:122— the cron is not among the domain's expression-typed sites, which is how it was found.Refs: #14797 · PR #14819 · ADR-0049.