Repository navigation
fix(lint): check the autonumber format the runtime resolves, not a local copy - #19787
Conversation
Red on this commit: with `autonumberFormat: ''` and a `format` shorthand, the lint takes the empty canonical key and checks nothing, while `resolveAutonumberFormat` (which the engine and the SQL driver mint through) treats the empty key as undeclared and renders the shorthand. Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude <noreply@anthropic.com>
…cal copy `lintAutonumberFormats` chose the format with its own precedence rule, which took `autonumberFormat: ''` as declared and skipped the field. The engine and the SQL driver resolve the same field to its `format` shorthand and mint through it, so a shorthand naming an unknown field passed the lint and then failed every create. The lint now asks `resolveAutonumberFormat`, the resolver both minting paths call, so there is no second copy of the rule to drift. A field declaring neither key is checked against the contract default it mints with; that default draws no finding. Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude <noreply@anthropic.com>
…nt-autonumber-resolver
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not 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
Coarse fallback — 4 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 7522d42541ce7297821279f46eeaf44562b1b206 && git checkout 7522d42541ce7297821279f46eeaf44562b1b206
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 531689c230af594abcefd2775ea076609272b12a 26c2a1d7623c16bf6817d2acd56612928be2ee64 && git checkout -B drift-repro 531689c230af594abcefd2775ea076609272b12a && git merge --no-ff 26c2a1d7623c16bf6817d2acd56612928be2ee64
node scripts/docs-audit/affected-docs.mjs --json 531689c230af594abcefd2775ea076609272b12a |
Fixes #19772
Clause-②: no
The defect
lintAutonumberFormatschose the format it checks with its own copy of the precedence rule. It tookautonumberFormatwhenever that was a string,''included, and skipped the field when the string was empty.resolveAutonumberFormat(@objectstack/spec/data) is what the engine (engine.ts:5051) and driver-sql (sql-driver.ts:10639) mint through, and it counts only a non-empty string as declared. So{ type: 'autonumber', autonumberFormat: '', format: '{nope}{000}' }parses throughFieldSchemaand passes the lint. At runtime it resolves to{nope}{000}, which names no field, so every create throws. The fix replaces the local rule with a call toresolveAutonumberFormat. That function is already exported from@objectstack/spec/data, which the lint already imported from. No export is added andpackages/specis not touched.Undeclared fields: the lint now checks the contract default
A field that declares neither key resolves to
DEFAULT_AUTONUMBER_FORMAT({0000}). The lint now checks that format instead of skipping the field. The reasons:{0000}parses to one sequence token, with no field references and no literal brace groups, so none of the four finding arms can fire. I tried six undeclared spellings ({},''on either key, both empty,null, a non-string). Base and head both return 0 findings for each, so no message names a format the author did not write.What was measured: base
4b23e4fab5against head26c2a1d762[]. The control{ format: '{nope}{000}' }returnsautonumber-references-unknown-field.resolveAutonumberFormatreturns{nope}{000}, andmissingFieldValueson that format with an empty record returns['nope'].FieldSchema.safeParsesucceeds and keeps both keys, and the lint on the parsed field is[]too (the rule's input tier isparsed). The engine's own suite,engine-autonumber-default-format.test.ts, pins the shorthand winning over an empty canonical key. It passes 11/11 locally.null,'', a number, an object and six formats). 4 spellings changed. All 4 areautonumberFormat: ''with a non-emptyformat, and each now draws the finding the shorthand draws on its own: unknown field, optional field, self-reference or unrecognised token. Head matches the resolver on all 121.runRuntimeAuthoringRules, typeobject). Base publishes the document witherrors: []. Head refuses it withautonumber-references-unknown-field. An undeclared field gets[]on both.Tests
lint-autonumber-formats.test.tsgains five tests:[].On
f395eafd6f(the tests alone, without the fix): 2 failed, 12 passed. The failures are test 1 and the table, and the table's only divergent spelling is{"autonumberFormat":"","format":"{nope}{000}"}. On head: 14/14 pass.Ablations start from the committed fix and go through
scripts/ablation-replace.mjs. In every leg the anchor went 1 to 0 and the blob changed. The restore was proven by blob == HEAD and an emptygit diff HEAD.Verification, at head
26c2a1d762@objectstack/linttests: base has 108 files and 4111 tests, head has 108 and 4116. The +5 are the new tests. Typecheck exits 0 on both. The test layer keeps its 6 ledgered errors in 2 other files, and--listFilesshows the edited test file is compiled. The build exits 0, and both the ESM and CJS dist carry the resolver call. The CJS build, loaded and run on the document, returnsautonumber-references-unknown-field.dispatch-gates --repo objectstack-ai/objectstack: 59 derived and 57 exited 0. Two are NOT MEASURED:check:dual-build-cjs-loadsandcheck:type-check-debtexit 3 until the whole workspace is built. CI builds it and runs both.--ranwith the exit codes recorded reconciles 59 derived, 57 run, 2 NOT MEASURED, 0 unrun.check:lean-entry-closurefirst exited 3 as well. After building@objectstack/objectqlit exited 0.@objectstack/cliruns this rule throughauthoringRulesForincompile.ts:382andvalidate.ts:331, and the rule table is unchanged.test/authoring-rule-command-parity.test.tsexercises this rule through the lint dist. Its 10 in-process cases pass, includingautonumber-references-unknown-field gates on all three commands. Its one spawned case, an approval-flow repro that does not touch this rule, was filtered out locally and is left to CI's integration tier.@objectstack/metadata-protocolprotocol.runtime-authoring-gate.test.tspasses 35/35.Acceptance notes
packages/lint/src. Among non-test sources,lint-autonumber-formats.tsis the only file that namesautonumberFormat.autonumberFormatis''and whoseformatis broken now failsos validate,os buildand the runtime publish gate. Before, it passed all three and then failed on every create. No field in this repository's sources declaresautonumberFormat: ''. Outside tests, it appears only in changelog and comment prose.Generated by Claude Code