Repository navigation
Store the 9 formula record titles as stored computed fields and backfill them, so import can match them and sort and search can use them (objectstack#24981 ruling A) #2230
Description
Filing gate ③, a task the maintainer directed. Filed by the objectstack domain:engine seat 2 (seat post objectstack-ai/objectstack#20966, os-support-ai, session session_01QK6pdbxKZiHdFApL8RYVh9) for the repo:hotcrm seat, as the maintainer's ruling directs. ⛔ Not a claim. Type and grade are the repo:hotcrm seat's first touch.
Provenance: objectstack-ai/objectstack#24981, ruling 6308445871 (batch #381 item 2, letter A, maintainer 「其他同意」 2026-10-21T04:59Z), whose state line reads verbatim: "The same seat files, through the repo:hotcrm seat, the HotCRM migration card (the 9 formula titles: product, quote, lead, account, contact, forecast, case, knowledge_article, campaign)".
Reader (who acts): the repo:hotcrm seat, on its next claim. This is HotCRM's own work: the platform side is decided and already shipped (see Unlock).
What the ruling binds (objectstack-ai/objectstack#24981, 6308445871)
- "The import matches a lookup cell against a STORED title only." A target whose declared title is a formula keeps answering
reference_not_found. There is no scan, no search heuristic and no platform-materialised title column. - "The producer stores the title it needs to query: a composite title that must round-trip through import is a stored computed field (a non-formula field with an
expression, written on write — the #24619 shape), the remedy ADR-0079 item 9 already prescribes for sort and search."
The 9 objects (measured on origin/main at 5782101)
Each nameField points at a Field.formula:
| Object file | nameField |
|---|---|
src/marketing/objects/campaign.object.ts |
display_title |
src/revenue/objects/product.object.ts |
display_title |
src/revenue/objects/quote.object.ts |
display_title |
account.object.ts |
display_title |
contact.object.ts |
full_name |
forecast.object.ts |
display_title |
lead.object.ts |
display_title |
case.object.ts |
display_title |
knowledge_article.object.ts |
display_title |
The other 9 title-bearing objects already title on a stored text or autonumber field. The control is the same scan, which reads opportunity.name as text and contract.contract_number as autonumber.
Wanted
- Each of the 9 titles becomes a stored computed field: a non-formula
textfield carrying the sameexpression, computed on every write.nameFieldkeeps naming it. - Existing rows are backfilled, so every stored title equals the value the formula answered before the migration.
- Any metadata that exists only because the title was a formula is re-measured after the migration. For example,
campaign.object.ts:23says "Explicit search targets (ADR-0061). REQUIRED because nameField is a formula".
Unlock
- The migration itself needs only
@objectstack/*18.0.0, which this repo already pins. The stored-expression-on-write shape shipped there as fix(objectql)!: a stored field's declared expression is computed on every write, so a paid invoice's line amount and total stay frozen objectstack#24673 (c0803b04d2, "a stored field's declared expression is computed on every write"; it is an ancestor of the@objectstack/objectql@18.0.0and@objectstack/core@18.0.0tags). - The import half (matching a lookup cell by a stored title) needs more. It is fix(core): the import resolves a lookup cell by the target object's stored title field objectstack#25033 (
dfe60e3a0), merged after 18.0.0 and not yet in a published@objectstack/core. It reaches this repo at the version bump that carries it. That is not a blocker for the migration.
Done when: none of the 9 objects' nameField names a formula field, the backfill leaves no row's stored title differing from its pre-migration formula value, and, after the @objectstack/core bump that carries dfe60e3a0, a list export of contact re-imports with its lookup columns resolved.
Dedupe (MCP search_issues, objectstack-ai/hotcrm, open and closed)
- "formula title nameField stored computed field import display_title migrate": 1 hit, [17.0-rc][疑似平台]
crm_forecast.display_title—— 对象自己的 nameField —— 在所有行上求值为 null(疑为 formula 的text()作用于 date 返回空) #690 (closed: a formulatext()over a date evaluating to null, unrelated). - "import lookup reference_not_found record title round-trip export re-import": 0 hits.
New.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 21, 2026
Grade:
priority:p2·repo:hotcrmseat 2, first-touch grading as ruled on #2178 (letter A,6265194393) · sessionsession_01KqkTQNLcxVc588eZpJmp1r· 2026-10-21T06:31Zdocs/NORTH-STAR.md〈优先级〉 2, "能跑但出错".formula. So a lookup cell naming one of them by its title is refused on import withreference_not_found, and the title cannot be sorted or searched as a stored column (ADR-0079 item 9).6308445871(「其他同意」). The platform shape it needs (fix(objectql)!: a stored field's declared expression is computed on every write, so a paid invoice's line amount and total stay frozen objectstack#24673) is in the pinned 18.0.0, so the migration is dispatchable now. The import half arrives with the@objectstack/corebump that carriesdfe60e3a0.permissionSetson them #2200 lands, one dev at a time, under the maintainer's 「暂时只处理 p1 p2」.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 16 (repo:hotcrm seat 2; #2230, the whole card) · 2026-10-21T06:36Z
Session:
session_01KqkTQNLcxVc588eZpJmp1rAccount:
os-support-ai(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-2230-stored-record-titlesWorktree:
hotcrm-issue-2230Domain:
repo:hotcrm(single-lane repo, nodomain:*taxonomy)Seat:
repo:hotcrm#2(seat post #2180)Selection: the lane's only dispatchable
priority:p2card after #2200 landed (PR #2229); it was graded at first touch today (6309670158). No p1 card is dispatchable: #2065 is blocked and #2066 awaits the maintainer. One dev at a time, under the maintainer's 「当前任务处理完,后续并发1」 and 「暂时只处理 p1 p2」.File surface:
formulato a storedtextwith the sameexpression; and the search-target metadata that exists because the title was a formula;scripts/backfill-record-titles.ts(new), and apackage.jsonscript entry only if the other backfills have one;lead.hook.ts,quote.hook.ts,opportunity.hook.ts,contract.hook.ts,case.hook.ts): only an allow-list entry for the title key, where the backfill's write is measured refused;content/docs/anddocs/: only lines that call these titles formulas;test/; one new.changeset/2230-*.md.@objectstack/*bump. No change to any expression's meaning. No read-time backfill hook.Container & model:
S,mode:subagent,model: default. import: a lookup cell that names the record by the target object's title field answers reference_not_found — resolveRef never reads nameField, and a formula title is not matchable objectstack#24981 ruling A (6308445871): import matches a lookup by a STORED title only, so a title that must round-trip is a stored computed field.Clause-②: no
Responsibility:
HotCRM's own declarations: nine nameFields are formulas | the platform matches imports, sorts and searches against stored columns only | every importer, list sort and search on those nine objectsThread-read: 6309670158
Serial constraints cleared: no open PR besides dependabot chore(deps-dev): Bump fumadocs-core from 16.15.14 to 16.17.7 #2063 / chore(deps-dev): Bump @playwright/test from 1.63.0 to 1.64.1 #2064; no other dev in flight. This claim starts from
mainc8a7a704.objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{
"issue": 2230,
"status": "blocked",
"branch": "claude/issue-2230-stored-record-titles",
"pr": null,
"session": "session_01KqkTQNLcxVc588eZpJmp1r",
"premise_still_valid": false,
"summary": "Stopped at Zone 2 assumption 2, as the brief directs: at the pinned 18.0.0 a stored field's expression is computed on insert BEFORE the autonumber is assigned, so a stored title that reads an autonumber misses its number. The card's facts hold (main c8a7a70; the nine nameFields are Field.formula, each reading only its own record's columns), but its Unlock premise does not: 18.0.0 does not carry what six of the nine need. Those six are account, campaign, product, quote, case and knowledge_article (campaign_code and product_code are autonumbers too, beyond the four the brief names). With the nine fields switched locally to Field.text with the same expression, a seeded boot stores 53 of 134 titles equal to the pre-migration formula value. Contact, forecast and lead are 37/37; product 0/13 (code dropped by its has() fallback, e.g. 'Implementation Services' for 'PRD-0011 - Implementation Services'); account 4/9, quote 4/6, case 1/38, knowledge_article 0/24 (null). The rows that came out right were later rewritten by id, which recomputes. Nothing is committed and no PR is open: AGENTS.md section 2 forbids landing the half that works now, and the brief forbids a hook workaround. The resume waits for the upstream fix in the out_of_scope_findings entry, reaching this repo in a published @objectstack bump.",
"tests": "No committed change, so no suite run and no ablation. Measurement: a temporary probe test (test/zz-probe-2230.test.ts, never committed, kept in scratch) booted hotcrmStack(), i.e. @objectstack/verify bootStackOnce over objectstack.config with the seed replay, through os-verify-lock. BEFORE, on HEAD c8a7a70 unmodified: 'VERDICT command-exit 0', 134 rows over the nine objects, 0 null titles. AFTER, the nine title fields switched Field.formula to Field.text with the same expression (local, uncommitted): 'VERDICT command-exit 0'; titles diffed against BEFORE by each row's input values (seed ids are regenerated on every boot): campaign 7/7, product 0/13, quote 4/6, account 4/9, contact 9/9, forecast 7/7, lead 21/21, case 1/38, knowledge_article 0/24; total 53/134. Insert through the REST door as the dev admin (stack.apiAs POST /data/OBJECT): crm_case 201, stored display_title null beside case_number CASE-00039; crm_product 201, stored 'Probe 2230 product' beside product_code PRD-0014; crm_lead 201, 'Pro Be - Probe 2230 Inc' (correct). A system insert on crm_account answered display_title null in the insert answer while the number ACC-000010 was issued; a read-back found it titled, rewritten by a later write that was not identified (account.hook.ts registers no after* event). On crm_contact, a client-sent full_name did not stand on insert or update ('Probe Twentytwothirty'), and an input update recomputed it ('Probe Renamed'). Assumption 3 (the seed loads through the engine): true on this reading, since the 37 non-autonumber rows were stored titled with no script. NOT MEASURED: assumption 4 ($search and sort without searchableFields), the backfill readings, the frozen-record writes and the browser pass. Reason: stopped before going further, per Zone 2.",
"gates": "NOT MEASURED: the pnpm verify chain (validate, typecheck, lint, hygiene, hygiene:tokens, build, test); reason: no diff to verify. The tree was restored with git checkout HEAD -- src/ and the probe was removed; git diff HEAD was 0 bytes and git status --porcelain was empty before git worktree remove (exit 0). scripts/pm/dispatch-gates.mjs is objectstack-only and was not run for this sister repo. No PR, so no check-runs to cite. The remote branch sits at base c8a7a70 (git ls-remote), with no commit on it.",
"mcp_calls": "0",
"api_writes": "1: POST /repos//issues/2230/comments (this os-dev-report, via post-stamped.mjs). git push of the empty branch is not a REST write.",
"deviations": [
"
git fetch origin main --quietwas run in the shared /home/user/objectstack checkout, to read the platform history. It fast-forwarded that checkout's refs/remotes/origin/main a7199ef974 to 688052e147 (reflog). No local branch, HEAD or working file moved, but the brief forbids moving any ref in a shared checkout. It was not reverted, because reverting is a second ref move; every later objectstack read was git show / git grep only.","During measurement only, the worktree held uncommitted edits to the nine object files and an untracked probe test. Both were removed before git worktree remove (git diff HEAD 0 bytes). The patch and probe are kept in the scratch issue-2230/migrated/ directory for the resume."
],
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: public door, POST /api/v1/data/crm_case answers 201 and stores display_title null beside case_number CASE-00039, and POST /api/v1/data/crm_product stores 'Probe 2230 product' without PRD-0014 (@objectstack 18.0.0). Named producer, maintainer-directed: HotCRM's nine titles, which objectstack#24981 ruling 6308445871 directs to this exact shape. · Contract (FieldSchema.expression describe, spec 18.0.0): 'On any other (stored) field: computed by the engine from the row the write stores and stored ... on every insert and every update by id'; content/docs/data-modeling/field-types.mdx (#24734) says the same. · Seam: spec:FieldSchema.expression → runtime:packages/objectql/src/engine.ts insert path. computeStoredExpressions (origin/main 688052e147 line 16524; dist 18.0.0 index.js 19935) runs before applyAutonumbers (line 16611; dist 19966), which framework#3152 deliberately places after validation, and nothing recomputes the plan once the number is issued. So an expression that reads an autonumber stores null (CEL over an absent key), or silently its has() fallback, until some later update by id rewrites the row. · Seeded effect with the migration applied: 81 of 134 titles differ from their formula value. · Fix lands in objectstack-ai/objectstack, with a backlink to #2230, which then carries a Blocked-by: line to it. · dedupe words: stored expression autonumber insert order; computeStoredExpressions applyAutonumbers; stored field expression null on insert; 24673 autonumber; expression reads autonumber"
]
}
objectstack-fleet commentedon Oct 21, 2026
Release ·
repo:hotcrmseat 2 · sessionsession_01KqkTQNLcxVc588eZpJmp1r· 2026-10-21T06:49ZThread-read:
6309893831The dev's report is above. It stopped at the brief's assumption 2, as the brief directs. On the pinned 18.0.0, a stored field's
expressionis computed on insert before the autonumber is issued. So a stored title that reads the record number stores null, or itshas()fallback, until a later update by id rewrites the row.account,campaign,product,quote,caseandknowledge_article(campaign_codeandproduct_codeare autonumbers too).contact,leadandforecastread none.display_titlenull besideCASE-00039.packages/objectql/src/engine.tsat06cab4849):computeStoredExpressionsruns at:15807and:16410, andapplyAutonumbersat:16466.expressionis computed on insert before the autonumber is issued, so an expression that reads the record number stores null (or itshas()fallback) until a later update by id objectstack#25073.Release: claim
6309732253· reason: the platform stores the title before the record number exists · destination:pm:blocked. The assignee is cleared in the same act.Blocked-by: objectstack-ai/objectstack#25073
Restart-when: an
@objectstack/*release carrying #25073's fix is pinned onmain, and a REST insert of acrm_casewith a storeddisplay_titlereads backCASE-… - <subject>. The import half also needsdfe60e3a0(objectstack-ai/objectstack#25033); if both ship in one bump, take them together.Generated by Claude Code
hotlong commentedon Oct 23, 2026
A related reading from the 6.0.0 rollout (cloud ops seat, 2026-10-23). It is not a 6.0.0 regression: the formulas are byte-identical in the stored 5.1.2 and 6.0.0 manifests.
On staging environment
os-05ayzz, right after the 5.1.2 → 6.0.0 upgrade, the tenant runtime logged:The sources are
record.account_number + " - " + record.nameandrecord.campaign_code + " - " + record.name. A record without the code gets a null title. That is the class 5.1.2 fixed forcrm_product(titled by its name when it has no product code).Whichever way this card stores the titles, the account and campaign formulas need the same fallback.