Repository navigation
Description
director#1 · zhuangjianguo · session_01QsR2jV5sBSzCYNEDgpYLJw
Ruled: 6574398310 · letter B · 2026-11-02T10:06Z
Filing gate: ② maintainer decision (a product/security trade-off only the maintainer can make).
维护者速读
- 一句话:外部门户用户能看到自己的记录,但记录上指向他读不到的对象(品类、物料、内部员工)的关联字段只显示「受限记录」,供应商看不懂这条记录是哪个物料、找谁。
- 要你定:平台是否提供「引用目标不可读时仍显示其名称」的能力(按字段或按对象声明),还是维持现状,由应用自己加只读文本快照。
- 不做会怎样:每个有门户的应用都要为每个这样的关联字段加冗余文本字段和写入 flow,并且各自保证不泄露。
Problem
An external portal user reads a record they are allowed to see (an admission application, a corrective action request) and the record carries lookups to objects they must not read in full: srm_category, srm_material (product catalogue with no supplier field, so no membership-key RLS can narrow it), and sys_user (portal users may only read themselves). Since objectui#13335 these lookups render as "Restricted record". That is safe, but the supplier cannot tell which material a corrective action is about or which category they applied for.
Reach: measured on 18.1.0 in the hotsrm v0.6 portal exit test (local objectstack dev, two external portal users): the record page shows the raw id for these lookups on 18.1.0; current objectui shows "Restricted record" (objectui#13335).
Business reason
Who benefits: every app with a supplier/customer portal (hotsrm today). Scenario: the portal user must act on a record whose meaning lives in a lookup name (which material failed inspection, which category is under review). Without it: each app adds a read-only text snapshot per lookup plus a record_change flow to keep it, and each re-derives that it does not leak.
Options
| Option | What it does | Customer-visible effect | Effort |
|---|---|---|---|
| A. Keep as is | Lookups to unreadable targets stay "Restricted record" | Apps add snapshot text fields (hotsrm does this now: objectstack-ai/hotsrm#95) | none on platform |
| B. Declared name exposure | A field-level (or object-level) declaration that the target's name field may be shown to anyone who can read the referencing record, resolved elevated for the name only | Portal shows "PCB-1001 Main board" instead of "Restricted record"; nothing else of the target is readable | spec key + objectql/REST expand + renderer; no precedent |
| C. Denormalized name on read | The engine returns the target's name for every lookup regardless of target access | Simplest for apps, but silently leaks names (e.g. internal user names, other suppliers' material names) | small, but reverses the #13335 stance |
Recommendation: B, if the platform wants to own portal UX; otherwise A. C is not recommended: it leaks silently.
os-decision-facets
- ① Long-term soundness: B adds one declared key but removes a per-app pattern (snapshot field + sync flow) that every portal app would otherwise repeat.
- ② Real business pull: one app today (hotsrm v0.6 portal, 3 lookups found; more expected on portal pages for contracts and performance reviews).
- ③ AI-error resistance: B is an explicit, closed declaration per field; the failure mode of A is a missing snapshot (visible "Restricted record"), the failure mode of C is a silent leak.
- ④ Startup-stage restraint: A declares nothing on the platform; B is a permanent key.
Prior rulings read: "Restricted record" lookup, "lookup unreadable target" → objectui#13335 (closed, the current stance), objectui#13377 (closed, tooltip id); objectstack 0 direct hits; ADR none found; thread: none.
Who acts
Maintainer decides A/B. If B: a platform seat implements spec + objectql + objectui renderer. If A: nothing on the platform; hotsrm keeps its snapshot fields (objectstack-ai/hotsrm#95).
Dedupe: repo:objectstack-ai/objectstack is:issue lookup name unreadable target (156 hits incl. closed, none on this ask), … lookup display name restricted record permission (35, none), repo:objectstack-ai/objectui is:issue lookup restricted (closest: objectui#13335, #13377, both closed and about hiding, not exposing).
Filed from the hotsrm project (requested by 田騏). Session: session_017ihL2fedQbjJWWSwaVb31Q
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
domain:spec· p1 ·needs-user-decision· 2026-11-02T09:07Z⛔ Not a claim.
Generated by Claude Code
zhuangjianguo commentedon Nov 2, 2026
director#1 · zhuangjianguo · session_01QsR2jV5sBSzCYNEDgpYLJw
Brief-claimed-by: session_01QsR2jV5sBSzCYNEDgpYLJw · batch #454 · 2026-11-02T09:27Z
Director seat (第 56 场). This card is presented to the maintainer as batch #454 item 2 in the 总监决裁 thread; the ruling comment follows the maintainer's answer. ⛔ Not a claim.
Generated by Claude Code
zhuangjianguo commentedon Nov 2, 2026
director#1 · zhuangjianguo · session_01QsR2jV5sBSzCYNEDgpYLJw
Ruling: batch #454 item 2 · letter B · maintainer 「454 同意」 2026-11-02T10:04Z
What the ruling binds
lookupfield (name chosen by thedomain:specseat; meaning: "a principal who can read this record may see the target record's display name"). Declared per field by the application author; default off, so the objectui#13335 posture (「受限记录」) is unchanged for every field that does not declare it.nameFieldvalue and the id, nothing else.expandon that field returns the same (name + id), never other fields. The same key coverssys_usernames; no separate carve-out.Workload of the letter
M across three lanes:
domain:specdefines the key and its semantics (Metadata-protocol: added,Clause-②: yes (widening), director contract review before landing);domain:serverimplements the objectql/REST read and expand branch;domain:uithe text-only rendering. hotsrm removes its snapshot fields afterwards (that is the cloud/hotsrm lane's card, not this one).Execution
domain:specseat takes the card first; server and ui cards are filed by the spec seat once the key lands, citing this ruling.needs-user-decision→pm:queue) is the lane seat's.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
Claim: PM loop round 1 · 2026-11-02T12:19Z
Session:
session_01ADHkmPi25XXKx92Xmg4viS(seat); dev = card threadsession_01ADHkmQAss6gtFU3jRjApfzAccount:
os-steveBranch:
claude/project-thread-yert63Worktree: the card thread's own checkout of
objectstack-ai/objectstackDomain:
domain:specSeat:
domain:spec#1File surface:
packages/spec/src/data/field.zod.ts(the lookup key block besidereferenceVia) and its tests; regenerated references and surfaces thatcheck:generatedmoves; root.changeset/; not objectql, REST or ui (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (published schema face, a new key) before landing,effort: highClause-②: yes (widening)
Metadata-protocol: added
Responsibility:
packages/spec field.zod.ts lookup | objectui#13335 renders a lookup to an unreadable target as 「受限记录」 and no key lets the app declare the name readable | portal apps (hotsrm) that add a snapshot field and sync flow per lookupThread-read: 6574398310
Premise: holds (on origin/main
2f7977cdthe lookup field carries no name-exposure key; ruling B binds the semantics)Serial constraints cleared: none (no in-flight card of this seat holds
field.zod.ts; PR #29331 touches its alias tables only, disjoint lines — this PR lands after PR #29331 and merges main first)Ruling-ref: director 6574398310, letter B, maintainer 「454 同意」 — a per-field optional key on the referencing lookup, default off; name chosen by the spec seat:
referenceNameVisible. Name + id only, expand the same,sys_usercovered, text-only render. Target-object switch refused. Server and ui cards filed by this seat once the key lands.objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmQAss6gtFU3jRjApfz
os-dev-report
{ "issue": 29171, "status": "done", "branch": "claude/project-thread-yert63", "pr": "https://github.com/objectstack-ai/objectstack/pull/29379", "session": "session_01ADHkmQAss6gtFU3jRjApfz", "premise_still_valid": true, "summary": "FieldSchema gains referenceNameVisible (optional boolean) on lookup and user fields, carrying ruling 6574398310 letter B: TRUE declares that a reader of the record may see the target's nameField value and id even when it cannot read the target, nothing else ($expand the same, displayField does not widen it, text with no link). It is refused at parse on every other type (master_detail included, an off-type false included) with one custom issue naming the types. It is declared ahead of its producer (describe opens [PLANNED — not yet served], so liveness counts it planned). The four closed FieldSchema key censuses gained one entry each. Main was merged after #29331 landed (head 32bc8d47); the branch delta is unchanged (13 files, +293/-2 vs origin/main).", "tests": "New packages/spec/src/data/field-reference-name-visible.test.ts (19 tests). Head 32bc8d47 (after merging main), each through os-verify-lock (VERDICT command-exit 0): spec test 792/792 files, 23471 passed, 1 todo; spec typecheck exit 0; metadata-protocol test 304 passed + 4 skipped files, 22155 passed; driver-sql builtin-column-collision.test.ts 11/11; metadata-core object-schema-fls-references.test.ts 60/60. Pre-merge head 734d67a6: driver-sql full suite 236 files (3847 passed) + typecheck exit 0; metadata-core 22 files 885 passed + typecheck exit 0; lint 189 files 7830 passed. At 4c09cb03 four census pins were red as designed (driver-sql collision, metadata-core FLS, metadata-protocol field count 82 vs 81, spec form reconciliation); each got one entry. No ablation: new parse code with its own refusal and control pins; no existing tested behaviour changed.", "gates": "Head 32bc8d47: dispatch-gates --commands = 106 commands; all 106 exit 0 on the first pass; --ran: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero). check:generated exit 0 (14/14). Offline check-changeset-no-major --event on the PR body: exit 0, LEVEL AXIS accepted (yes (widening) + added, spec minor; driver-sql, metadata-core patch). eslint --no-inline-config on the 7 changed .ts files exit 0 (734d67a6). PR check-runs at 32bc8d47: 65 runs, every completed one success or skipped, including the ten required (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Lint UI skipped, Type Check UI, Test UI, Build & E2E UI, Live E2E UI), plus Check Changeset success, PR-body guards success, Spec property liveness success.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "6 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x3 (get #29331 x2, get_check_runs #29379); no write tools", "api_writes": "2 — relay pr_create (objectstack-ai/objectstack#29379, run 41453321390) and this os-dev-report comment via post-stamped.mjs; git push is not a REST write", "files_changed": [ "packages/spec/src/data/field.zod.ts", "packages/spec/src/data/field-reference-name-visible.test.ts", "packages/spec/src/data/field-customization.test.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts", "packages/drivers/driver-sql/src/builtin-column-collision.ts", "packages/metadata-core/src/object-schema-fls-references.ts", "packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts", "packages/spec/authorable-surface/data.json", "packages/spec/liveness/state-counts/field.md", "content/docs/references/data/field.mdx", "content/docs/references/data/object.mdx", "content/docs/references/system/migration.mdx", ".changeset/29171-lookup-reference-name-visible.md" ], "line_budget": "+293 / -2 vs origin/main (13 files), non-governed, size well under 3000", "deviations": [ "Claim file surface breach, forced-by-gate class (seat pre-accepted on the thread): driver-sql builtin-column-collision.ts, metadata-core object-schema-fls-references.ts, metadata-protocol protocol.meta-types-degenerate-derivation.test.ts, spec metadata-form-zod-reconciliation.test.ts, each one census entry; none in objectql, REST or ui. The seat amends the claim surface.", "Changeset also names @objectstack/driver-sql and @objectstack/metadata-core at patch (their published src moved by one table entry each).", "The delta said beside referenceFilters: that key is a retired tombstone on main, so the key sits beside referenceVia / referenceDeletedWhen.", "No field.form row: an omit ledger entry instead (lookupAutofill precedent); the ui follow-up adds the row with the renderer.", "PR body readings are at 734d67a6 (pre-merge); the 32bc8d47 readings are in this report. The seat may append them to the body.", "Harness-written attribution footer in the PR body uses the session URL; the commit trailers are model-free." ], "open_questions": [ { "question": "Which field types accept referenceNameVisible?", "options": ["A lookup + user (as built): user is the sys_user reference the ruling says the key covers; master_detail refused", "B lookup only: a user field would have to become lookup('sys_user') to declare it", "C lookup + user + master_detail"], "recommendation": "A, seat-accepted on the thread: the ruling's 'covers sys_user names, no separate carve-out' reads onto the dedicated sys_user type, and master_detail stays out because the ruling places the key on the referencing lookup (a later widening stays additive)." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: spec seat 1 · follow-up cards per the ruling, filed once this lands: server (objectql/REST read + $expand name-only elevated branch; drops the PLANNED marker and flips the pin in field-reference-name-visible.test.ts), ui (text-only render with no link + field form row, removing the reconciliation omit entry), hotsrm (remove snapshot fields, objectstack-ai/hotsrm#95) · noted, not filed" ] }Stamped 2026-11-02T14:46Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
Claim amendment on #29171 (amends claim 6576958258) · 2026-11-02T14:48Z
File surface widened, accepted, as declared in os-dev-report 6580079196 (forced-by-gate class: four closed
FieldSchemakey censuses each needed one entry for the new key):packages/drivers/driver-sql/src/builtin-column-collision.ts,packages/metadata-core/src/object-schema-fls-references.ts,packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts,packages/spec/src/system/metadata-form-zod-reconciliation.test.ts, plus generated references and liveness counts. None is objectql, REST or ui. The changeset names@objectstack/driver-sqland@objectstack/metadata-coreat patch. The open question is answered A, as agreed on the thread: the key is accepted onlookupanduser(the sys_user reference the ruling covers) and refused on every other type,master_detailincluded. Lines unchanged:Clause-②: yes (widening)·Metadata-protocol: added, spec minor.objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
Landed · 2026-11-02T15:46Z
9467a1b40e, through the merge queuecompletedbyFixes #29171pm:dispatchedis removed. The seat ACCEPTed it, with a claim amendment for four forced census entries, and a CONTRACT_REVIEW_TIER PASS on32bc8d47.referenceNameVisibleis declared onlookupanduserfields, default off and refused on every other type, and marked[PLANNED — not yet served].The follow-ups ruling 6574398310 assigns:
$expandbranch.The spec seat files both as cards citing the ruling; they cross-reference this card. The hotsrm snapshot-field removal belongs to the cloud/hotsrm lane, per the ruling, and is not filed here.