Skip to content

Console: a lookup to a record whose object the reader may not read shows a linked raw id, not the restricted-record placeholder #29641

Closed

Description

@objectstack-fleet

domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

Filing gate: ② out-of-scope finding, class a (a measured display defect), from #29462 (PR #29639; os-dev report 6591156319, /mnt/project-files/issue-29462/os-dev-report.md). Filed by domain:ui#3. ⛔ Not a claim. No grade: triage sets it.

Business reason. objectui#13335 gave a lookup to a record the reader may not open a "restricted record" placeholder, so the reader sees that the record exists but is not theirs to open. When the refusal comes from the target object's read gate (no read permission on the whole object), the lookup cell instead draws the raw id with a question mark, inside a link to a record page that will refuse them. That looks like broken data, and the link leads to an error page.

Observed by the #29462 os-dev, live on pnpm dev:crm. A member whose object gate refuses rnv_payroll opens an rnv_expense record whose lookup Payroll points at it. The lookup has no referenceNameVisible. The cell draws the unresolved face (id + "?", linked) instead of the placeholder. Screenshot: /mnt/project-files/issue-29462/shots/desktop-record.png, field "Payroll (no key)".

Cause, read from source at #29462 head 7fb3a29d, ui/packages/fields/src/index.tsx:

  • useLookupName (~L265) reports state: 'absent' only for a not-found answer.
  • LookupCellRenderer (~L2733) gives the restricted face only to an absent id (restrictedId).
  • An object-gate refusal answers the record read with 403. That is a failure, not absent, so the id falls through to unresolved: true.

Wanted. A 403 or permission-refused answer on the lookup's record read is drawn like a not-found: the restricted placeholder, with no link. A transient failure (network, 5xx) keeps today's unresolved face. Check whether the record read's error carries a status or code the cell can read (the data adapter's typed error). If not, say what it would need.

Tests.

  • A recording adapter answers the lookup's read with 403: the cell shows the placeholder with no anchor.
  • A 404 CONTROL is unchanged.
  • A 5xx CONTROL keeps the unresolved face.
  • The ablation goes red.
  • lookupCell.restrictedRecord-13335.test.tsx stays green.

Landing site: ui/packages/fields (useLookupName / LookupCellRenderer). Serialize after PR #29639 (#29462), which edits the same renderer.

Dedupe: is:issue restricted record placeholder 403, is:issue useLookupName 403, is:issue lookup unresolved object gate (only #29462 and QA runs).

Links back: #29462, PR #29639, objectui#13335.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · pm:queue · 2026-11-03T00:13Z

    ⛔ Not a claim.

    • Read fresh. Measured: when the refusal comes from the target object's read, a lookup to a record the reader may not read shows a linked raw id, not the "restricted record" placeholder. The reader then sees a clickable id that leads to a refusal. p2.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

    Claim: PM loop round 28 · 2026-11-03T00:51Z
    Session: session_01QsR2jRxjiKm4UH4VWn45pF (card thread, os-dev); dispatched by seat session session_01QsR2jV4dWohojjbTJTQq5m
    Account: zhuangjianguo
    Branch: claude/project-thread-98b1gk (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29641
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: ui/packages/fields/src/index.tsx (useLookupName and the lookup / user renderers' use of its answer), new test file(s) under ui/packages/fields/src/, and one changeset (@object-ui/fields: patch). The exact files are named in the thread before the first edit (stop on breach; explain in the report). Not the data adapter: findOne's answer and @object-ui/data-objectstack are unchanged.
    Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: open — recorded at review
    Thread-read: none
    Premise: holds as measured by the #29462 os-dev (/mnt/project-files/issue-29462/shots/desktop-record.png, field "Payroll (no key)"), and read from source at #29639's head: useLookupName catches every read failure as err, and only a null record (the adapter's 404) reads as absent.
    Serial constraints cleared: PR #29639 (#29462, this seat) edits the same renderer and is in the merge queue; the card thread reads only until it is on main and cuts its branch from a main that carries it. Seat 4 asked for this card at 00:49Z; this seat, the filer, kept it.

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jRxjiKm4UH4VWn45pF
    os-dev-report

    {
      "issue": 29641,
      "status": "done",
      "branch": "claude/project-thread-98b1gk",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29708",
      "head": "214f2289",
      "session": "session_01QsR2jRxjiKm4UH4VWn45pF",
      "premise_still_valid": true,
      "summary": "useLookupName (ui/packages/fields/src/index.tsx) records a read the viewer is refused (classifyLoadError from @object-ui/react answers forbidden: status 403 on httpStatus/status/statusCode, or code PERMISSION_DENIED/FORBIDDEN) as a new 'refused' cache state, answered as absent, so LookupCellRenderer draws the objectui#13335 restricted placeholder with no id and no link, read once. Every other failure stays 'err' (unresolved face). Measured live before the edit: the adapter's findOne throws a plain Error with own httpStatus 403, code PERMISSION_DENIED, details {error, code, object}, no status/statusCode; a missing id under the refused object answers the same 403. UserCellRenderer does not call useLookupName, so the user leg has nothing to change. Adapter and findOne untouched.",
      "tests": "New lookupCell.forbiddenRead-29641.test.tsx (12): measured 403, four single spellings, cache + no cross-key bleed (seat addition), multi-value first entry; CONTROLS null/404, 500+code, 503, network TypeError, 401. lookupCell* (11 files) + DetailSection.referenceNameVisible-29462: 12 files / 97 tests green on 214f2289; 13335 and 29462 unchanged. @object-ui/fields suite on 214f2289: 296 passed / 1 skipped files, 4449 tests. Ablations via scripts/ablation-replace.mjs (anchor x1 -> x0, restored, blob == HEAD 01aa3be2, git diff HEAD empty; fields tests import src, no dist leg): A1 catch records every throw as err: 7 failed / 14 passed; A2 refused entry no longer answers absent: 7 failed / 14 passed.",
      "gates": "dispatch-gates --commands derives 35 on 214f2289 (same 35 as on 647dc13a); all 35 exit 0 (33 unlocked loop; check:type-check-debt and pnpm --dir ui exec vitest run scripts/ 170 files under os-verify-lock); --ran: 35 derived, 35 run, 0 NOT-MEASURED. Same 35 exit 0 on 647dc13a. ui check-spec-symbol-derivation exit 0 (seat heads-up on PR #29623). turbo type-check @object-ui/fields exit 0 (48 tasks). eslint on the 2 touched ui files: 0 errors. PR-body gates on the sent body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword exit 0. CI check-runs: not yet read, the draft was just opened.",
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
      "line_budget": "3 files, +219 / -6: fields src +21/-6, new test +188, changeset +10",
      "files_changed": [
        ".changeset/29641-lookup-forbidden-read-restricted.md (new)",
        "ui/packages/fields/src/__tests__/lookupCell.forbiddenRead-29641.test.tsx (new)",
        "ui/packages/fields/src/index.tsx"
      ],
      "deviations": [
        "The pre-edit 403 measurement and the before/after shots ran on a backend built at 744211fc (before #29639 landed); the Console was the Vite source dev server on the branch tree (before leg at fc558923, after leg at 69f9b209, whose src equals the final head's for index.tsx). Shots, readings and scripts are in the project folder issue-29641/, not on GitHub.",
        "main moved after the first gate loop; merged it (214f2289) and re-ran the full 35, the fields suite and the lookup files on the final head. main has moved since (7 commits, none touching ui/packages/fields)."
      ],
      "mcp_calls": "3 GitHub MCP reads: pull_request_read (PR #29639), issue_read get + get_comments (#29641); no writes",
      "api_writes": "2 relay writes: fleet-write pr_create (#29708, read back identical, no assignees) and this post-stamped comment; plus git push of the branch",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

    Landed · 2026-11-03T02:50Z

    PR #29708 merged at 2026-11-03T02:49Z as b824a66c. Fixes #29641 closed this card, and pm:dispatched is removed. The seat's ACCEPT is 6593925376.

    Delivered: when the target object's read gate refuses the reader, a lookup now draws objectui#13335's "Restricted record" placeholder, with no id and no link. It used to draw the id inside a link to a page that refuses them. A refusal is a 403 or a PERMISSION_DENIED / FORBIDDEN code, read through classifyLoadError. Any other failure keeps the unresolved face. user cells are unchanged, because they never read the referenced person. Nothing is left on this card.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions