Repository navigation
Commit 01df025
fix(lint): check the autonumber format the runtime resolves, not a local copy (#19787)
Fixes #19772
Clause-②: no
## The defect
`lintAutonumberFormats` chose the format it checks with its own copy of
the precedence rule. It took `autonumberFormat` whenever 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
through `FieldSchema` and 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 to `resolveAutonumberFormat`. That
function is already exported from `@objectstack/spec/data`, which the
lint already imported from. No export is added and `packages/spec` is
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:
- Skipping would need the lint to decide whether a key is declared. That
decision is the resolver's non-empty-string rule, so skipping would keep
a second copy of the rule this PR removes.
- Measured: `{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.
- If the contract default changes one day to a format that does draw a
finding, the runtime will mint with it too, so reporting it is correct.
The undeclared-field test below goes red on that day, so the change gets
noticed.
## What was measured: base `4b23e4fab5` against head `26c2a1d762`
- Premises, on the built dist at base. The lint on the document above
returns `[]`. The control `{ format: '{nope}{000}' }` returns
`autonumber-references-unknown-field`. `resolveAutonumberFormat` returns
`{nope}{000}`, and `missingFieldValues` on that format with an empty
record returns `['nope']`. `FieldSchema.safeParse` succeeds and keeps
both keys, and the lint on the parsed field is `[]` too (the rule's
input tier is `parsed`). 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.
- Base against head, over 121 spellings of the two keys (11 values each:
absent, `null`, `''`, a number, an object and six formats). 4 spellings
changed. All 4 are `autonumberFormat: ''` with a non-empty `format`, 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.
- Runtime publish gate (`runRuntimeAuthoringRules`, type `object`). Base
publishes the document with `errors: []`. Head refuses it with
`autonumber-references-unknown-field`. An undeclared field gets `[]` on
both.
## Tests
`lint-autonumber-formats.test.ts` gains five tests:
1. An empty canonical key lints the shorthand. This is the test written
to fail first.
2. A non-empty canonical key wins over the shorthand.
3. The shorthand is linted when the canonical key is absent.
4. With neither key declared, the findings equal those for the declared
default, and are `[]`.
5. A table over 25 spellings asserts the findings equal those for the
format the resolver returns.
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 empty
`git diff HEAD`.
- Revert the fix (put the old copy back): 2 red, test 1 and the table.
- Swap the precedence: 2 red, test 2 and the table.
- Ignore the shorthand: 3 red, tests 1 and 3 and the table.
- Make the default draw a finding: 1 red, test 4.
## Verification, at head `26c2a1d762`
- `@objectstack/lint` tests: 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
`--listFiles` shows 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, returns
`autonumber-references-unknown-field`.
- `dispatch-gates --repo objectstack-ai/objectstack`: 59 derived and 57
exited 0. Two are NOT MEASURED: `check:dual-build-cjs-loads` and
`check:type-check-debt` exit 3 until the whole workspace is built. CI
builds it and runs both. `--ran` with the exit codes recorded reconciles
59 derived, 57 run, 2 NOT MEASURED, 0 unrun. `check:lean-entry-closure`
first exited 3 as well. After building `@objectstack/objectql` it exited
0.
- Consumers. `@objectstack/cli` runs this rule through
`authoringRulesFor` in `compile.ts:382` and `validate.ts:331`, and the
rule table is unchanged. `test/authoring-rule-command-parity.test.ts`
exercises this rule through the lint dist. Its 10 in-process cases pass,
including `autonumber-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-protocol`
`protocol.runtime-authoring-gate.test.ts` passes 35/35.
- Hygiene: the three changed files, the commit messages and this body
have 0 control bytes and 0 model identifiers. Both positive controls
fired.
## Acceptance notes
- The sweep found no other copy of the autonumber precedence in
`packages/lint/src`. Among non-test sources,
`lint-autonumber-formats.ts` is the only file that names
`autonumberFormat`.
- A field whose `autonumberFormat` is `''` and whose `format` is broken
now fails `os validate`, `os build` and the runtime publish gate.
Before, it passed all three and then failed on every create. No field in
this repository's sources declares `autonumberFormat: ''`. Outside
tests, it appears only in changelog and comment prose.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent a690c49 commit 01df025
3 files changed
Lines changed: 84 additions & 5 deletions
File tree
- .changeset
- packages/lint/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
127 | 128 | | |
128 | 129 | | |
129 | 130 | | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
22 | 30 | | |
23 | 31 | | |
24 | | - | |
| 32 | + | |
25 | 33 | | |
26 | 34 | | |
27 | 35 | | |
| |||
57 | 65 | | |
58 | 66 | | |
59 | 67 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
| 68 | + | |
64 | 69 | | |
65 | 70 | | |
66 | 71 | | |
| |||
0 commit comments