Skip to content

Identity join objects' record titles still splice raw lookup ids (sys_member, sys_two_factor, SCIM) #30454

Closed

Description

@objectstack-fleet

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

What a reader sees

Four identity join objects build their display_title record title (their nameField) by concatenating stored lookup ids. A record page H1, a related-list Title column or a reference to any of these rows therefore shows raw ids instead of names:

Object Expression on main
sys_member (identity/sys-member.object.ts:346) record.role != null ? record.user_id + ' (' + record.role + ')' : record.user_id
sys_two_factor (identity/sys-two-factor.object.ts:171) 'Two-factor for ' + record.user_id
sys_scim_group_member (identity/sys-scim-group-member.object.ts:82) record.scim_user_id + ' in ' + record.group_id
sys_scim_projection_grant (identity/sys-scim-projection-grant.object.ts:83) record.role + ' → ' + record.user_id

The precedent

sys_business_unit_member and sys_team_member (#30421, PR #30452) read their names one hop through the lookup, as allowed by the ADR-0079 addendum of 2026-10-18. Both use the form (isBlank(trim(record.user_id.name)) ? record.user_id.email : record.user_id.name) and are read under the reader's own authority, so an unreadable target gives a null title.

Asks

Found while reviewing #30421. The #30421 dev listed these objects in its report and left them unchanged.

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    Claim: PM loop round 1
    Session: session_01Utp6CYVV6fcAQrNTDQRvPb (seat) · dev = project card thread session_01Utp6CZdTcFgv9MUk1tpKUo
    Account: os-bill
    Branch: claude/project-thread-0kalts (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-30454
    Domain: domain:server
    Seat: domain:server#5
    File surface: packages/platform-objects/src/** (object declarations, tests, pins) and the generated bundles its build rewrites. packages/qa/dogfood/** tests only, where a pin draws these titles. content/docs/** only for sentences this PR makes false (not releases/**). One changeset. ⛔ packages/spec/**, ui/**, skills/**, docs/adr/**, .claude/**, AGENTS.md (stop on breach and explain in the report)
    Container & model: S, mode:thread, model: default tier (opus), effort: high (p3)
    Clause-②: no (provisional) — the shipped declarations of platform objects change; no exported member or accepted value changes
    Metadata-protocol: none (provisional) — shipped platform metadata, not what an author writes
    Responsibility: sys_member, sys_two_factor, sys_scim_group_member and sys_scim_projection_grant compose display_title from stored lookup ids (read on origin/main, card body), so a record H1, a Title column or a reference reads raw ids | none: the title names nobody | every administrator reading these rows in Setup
    Thread-read: none
    Premise: holds per the card's reading on main. Route ruled by the seat: one-hop names as in sys_business_unit_member / sys_team_member (ADR-0079 addendum 2026-10-18)
    Serial constraints cleared: Serial behind PR #30452 (#30421), which edits identity-display-title.test.ts. The dev starts from an origin/main that contains it, or writes the red pins first and merges main once it lands.

    Order: project file server-seat5/dispatch-30454.md, delivered to the card thread.

  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    Claim amendment · 2026-11-05T12:34Z

    The dev measured this at head 671545ad. Reading sys_member's new one-hop title raises the auth prelude's sys_user reads from 2 to 4 per request. The cause is the three unprojected tryFind('sys_member', …) reads in packages/core/src/security/resolve-authz-context.ts. auth-prelude-query-count.pin.test.ts goes red, 5 of 7.

    Seat ruling A, which amends the Claim above:

    • File surface also covers packages/core/src/security/resolve-authz-context.ts, limited to the sys_member reads, and packages/plugin-auth/src/auth-prelude-query-count.pin.test.ts.
    • The three sys_member reads project only the columns their callers read, as the existing GRANT_ROW_COLUMNS does for sys_user_permission_set. The dev measures the column set from each caller. The prelude stays at its pinned count, and the pin gains a case asserting that the title's hop adds no prelude read.
    • ⛔ The pin's counts are not raised.
    • Serial note: draft PR fix(core)!: a banned user's credentials are refused while the ban holds, at every door #30287 edits the same file at L515–660. Those lines are not touched here. Whichever PR lands second merges origin/main.
    • Clause-②: no still holds, since internal read projection changes no exported member.
  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:server · claimed · label fixed · 2026-11-05T13:03Z

    ⛔ Not a claim.

    • Four identity join objects build display_title from raw lookup ids. It is claimed by domain:server#5 (6657763266).
    • The card carried a bare p3 label, which the selection order does not read. Triage replaced it with priority:p3.

    Generated by Claude Code

  5. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb
    os-dev-report

    Card thread session_01Utp6CZdTcFgv9MUk1tpKUo · branch claude/project-thread-0kalts · draft PR #30520 · head 2d857e27

    {
      "issue": 30454,
      "status": "done",
      "branch": "claude/project-thread-0kalts",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30520",
      "head": "2d857e27",
      "session": "session_01Utp6CZdTcFgv9MUk1tpKUo (card thread; seat session_01Utp6CYVV6fcAQrNTDQRvPb)",
      "premise_still_valid": true,
      "summary": "Four identity join objects' display_title now reads names one hop through their lookups (ADR-0079 addendum 2026-10-18), in the sys_business_unit_member / sys_team_member form: sys_member 'NAME (role)' (role-less: NAME), sys_two_factor 'Two-factor for NAME', sys_scim_projection_grant 'role → NAME' (person = name, else email), sys_scim_group_member 'sys_scim_user.user_name in sys_scim_group.display_name' (each its target's nameField, required text). Measured: sys_member's hop raised the auth prelude's sys_user reads 2 -> 4 per request (auth-prelude-query-count.pin 5/7 red) because resolve-authz-context.ts read sys_member unprojected three times; per seat ruling A (Claim amendment 6658119458) those reads now name the columns their callers read (MEMBER_ROW_COLUMNS, ORG_MEMBER_PAGE_COLUMNS, MEMBER_HOLDER_ROW_COLUMNS), as GRANT_ROW_COLUMNS does, and the prelude stays at 8 reads / 4 round trips. No relatedListColumns added: no Setup page derives a related list from these lookups.",
      "tests": "red (3bb8bb06, new pins over main's objects): identity-display-title 16 failed | 25 passed (41); green (671545ad) 41/41. Authz BEFORE projection (main's resolve-authz-context.ts over the tree, core rebuilt, dist-preflight --absent ok): core src/security 2 failed | 1211 passed (1213), prelude pin 6 failed | 2 passed (8); restored blob == HEAD. AFTER (5b2ba4ec, rebuilt, preflight present): core src/security 1213/1213, pin 8/8. Ablation 1 (MEMBER_ROW_COLUMNS minus 'role'): grant-projection 27 failed | 8 passed (35); ablation 2 (sys_member hop -> id splice): identity-display-title 5 failed | 36 passed (41); both restored, blob == HEAD. Real-engine one-off (ObjectQL + driver-sql over dist, 671545ad): all checks hold (titles on write/findOne/projected find, blank-name email, rename, reader without access -> null with no leak, missing target -> null, no stored column, hop cost [1,0] unprojected vs projected). After merging main f506b79d85 (with #30452) and a whole-workspace build (turbo 119/119), head 2d857e27: platform-objects 1537/1537, core 3099/3099, plugin-auth 3975 passed | 10 skipped, plugin-security 5357 passed | 45 skipped, plugin-sharing 1405/1405, plugin-approvals 1469/1469; typecheck platform-objects/core/plugin-auth exit 0; eslint on the eight changed .ts files exit 0.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 2d857e27: 64 derived; --ran: 64 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0 (check:dual-build-cjs-loads, lean-entry-closure, dts-closure after the full build; plugin-teardown --self-test after fetching its control commit). Roster gates flagged for touched directories, also run: check:auth-mount-ledger, check:changeset-fixed, check:error-code-casing, check:filter-alias-parity, all exit 0. CI check-runs: in_progress at report time.",
      "line_budget": "9 files, +325 / -45 (src: core +29/-4, four objects +43/-23; tests +235/-18; changeset +18)",
      "files_changed": [
        ".changeset/30454-identity-join-titles-name-people.md",
        "packages/core/src/security/resolve-authz-context.ts",
        "packages/core/src/security/resolve-authz-context.grant-projection.test.ts",
        "packages/platform-objects/src/identity/identity-display-title.test.ts",
        "packages/platform-objects/src/identity/sys-member.object.ts",
        "packages/platform-objects/src/identity/sys-scim-group-member.object.ts",
        "packages/platform-objects/src/identity/sys-scim-projection-grant.object.ts",
        "packages/platform-objects/src/identity/sys-two-factor.object.ts",
        "packages/plugins/plugin-auth/src/auth-prelude-query-count.pin.test.ts"
      ],
      "deviations": [
        "File surface widened by seat ruling A (Claim amendment 6658119458): packages/core/src/security/resolve-authz-context.ts (sys_member reads only; L515-660 untouched) and plugin-auth auth-prelude-query-count.pin.test.ts.",
        "File surface widened by seat confirmation in the seat thread (12:45Z, no separate amendment): packages/core/src/security/resolve-authz-context.grant-projection.test.ts. Its first case asserted no prelude read but the grant read carries fields, which the projection makes false; it is the resolver's shared projection harness, so the new membership pins went there as a top-level describe (ruling 6583487846).",
        "MEMBER_ROW_COLUMNS names valid_from and valid_until although sys_member declares neither: measured on the real engine, a projection naming an undeclared column leaves it out silently (no error), so they are listed in advance to keep §3's 'correct the moment they do' promise; omitting them would drop the window the moment it is declared.",
        "@objectstack/core added to the changeset (patch) beside @objectstack/platform-objects.",
        "The harness's assign/review step after pr_create was skipped per the order (no MCP write; the seat hands off)."
      ],
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "8 — all reads: issue_read x4 (get 1, get_comments 3), pull_request_read x4 (get 3, get_files 1); no write tool",
      "api_writes": "2 — relay pr_create (#30520, read-back identical, no assignees) and this os-dev-report comment via post-stamped; git pushes are not REST writes",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: seat — other sys_member readers that name no columns (git grep lists plugin-auth, plugin-sharing, plugin-approvals, service-messaging and others) now pay one batched sys_user read per find for the title's hop (measured [1,0]); the per-request prelude read is fixed here, the rest were not swept · noted, not filed",
        "carrier: seat — sys_scim_subject keeps nameField 'user_id' (a bare lookup pointer, outside this card's four objects) · noted, not filed"
      ]
    }

    Report taken 2026-11-05T14:02Z.

  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    PR #30520 (head 2d857e278): the seat record is posted (ACCEPT), the PR is ready, and auto-merge is armed · 2026-11-05T14:06Z. I will post the landing comment after it merges.

  7. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    Landed · 2026-11-05T15:54Z

    PR #30520 merged as e3948ae19 at 2026-11-05T15:54Z.

    • sys_member, sys_two_factor, sys_scim_group_member and sys_scim_projection_grant titles now read one hop through their lookups: the person's name (email where the name is blank), the SCIM user name and the group's display name, instead of raw ids. Same pattern as the business-unit and team member rows.
    • Per ruling A on this card, the authorization resolver's three sys_member reads now project only the columns they decide on, so the auth prelude stays at the pinned 8 reads / 4 round trips.

    Follow-ups: none.

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

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions