Repository navigation
docs(adr-0079): addendum — a formula title reads one hop through a reference, and is not sortable or searchable - #24323
Conversation
…ference, and is not sortable or searchable Records ruling 6245544505 (letter A on objectstack-ai/cloud#2925) as it shipped in #24274 and #24307: the read path's one-hop preload under the reader's permissions, the lint's two refusals, the hook-body title accessor's channel, and the declared-unsortable answer with its remedy. One appended section and one header pointer line; nothing above edited. Claude-Session: https://claude.ai/code/session_01RVjnmZ6Jv6NcLDwow6RpNi Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #24197 (the body with its Check-runs on ① Derived judgmentsThe diff is two hunks in one governed file: one Each behavioural statement, judged against the code at this head:
② Semver levelDocs-only: one file under ③ Boundary flagsThe dev's report is 6249874213; the dispatch is 6249693922; the claim is 6249662765.
Implemented-by: VERDICT: FAIL One WRONG statement (①.10): the heading "Hook and action bodies" places |
ctx.title() is installed on the hook face alone (body-runner.ts buildTitleSurface; the action-face context carries no title), so the group heading no longer says "and action bodies". The provenance sentence now says what is on main after the two changes, since the sort and search refusals predate both. Claude-Session: https://claude.ai/code/session_01RVjnmZ6Jv6NcLDwow6RpNi Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Delta review on top of record 6250184223 (head What the delta is: one file, two hunks, three lines ( Check-runs on ① Derived judgments
1–9. Each RIGHT, carried from record 6250184223 by interdiff, not re-derived. The lines carrying items 1–4 (357–374), 5–8 (378–392), 9 (396–402) and the three qualification bullets (406–414) are byte-identical to Fence, quotation, exclusion. The fence holds: the diff adds and deletes nothing outside the two hunks; Provenance, Context, D1–D10, the status table, the open questions and the alternatives are untouched; the two qualifications (the Alternatives bullet's "can return and query"; D1's "real STORED") are still made in the addendum's own text. The ruling is quoted verbatim: lines 349–352, unwrapped, equal the card's ② Semver levelDocs-only: one file under ③ Boundary flagsThe dev's rework report is 6250259693; the seat's rework comment is 6250198140; the dispatch is 6249693922; the claim is 6249662765.
Implemented-by: VERDICT: PASS The one WRONG statement of record 6250184223 (①.10) is corrected: the heading places |
维护者速读 · Tier H · PR #24323(#24197 第三部分,ADR-0079 附录)改了什么
为什么改 风险与代价(含回滚) 席位意见 你要做的(一个动作) 席位核对 ·
|
| Item | Reading |
|---|---|
| Shape | One file, docs/adr/0079-record-display-name.md, +77 / −0: one header pointer line and one appended addendum. The seat read the net diff: no line above the addendum is edited. The rework interdiff is exactly two lines: the group heading now names hook bodies only, and the provenance sentence now says "what is on main after those two changes". The merge 73fd87521c changes nothing in the file and none of the files the addendum's statements rest on (it brings Hono adapter files and one changeset). |
| Review face | Governed rules text, CONTRACT_REVIEW_TIER. Record 6250184223 on da22e1db2a was FAIL on ①.10: the heading placed ctx.title() in action bodies. The seat verified that in code: buildTitleSurface is called only on the hook face (body-runner.ts), and quickjs-runner.ts installs ctx.title for hook bodies only. The delta record on this head is 6250527790, PASS. |
| Declaration | Clause-②: no, no arm. An ADR note records what shipped; it moves no accept set and no public surface. skip-changeset: docs-only, nothing published. |
| Prose | The seat read the addendum sentence by sentence against the record's per-statement verdicts. The ruling is quoted verbatim. Neither #24284 nor #24316 is named or described. "Every surface" is not claimed: two surfaces are named, the read path and hook bodies. |
| PR body | The seat re-worded one Summary line through the relay (issue_patch). The case-insensitive Part of #N reader matched "the last part of #24197" beside Fixes #24197. Part-of PR must not also close its card is green on this head. After record 6250527790 (③.3), the seat also corrected two stale phrases in the Summary and the verification lead-in: "hook and action bodies" now reads "hook bodies", and a note names the rework head. |
| Gates | Dev reports: 19 derived, 19 run, every exit 0, on both rounds. |
| CI | On this head, before the seat's second body edit: 39 check-runs, 31 success, 8 skipped, 0 failed. The skips are on the EXPECTED_SKIPS roster: path or opt-in skips (Build Core, Build Docs, Console Pin Gate, Dogfood Verify CLI, the Dogfood shards, Temporal Conformance, Packed-tarball smoke) and Check Changeset, which the skip-changeset label skips by design. The body edit re-fires the three claim and card guards; their earlier runs on this head succeeded. Lint & Repo Gates, TypeScript Type Check, every Test Core shard and Governed Surface Queue Guard are green. |
| Cross-lane | domain:skills (the governed surface): 6249668097 / 6249673718. No objection on the card at this read. |
Disposition:
open_questions: A, A, A, accepted as record 6250184223 answers them.- Name the two verified surfaces, not "every surface".
- Qualify D1's "STORED" in the addendum's own text.
- Leave out the two details that were not re-verified.
- Deviations: process only (the worktree, attribution and subscription notes), all accepted.
out_of_scope_findings: other title consumers (searchAllhit titles,plugin-approvals'recordTitleOfoverctx.previous) were not verified for a title over lookups. Noted on seat post [PM seat] domain:engine — 🟢 os-project-manager #6367 for the next engine or approvals touch. No defect is asserted.
Landing (Tier H): check-governed-merges.mjs --pr 24323 reads governed (docs/adr/**). In this same act the seat puts needs-user-decision on the PR and requests reviews from os-zhuang and hotlong. The seat never approves or merges. After a governed approver's APPROVED, the seat lands it: pre-landing checks, clear the label, ready, then auto-merge. Fixes #24197 then closes the card.
Generated by Claude Code
Fixes #24197
Clause-②: no (an ADR note records what shipped; it moves no accept set and no public surface. The governed path owes one
CONTRACT_REVIEW_TIERreview, and the PR lands only on a governed approver's approval: Tier H.)Summary
The third and last of card #24197's three parts: the ruling's documentation leg, "written as an ADR-0079 note". One appended section in
docs/adr/0079-record-display-name.md,## Addendum (2026-10-18, #24197) — a formula title reads one hop through a reference, and is not sortable or searchable, plus one- **Addenda**:pointer line in the header block (the spelling ADR-0048 uses). Docs-only:skip-changeset.The addendum names ruling 6245544505 (letter A on objectstack-ai/cloud#2925) and the two PRs that shipped it, quotes the ruling verbatim, then states nine facts about
mainin three groups (read path, hook bodies, sort and search), and ends with what it qualifies above. It qualifies the## Alternatives consideredfirst bullet ("the server can return and query") and D1's "real STORED" in its own text; neither is edited. No edit to Provenance, Context, D1–D10, the status table, the open questions or the alternatives.Sources combined, as the dispatch named them: part 1's
adr_note_text(report 6247217617), amended with record 6247399985's ③.4 (userandtreeare references) and ③.8b (FLS on the carrying reference column); part 2'sadr_note_addendum(report 6248949940), amended with record 6249342777's ③.7 (the channel's identity, the channel that cannot read, FLS on the title field)."Shown on every surface" is not carried. The addendum names the two surfaces verified (the engine read path and the hook-body title accessor) and says it makes no statement about any other consumer of a record's title.
Verification record
Every statement checked in code on
mainat the mergeda22e1db(merge base2b7fc52c3). The rework head73fd87521c(report 6250259693) changes two addendum lines only, the hook-bodies heading and the provenance sentence, and none of the files below. (Note added by the seat.) The merge brought#24305,#24308,#24312;git diff --stat 2950277 da22e1dbover every file below is empty.d537a318band1bf5f6e17care ancestors of the head (git merge-base --is-ancestor: exit 0 for both).lookup/master_detail/user/tree; one batched read per reference field per page; the reader's own permissions; onfind,findOneand the insert and update responsespackages/objectql/src/engine.ts·resolveFormulaRelated→resolveOneHopRelated(…, 'caller')(hop query carries the caller'scontext, noisSystemspread;where id $in), call sites infind,findOneand the twohydrateWriteFormulascallers;packages/spec/src/data/field-value.zod.ts·REFERENCE_VALUE_TYPES= lookup, master_detail, user, tree, read throughreferenceTargetOfnullwith a reason, never a fallback, the reason not saying which; an empty reference is evaluatedengine.ts·formulaHopScope(ok: falsebefore evaluation;no-reference→ evaluated),formulaHopRefusal(docblock: never says which of not-found / not-readable held),applyFormulaPlannullwith the same reason; a related object's own hop is not served when read as a hoppackages/lint/src/validate-expressions.ts·judgeFieldFormulapassestraversalHydration: true;packages/formula/src/validate.ts·TRAVERSAL_CONFLICT_CODE(traversal-multi-hop,traversal-bare-and-traversed);engine.ts·hopConflictinapplyFormulaPlan,formulaHopReadsinresolveFormulaRelatedengine.ts·findevaluatesapplyFormulaPlanon the driver rows inside the operation;packages/plugins/plugin-security/src/security-plugin.ts·maskOperationResultmasksopCtx.resultafterwardsctx.title()over the in-flight state, one batched read per reference field on the body'sctx.apichannel, its identity (SYSTEM underrunAs: 'system') and transaction; null casespackages/runtime/src/sandbox/body-runner.ts·buildTitleSurface(hookRecordState,resolveServedRecordTitle);packages/runtime/src/sandbox/quickjs-runner.ts· title wire handstxState.api ?? ctx.api,value ?? null;engine.ts·ScopedContext.serveFormulaField(itsexecutionContext),ScopedContext.withRunAs('system'→sudo());packages/objectql/src/record-title.ts·resolveServedRecordTitlerunAs: 'user', no user) serves no hop: own-column title resolves, title over a referencenullengine.ts·withRunAs(nouserId→UnscopedHookApi);packages/objectql/src/hook-run-as.ts·UnscopedHookApi(noserveFormulaField);body-runner.ts·buildTitleSurfacefalls toresolveRecordTitlectx.title()engine.ts·ObjectQL.serveFormulaFieldreturnsscratch[field]with no mask;record-title.ts·resolveRecordTitle/resolveServedRecordTitleapply nonectx.title('FIELD')answers the title asfindOneserved it, the related record's own one-hop title includedbody-runner.ts·buildTitleSurface→source.object(…).findOne→servedRecordTitle400 INVALID_SORT; requestsearchFields400 INVALID_FIELD; asearchableFieldsentry dropped; never in the auto-default setengine.ts·assertOrderByIsMaterializable(formula inorderBy,findandfindOne);packages/metadata-protocol/src/protocol.ts·assertSearchFieldsAreSearchable(virtualbranch,INVALID_FIELD, 400);packages/spec/src/data/search-fields.ts·resolveSearchFieldResolution(declared entries filtered byisVirtualSearchField),SEARCH_VIRTUAL_TYPES= formula,autoDefaultFields(formula in neither positive list)The header pointer's fragment equals
github-slugger's slug of the new heading (computed, identical).Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) onda22e1db: 19 commands, the same 19 as on the pre-merge head2950277, and the dispatch's count of 19. All 19 ran onda22e1db, each exit captured before any pipe: 19 × exit 0, among thempnpm check:adr-anchors,node scripts/check-adr-links.mjs,node scripts/check-adr-symbol-anchors.mjs,pnpm check:doc-authoringandpnpm check:nul-bytes.--ran: "19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED (a DERIVED zero — all 19 recorded an exit code and none of them is 3)".On the first sweep (
2950277),pnpm --filter @objectstack/lint run check:doc-formula-expressionsexited 3 (PREREQUISITE NOT MET:@objectstack/formulaand@objectstack/lintnot built in the new worktree). Not counted as a measurement; afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lint(under the verify lock, exit 0) it exited 0, and 0 again onda22e1db.No package is touched, so no build closure, test suite or typecheck is owed. CI check-runs: not read at this writing; CI's verdict is the record.
Acceptance notes
维护者速读(草稿)
改了什么
在 ADR-0079(记录标题契约)末尾追加一节附录,并在文件头加一行指向它。附录只陈述已经上线的事实:公式字段可以穿过一个引用字段读取关联记录(一跳),读取时用读者自己的权限;看不到或不存在的关联记录让公式为空,不给回退值;两跳被 lint 拒绝;钩子里的
ctx.title()也按同样方式取标题;由多个查找字段拼成的标题不落库,所以不能按它排序、不能按它搜索。为什么改
裁决 6245544505(cloud#2925 字母 A)要求这条决定「写成 ADR-0079 的一则说明」。代码部分已由 #24274 和 #24307 落地,本 PR 是这张卡的最后一块:让 AI 和开发者读 ADR 时就知道「拼接标题」能显示、不能排序搜索,需要排序搜索就另存一个字段。
风险与代价(含回滚)
只改一份文档,不动代码、不动契约、不发布任何包。风险是文字与实现不符;每句话都在 PR 正文里对应到了具体文件和符号。回滚就是 revert 这一个提交。
席位意见
你要做的(一个动作)
读完附录,无异议就批准本 PR(Tier H,需维护者批准后由席位落地)。
Generated by Claude Code