Repository navigation
test(rest): a lookup cell re-imports by a stored computed title through the real import door - #25087
Conversation
…the real import door The ruling's remedy for a composite title (a non-formula field with an expression, computed on write) resolves by its title on import, through the real POST /api/v1/data/:object/import route, a real ObjectQL engine and a sqlite :memory: driver. Two records whose computed titles collide answer reference_ambiguous. A formula-declared twin is the control leg. Claude-Session: https://claude.ai/code/session_01QK6pdbxKZiHdFApL8RYVh9 Co-authored-by: Claude <noreply@anthropic.com>
…port-title-close-out
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对Seat review ·
Generated by Claude Code |
Fixes #24981
Clause-②: no (pins, and at most one refusal sentence; no accept set moves, since the formula skip already landed in PR #25033)
This is the close-out of the card under the maintainer's ruling 6308445871 (letter A). The stored-title half landed in PR #25033 (
dfe60e3a0). This PR adds the pins the ruling's remedy was missing. It changes no runtime code and ships no changeset: the one new file is a test, and@objectstack/restpublishesdistonly.The ruling, items 1 to 4 (6308445871, verbatim)
displayField,resolveRefasks the target's declared title field by the one rule (resolveDisplayField) when that field is stored — landed in PR fix(core): the import resolves a lookup cell by the target object's stored title field #25033. A target whose declared title is a formula keeps answeringreference_not_found; no scan, no search heuristic, no platform-materialised title column (B-cap, C and D are not taken)."expression, written on write — the 18.0 round-2 sweep framework findings: a paid invoice line'samount/totalstays writable while its inputs are locked; an unevaluable hook condition answers 500 INTERNAL_ERROR;remote-tables?schema=never narrows on the SQL drivers #24619 shape), the remedy ADR-0079 item 9 already prescribes for sort and search."reference_not_foundsentence should say why a formula-titled target cannot match is the dev's to measure against the existing text and declare in the PR; ⛔ no new error code."The card's second Wanted bullet is withdrawn by ruling 6308445871. That bullet read: "A formula title is matched by the engine's own record-title resolution, not by a
whereon a column that does not exist." Nothing here matches a formula title. The fallback key list (name,title,label,full_name,email,username) and the exposure order from #22739 are unchanged.What this adds
packages/rest/src/import-reference-stored-computed-title.test.ts, two pins through the REAL import door (POST /api/v1/data/:object/import), a REAL ObjectQL engine and a sqlite:memory:driver:Acme (EMEA)againstnameField: 'display_title', a storedtextfield withexpressionrecord.name + " (" + record.region + ")"(not on the fallback list), andJohn Smithagainst a storedfull_name(on the list). The test reads the stored ids back. A control row names the same title over the same rows on a twin target whosedisplay_titleis aformula. It answersreference_not_found, so the stored declaration is what changes the answer.Acme (EMEA)answerreference_ambiguous, and neither is linked. A third record,Acme (APAC), links in the same import.No change to
import-runner.tswas needed (the dispatch's H2): the remedy is matched by the rung PR #25033 landed.Pins that already held (H1), named and not repeated
packages/core/src/utils/import-runner-reference-title.test.ts(PR #25033). It was run here atfe962effe3: 7 of 7 passed.Wren resolves through a stored displayNameField (the card's cell, as authored), and the same throughnameField. Each asserts the candidate orderid, the lookup'sdisplayField, then the title.the stored title stores the matched record's id: onegetMetaItemper target per import.a formula title (xt_contact.full_name)and(xt_account.display_title)is never asked by where:reference_not_found, nowhereover the formula, no recorded refusal.two records sharing a stored title answer the import's ambiguity refusal, and link neither.listis never read.Beside them,
packages/rest/src/import-integration.test.tspinsreference_ambiguousfor a duplicateddisplayFieldvalue.The gap: every one of those pins runs over a fake
findData, or over a target with no computed title. None had a target whose title is a stored computed field, and none had the engine compute a title on write before the import matched it. This PR's file closes that gap and only that gap. It does not repeat what the core suite pins closer to the code (that a formula is never asked bywhere, the candidate order, the metadata read count, the exposure check). The formula row in pin 1 is that file's control leg.The
reference_not_foundsentence (ruling item 4): unchangedMeasured at
fe962effe3. A scratch probe, never committed, calledrunImportfrom@objectstack/core's builtdist/against a target whosenameFieldis a formula:Formula Account: no record matches "Acme (EMEA)". zh-CN:Formula Account:未找到与“Acme (EMEA)”匹配的记录. Asked keys:id,name,title,label,full_name,email,username(the formula is skipped).import_reference_not_foundlives inpackages/spec/src/system/validation-message.ts: en:158, zh-CN:206, ja-JP:251, es-ES:296. It is rendered bycoerceErrorinpackages/core/src/utils/import-coerce.ts(:743,:759), not inimport-runner.ts.Declared: the sentence stays as it is. Reasons:
RefMatch, which@objectstack/coreexports (todayid,ambiguous,matchedField), so the coercion layer can learn why the resolver missed. Second, a new keyed sentence in four locales inpackages/spec. That is no new error code, but it is a public type change and a new message key, for a fact that holds for every row of every import against that target.validate-record-titlewording card that the seat files under this ruling.The code, the envelope and the i18n key are unchanged.
Ablation (one leg: the stored title made invisible to
resolveRef)The pins were committed before the mutation.
@objectstack/restresolves@objectstack/corethrough its builtdist/(an unaliased pair inKNOWN_UNALIASED_TEST_IMPORTS), so the leg rebuilt core and proved the mutation was indist/before reading the run.scripts/ablation-replace.mjs, inservesReferenceTarget. The linetitleField: resolveDisplayField(item),became a branch that always yieldsundefinedand carries the markerablation-24981-title-invisible. Anchor x1 to x0, replacement x0 to x1, blobd9b249d961d4to79957dd60e4f. Core build OK.ablation-dist-preflight: "marker present in 2 built files".Acme (EMEA)withreference_not_found, whileJohn Smithstays linked (itsfull_nameis on the fallback list). Pin 2 red:reference_not_foundin place ofreference_ambiguous, andAcme (APAC)unlinked.Tests 2 failed (2). Pin 1's diff shows only row 1 moved (ok: false, code: reference_not_found, field: account), with rows 2 and 3 unchanged. Pin 2's diff shows both rows atreference_not_found.d9b249d961d4) andgit diff HEADis empty". Core was rebuilt, thenablation-dist-preflight --absentread "marker absent from all 14 built files" and "working tree clean against HEAD". The pins re-ran green:Tests 2 passed (2). After the full build, the absent reading was taken again and passed.Tests and gates (head
fe962effe3)@objectstack/restdependency closure (turbo run build --filter=@objectstack/rest^...: "25 successful, 25 total"), then the full workspace (turbo run build --filter=!@objectstack/docs: "76 successful, 76 total"). Both ran underos-verify-lock,VERDICT command-exit 0.pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2 src/import-reference-stored-computed-title.test.ts: "Tests 2 passed (2)". The core title pins: "Tests 7 passed (7)".pnpm --filter @objectstack/rest typecheck: exit 0. The output reads "check:test-typecheck: OK — @objectstack/rest's test layer compiles under packages/rest/tsconfig.test.json; 0 file(s) / 0 error(s)". The new file is in that program:tsc --listFilesOnly -p tsconfig.test.jsoncounts it once.pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2: "Test Files 310 passed (310)", "Tests 6179 passed | 326 skipped (6505)".node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) derived 54 commands. Each exit code was captured before any pipe, and all 54 exited 0. Two of them first read exit 3 (PREREQUISITE NOT MET), so neither was a measurement.check:dual-build-cjs-loadshad no full build yet.check:type-check-debtdid not get the shared lock within its budget. Both were re-run on the same head after the full build. They read "112 published require entry point(s) across 69 package(s) load" and "1 ledger entr(ies) re-measured … none above its recorded number".--ranreconciliation: "54 derived famil(ies) accounted for — 54 run, 0 NOT-MEASURED (a DERIVED zero — all 54 recorded an exit code and none of them is 3)".check:platform-checklist --changed-fromwas not run: no exported symbol was renamed or removed. The repo-widepnpm lint,test:repo(the diff touches none of its files) and the full farm are CI's.Acceptance notes
packages/core, "through the real import path and a real engine, the way PR fix(core): the import resolves a lookup cell by the target object's stored title field #25033's pins do". Measured: PR fix(core): the import resolves a lookup cell by the target object's stored title field #25033's pins run over a fakefindData, andpackages/corecannot build a real engine (@objectstack/objectqldepends on@objectstack/core; core has no objectql dependency). The ruling's remedy is an engine behaviour (computed on write), so the end-to-end pins sit inpackages/rest, besideimport-integration.test.ts, which carries the door's other reference pins. A core-side fake pin of the same remedy would only repeat the core suite's stored-title pin with anexpressionkey added, so none was written.distonly.skip-changesetis the PM's decision.VP of Engineeringlinks a contact by its jobtitle). The ruling keeps the list unchanged.validate-record-titlewording are the seat's cards under the ruling. Neither is touched here.72518d20f5.origin/mainwas merged at688052e147before the gate battery and the full build.mainhas since moved 7 commits (objectql,specand others). The branch was not merged again, because a second merge would need a second battery and the diff overlaps none of those files. CI and the merge queue rebuild against currentmain.Generated by Claude Code