Repository navigation
fix(rest): the import template stars a column only when the import refuses a blank in it - #20904
Conversation
…nsert-time default isTemplateRequired read only defaultValue, while the engine also fills a blank field from the option marked default: true. Mirror the engine's rule (templateInsertDefault) and pin the mirror against the real import door. Cap each dropdown's errorTitle at Excel's 32 characters. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
… cap Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
📓 Docs Drift Check7 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 — 15 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 32e100a77d354e23e4414d0c94e2572e3388f4ff && git checkout 32e100a77d354e23e4414d0c94e2572e3388f4ff
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 33b6e8bece79e70156dc2c1880813f8d88af61d4 daf0aa882a4ab74f46758f4100b85dc94478edad && git checkout -B drift-repro 33b6e8bece79e70156dc2c1880813f8d88af61d4 && git merge --no-ff daf0aa882a4ab74f46758f4100b85dc94478edad
node scripts/docs-audit/affected-docs.mjs --json 33b6e8bece79e70156dc2c1880813f8d88af61d4 |
Contract reviewServed-tier: PR #20904 (draft, ① Derived judgmentsThe predicate, judged case by case against the engine at this head.
What metadata cannot see (the converse). "Starred exactly when the import refuses a blank" holds in the starred ⇒ refused direction for every metadata-declared default; the refused ⇒ starred direction has four exceptions, none introduced by this PR: (a) a The parity test's power.
Instructions sheet, each sentence about starred columns against the new predicate.
Changeset PR body, remaining sentences. " Public surface. Three new exports and two new optional keys on ② Semver level
Clause-②: no ③ Boundary flagsDev report
Card thread: the machine-verification risks 2 (showcase grants nobody Check-runs on Implemented-by: VERDICT: PASS Adopted and posted by
Generated by Claude Code |
Part of #18386
Clause-②: no
The import template now stars a column when a blank in it is refused on insert, as far as the metadata can tell. Every default the metadata declares counts, while a
before*hook,requiredWhen, and a default that fails at insert time cannot be seen from it.isTemplateRequiredused to read onlydefaultValue. The engine also fills a blank field from the option markeddefault: true(ObjectQL.resolveOptionDefault), treatsdefaultValue: nullas no default, and never runsrequiredonsystem,readonlyorautonumberfields. Separately, each dropdown'serrorTitleis now cut to Excel's 32 characters; itserrortext was already cut to 255.Why the engine's rule is copied here, not called.
resolveOptionDefaultandapplyFieldDefaultsare private members ofObjectQL, and@objectstack/objectqlis only a devDependency of@objectstack/rest. SotemplateInsertDefaultcopies the rule, andimport-template-route.test.tschecks the copy against the engine. For 19 field shapes, the template's own header is starred exactly when a blank cell sent through the real import door is refused. When the blank is filled instead, the stored value is the default the copy predicts.Pins.
default: trueoption gets an unstarred header, and its instructions row saysNo. A blank cell imported through the xlsx import door stores the default.errorTitleof 32 characters.Ablation. With the fix committed, the old predicate (
def?.required === true && def.defaultValue === undefined) was put back throughscripts/ablation-replace.mjs: anchor 1 → 0, blob c5f1888b → 24d5a851.git diff HEADis empty.Local verification at daf0aa8 (after merging
main):@objectstack/restfull local test project: 238 files, 4664 passed, 106 skipped.typecheck: green, with 0 debt in the test layer.check:dual-build-cjs-loadsandcheck:type-check-debtboth need a whole-repo build (PREREQUISITE NOT MET)..tsfiles: 0 errors, 0 warnings. The changeset is outside eslint'sfilesglobs. The config enables no type-aware linting, so this diff cannot change the lint result of any file it does not touch.Acceptance notes
before*hook that fills it, and an expression default that fails to evaluate at insert time (the engine then leaves the field unset and logs a warning). The template counts every default an author declared, if it is not empty, as filling the field.main, nothing at runtime resolves apicklistreference intooptionsyet, so the template's object read and the engine both see no options, and they agree. When resolution lands, it has to reach both, or a picklist option markeddefault: truewould remove the star from a column the engine still refuses.Generated by Claude Code