Skip to content

Reach the member page again on 18.2 with a record-header action - #175

Merged
os-warren merged 1 commit into
mainfrom
claude/project-thread-ad21nc
Nov 2, 2026
Merged

os-warren merged 1 commit into
mainfrom
claude/project-thread-ad21nc

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Requested by Warren · project thread

Fixes #113

What the 18.2 re-measure found

#113 was written on 17.2, where duly_member won the sys_user page by list order. On 18.2.0 (main ac5b5c4, live pnpm demo), /api/v1/meta/page still lists duly_member first. However, the console's usePageAssignment now picks the isDefault page first: candidates.find(p => defaultRecordPageObject(p) === object) ?? candidates[0]. So Duly > People > a person opens plugin-auth's sys_user_detail, and duly_member was unreachable by the plain record route. This was a regression from the 18 upgrade, not the latent risk the card described.

No authorable key outranks the platform page:

  • PageSchema has no priority key.
  • A second isDefault for sys_user refuses the boot with DEFAULT_RECORD_PAGE_CONFLICT (ruling A on objectstack#23015).
  • plugin-auth's opt-out exists only for its organization page.

Filed upstream as objectstack-ai/objectstack#28979. It asks for an app-scoped record page, because a deployment-wide sys_user default would also replace Setup > Users.

What this ships (seat Claim + amendments 1 and 2 on #113)

  • src/actions/member.actions.ts: duly_member_open. A type: 'url' action on sys_user that opens the explicit route /apps/ai.objectstack.duly/sys_user/record/${ctx.recordId}/page/duly_member. It is a labelled stopgap, to be deleted when #28979 lands. It is ungated, by seat ruling.
    • record_header only. On 18.2 a row (list_item) url action runs with no record in context: ${ctx.recordId}, ${ctx.record.id} and ${ctx.selection.ids} all came out empty, measured live. The row version would land on a broken /record/page/duly_member.
  • src/actions/index.ts: barrel entry. src/translations/zh-CN.ts: 打开成员页.
  • src/pages/member.page.ts: gap §5 is rewritten with the 18.2 measurement and #28979.
  • test/member-page.test.ts: mirrors the console's rule and pins that a person resolves to sys_user_detail today. The control is that the same rule without plugin-auth's page picks duly_member. It also pins the route the action builds, against manifest.id and the page name, and the record_header-only location. The card's original "first in list" pin was not written, because on 18.2 it would pass while the page was gone.
  • test/security.test.ts: the "every action declares a gate" walk now exempts exactly one shape: type: 'url', no registered handler, no operation/patch/body, and a target starting /apps/ai.objectstack.duly/. Controls one step off that shape must still fail: an external target, a protocol-relative target, another app's route, a template-led target, an operation, a patch, a body, a registered handler, and a non-url type.

Verification

  • pnpm validate 0 · pnpm typecheck 0 · pnpm test 1143/1143 · pnpm build 0
  • Browser, live pnpm demo on this branch, en-US and zh-CN: People > Elin Halvorsen > ⋯ shows Open member page / 打开成员页. Clicking it opens …/page/duly_member with the member page rendered. The People row menu no longer offers it. Screenshots are in the project folder duly-113-user-page/browser-pass/.

Generated by Claude Code

On @objectstack 18.2 the console opens the isDefault record page first, so
Duly > People > a person opens plugin-auth's sys_user_detail and duly_member
is unreachable by the plain record route. PageSchema has no priority key and
a second default refuses the boot, so nothing authorable outranks it (filed
as objectstack-ai/objectstack#28979).

- duly_member_open: a type 'url' action on sys_user that opens the explicit
  /page/duly_member route from the record's menu. A labelled stopgap.
  record_header only: 18.2 runs a row url action with no record in context.
- test/member-page.test.ts mirrors the console's resolution rule and pins
  that a person resolves to sys_user_detail today, with a control, plus the
  route the action builds.
- test/security.test.ts exempts exactly handler-less in-app navigation into
  Duly from the capability gate, with controls one step off that shape.
- member.page.ts gap §5 rewritten with the 18.2 measurement.

Fixes #113

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbY2bWFwe8GRYRBqzbg
@os-warren os-warren self-assigned this Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor

席位核对

ACCEPT — PR #175 at head efb3ee39f962843c16d2a1dc7434a81a8ac69374 (duly lane seat, session session_011HyoSXGRy6L13mYq1Hp4n1, 2026-11-02T05:54Z). Implemented by the #113 card thread session_01JQwxbY2bWFwe8GRYRBqzbg under Claim comment 6569926275 and amendments 6569994534 and 6570070207.

  • Shape: base main 98c188a, Fixes #113, Clause-②: no, Metadata-protocol: none.
  • Re-scope (seat): on 18.2 the card's premise flipped. The console opens the isDefault record page first, so Duly > People > a person opens plugin-auth's sys_user_detail, and duly_member was unreachable from the UI. That was a regression from the 18 upgrade, which the 18.2 browser pass missed. This PR restores a way in and pins the real resolution rule.
  • Files (API list): src/actions/member.actions.ts (new), src/actions/index.ts, src/translations/zh-CN.ts (objects.sys_user._actions only), src/pages/member.page.ts, test/member-page.test.ts, test/security.test.ts. This is inside the Claim and its two amendments. No governed path.
  • Gate: check-run verify success on efb3ee3, mergeable_state clean, and a local git merge-tree against main is clean.
  • Read in the diff:
  • Accepted deviation: list_item is dropped. On 18.2 a row url action interpolates ${ctx.recordId} to empty, so the row click led to a broken route. The seat filed this as A list_item url action runs with no record in context: ${ctx.recordId}, ${ctx.record.id} and ${ctx.selection.ids} interpolate to empty in a row menu objectstack#28995.
  • Browser (project files duly-113-user-page/browser-pass/, en-US and zh-CN): People > Elin Halvorsen > ⋯ shows Open member page / 打开成员页, and the click renders the member page.
  • Tests: 1143 locally.
  • Landing: direct-merge repo. The seat flips the PR ready and squash-merges it with expected_head_sha pinned.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 05:55
@os-warren
os-warren merged commit 43b20c0 into main Nov 2, 2026
1 check passed
@objectstack-fleet

Copy link
Copy Markdown
Contributor

Landed

Seat landing at 2026-11-02T05:56Z (duly lane seat, session session_011HyoSXGRy6L13mYq1Hp4n1). The seat flipped the PR ready through the relay, then squash-merged it with expected_head_sha efb3ee3 pinned.

probe reading
PR object merged: true, merge_commit_sha 43b20c0
origin/main by content 43b20c0, single parent 98c188a; dulyActions holds duly_member_open at record_header

State: #113 is closed completed by Fixes #113, and pm:dispatched is removed. The stopgap is deleted when objectstack-ai/objectstack#28979 lands, and list_item comes back when objectstack-ai/objectstack#28995 is fixed.


Generated by Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding: duly_member wins the sys_user page assignment over the platform's sys_user_detail by list position only

2 participants