Skip to content

test(ui): live e2e signs in a read-not-write member and asserts the Console refuses coherently (#27552) - #27768

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-l13hln
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-l13hln

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

维护者速读

Fixes #27552

Clause-②: no
Metadata-protocol: none

What changes

  • A second, non-admin identity for the live harness (ui/e2e/live/member.ts). signInMember(adminRequest, permissionSet) has the admin create a member holding one of the showcase's own permission sets through POST /api/v1/auth/admin/create-user (password, mustChangePassword: false, permissionSet), signs it in, and returns a storageState with the same two halves global-setup.ts writes for the admin (the API-origin cookie and the APP-origin auth-session-token). One member per set at a fixed address, so a re-run against a long-lived backend reuses it (USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL is accepted). The state is returned, not written to disk, so no path needs rooting.
  • A new live spec (ui/e2e/live/read-only-member-record.spec.ts). The member holds showcase_manager: on showcase_inquiry (OWD private) it reads org-wide and writes only its own rows. The admin creates an inquiry, so the member may read it and may not write it: a row verdict, the shape Record edit dialog: on a row the caller may read but not write (sharing / RLS / ownership), the fields are offered as editable and the save is refused whole — the dialog never asks whether the row is writable #27138 had. In order:
    1. premise through the server: the member's GET answers 200, its PATCH answers 403 PERMISSION_DENIED;
    2. as the member, ?form=ID opens the edit dialog with closed-affordance-notice visible, name/email/company/message disabled and no submit button in modal-form-footer; the record page under it has no Edit button and no inline-edit pencils; the member's browser sent no non-GET request to the object;
    3. control, as the admin: the same record page offers Edit and the pencils (the same locators step 2 finds absent), the same form has no notice and an enabled field, and a save through the form answers 2xx and the record reads back the new value.
  • Nightly only (ui/package.json): the spec joins test:e2e:live:nightly. Per the card's promotion rule it moves to test:e2e:live:ci only after 3/3 consecutive green on the live pair plus 5/5 of the enlarged allowlist, inside [devx] Live E2E UI skips every server-only PR: widen its gate to the server packages the Console runs against, and pin the spec-time budget #27550's 2-minute budget.

Tests

Live stack in this container: this tree's built CLI serving examples/app-showcase through ui/e2e/live/ci/start-backend.sh (objectstack dev --seed-admin --fresh, port 4127), the Console from ui/apps/console on vite (port 5327) proxying to it.

Spec runs: green 3/3 consecutive, about 38 s each on the vite dev server
run 1 exit 0    1 passed (39.9s)
run 2 exit 0    1 passed (39.3s)
run 3 exit 0    1 passed (38.3s)

Runs 2 and 3 took the member-already-exists path.

The spec can fail: the row write verdict removed, the spec goes red

Mutation through scripts/ablation-replace.mjs on ui/packages/permissions/src/RecordFieldAccessScope.tsx (vite serves source, so no build was needed): the anchor answer?.writable === false ? 'record-not-writable' replaced so the projection's writable: false is ignored, which is the #27138 shape (the dialog offers editable fields on a row the server will refuse).

ablation-replace: ok mutation landed: anchor 1 -> 0, blob 948fb29ceea0 -> 93c6ea3bfde0
  ✘ … › as the member: a read-only form with a named notice, no edit affordance, no write sent
    Error: expect(locator).toBeVisible() failed
    Locator: getByRole('dialog').getByTestId('closed-affordance-notice')
    Error: element(s) not found
ablation-replace: ok restored: blob == HEAD (948fb29ceea0) and `git diff HEAD` is empty
What was measured before writing the spec
  • POST /api/v1/auth/sign-up/email on the dev server answers 403 SELF_REGISTRATION_CLOSED (audience posture invite_only), so a sign-up step in the global setup cannot make the member; the admin's create-user door can.
  • As the showcase_manager member: GET of an admin-owned inquiry 200, PATCH 403 PERMISSION_DENIED ("You do not have access to this record…"); PATCH of a project 403 (object-level, from the everyone baseline's read-only grant).
  • Console as the member on that inquiry: header shows More actions and Timeline only (no Edit), the ⋯ menu holds Refresh / Add to favorites / Share (no Delete), no inline-edit pencils; ?form=ID draws the dialog read-only with "You don't have permission to edit this record. The fields are read-only." and Cancel / Close only. As the admin: Edit, six pencils, Delete in the menu, the dialog editable with Update.
  • pnpm --dir ui run type-check:e2e exit 0; tsc --listFiles includes both new files.
  • pnpm --dir ui exec vitest run scripts/__tests__/live-e2e-storage-state-roots-9519.test.ts scripts/__tests__/check-test-path-roots.test.ts scripts/__tests__/e2e-type-check.test.ts: 3 files, 43 tests passed.
  • pnpm --dir ui exec eslint e2e/live/member.ts e2e/live/read-only-member-record.spec.ts exit 0; node ui/scripts/check-test-path-roots.mjs OK.
Derived gates on head 3937f50: 31 derived, 31 run, all exit 0, plus check-sdui-manifest exit 0

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE: 31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN.

node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-osv-exemptions.mjs :: exit 0
node scripts/check-osv-exemptions.mjs --self-test :: exit 0
node scripts/osv-base-relative.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm check:changeset-fixed :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:manifest-repository-directory :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:override-consistency :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:turbo-task-graph :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-export-contract-resolve :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
pnpm check:workspace-manifest-cycles :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0

Acceptance notes


Generated by Claude Code

…onsole refuses coherently (#27552)

The live suite signed in only as the seeded admin, who passes every access
check, so the Console⇄server access class (#27138 and siblings) was
invisible to it. `ui/e2e/live/member.ts` gives the harness a second
identity: the admin creates a member holding one of the showcase's own
permission sets through `POST /auth/admin/create-user` (self sign-up is
closed on the dev server), and the member's storageState is handed to the
spec. The new spec, as a `showcase_manager` member on an admin-owned
inquiry, asserts the server's read/refuse premise, the read-only edit form
with its notice and no submit, no Edit and no inline pencils on the record
page, and no write sent; then the admin control edits the same record and
the save persists. It runs in `test:e2e:live:nightly` until the promotion
rule admits it to the CI lane.

Claude-Session: https://claude.ai/code/session_01Utp6CbDmdfNUqBtfbQtu5T
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file tests labels Oct 29, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 29, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

席位核对

ACCEPT · head 3937f501 · 2026-10-29T23:02Z

  • Shape: draft → main, Fixes #27552 on its own line. Clause-②: no and Metadata-protocol: none match the claim; test-only (new ui/e2e/live/member.ts and read-only-member-record.spec.ts, one item added to test:e2e:live:nightly in the private ui/package.json). No contract review owed.
  • Premise, measured: the dev server answers self sign-up with 403 SELF_REGISTRATION_CLOSED, so the admin makes the member through POST /auth/admin/create-user holding the showcase's own showcase_manager (inquiry readScope: org, writeScope: own); no seed or global-setup.ts change. The spec states its premise through the server first (member GET 200, PATCH 403 PERMISSION_DENIED), then as the member: ?form= shows closed-affordance-notice, the four fields disabled, no submit; the record page has no Edit and no inline pencils; no non-GET request left the member's browser. Admin control: the same locators are present and a save persists. Local live stack 3/3 green; ablating the row writable verdict turns it red. Diff read on 3937f501.
  • Gates (check-runs on the head): 44 success, 16 skipped; Live E2E UI green. Check Changeset failed on its first run because skip-changeset was missing; the card thread added the label afterwards through label-write.mjs (relay run 40968443411, read back), and the re-run on the same head is skipped. So the report's api_writes is 3, not 2. check-governed-merges --pr 27768: NOT governed.
  • Deviations, accepted: the PR's second line links the seat thread rather than the card thread; the maintainer quick-read sits above Fixes per the order's layout; the new helper member.ts is inside ui/e2e/live/. test(ui): promote the Studio live specs into Live E2E UI, with a stored-state re-read each #27770 edits the same :nightly line; the later lander merges main.
  • Landing: non-governed, checks green on the head, mergeable_state clean ⇒ the seat lands it (pr_ready + automerge_enable).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 23:03
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 23:03
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 29, 2026
…-l13hln

Resolves the ui/package.json conflict with #27770: its live:ci and nightly
lines kept, plus read-only-member-record.spec.ts in the nightly list.

Claude-Session: https://claude.ai/code/session_01Utp6CbDmdfNUqBtfbQtu5T
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

席位核对 · delta

ACCEPT · head c711ca02 · 2026-10-29T23:57Z


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit c5f4a06 Oct 30, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-l13hln branch October 30, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ui] Live E2E UI has no access scenario: a member who can read but not write a record must be refused coherently (needs a seeded non-admin user)

1 participant