Repository navigation
Description
domain:devx#1 · os-warren · session_01JQwxbXSZs9PBUc2H8z6mtB
Filing gate: ① product defect with a named landing point and a reproduction (reach: the console's account-menu sign-out and the login form, measured 2/2 in fresh browser contexts with each identity proved by GET /auth/get-session).
Business reason: list state is per user on purpose. The checklist item says "a filter value can be sensitive" (a customer name, a deal size, a search term), and the per-user storage key exists to keep it from the next person on a shared machine. On a shared desk or kiosk browser, one person signs out and the next signs in on the page that opens; today that second person starts on the first person's filtered, searched and sorted list. This is a clause FAIL on a P1 item (records-forms.adhoc-filter-sort-builder A3 and N2). The rows shown are still read under the second user's own permissions, so this is a state carry-over, not an access bypass.
Reader who acts: the ui lane (ui/packages/app-shell). Either sign-out drops the list's URL-mirrored state when it composes the login redirect, or the login page drops uf__* parameters when the signing-in user differs from the user who left. Add a test for the sign-out → different-user sign-in path.
Dedupe: uf__filter 4 hits (#29115 relative dates, #28971 personal views, #24616 and #27438 run records); redirect sign out previous user 111 and login redirect list filter 137 (run records and seat posts; nearest #26618, closed, single logout to the cloud, not this). No card covers this.
QA-source: #29172 · records-forms.adhoc-filter-sort-builder · A3, N2
What happens
Account A works on a list with a toolbar filter, a search and a sort, then signs out from the account menu. The login page opens at /_console/login?redirect= plus A's list URL, including its uf__filter, uf__sort and uf__search parameters. Account B signs in on that page and lands on A's list, with A's conditions, sort and search term on B's toolbar. B's first data request carries them.
Measured at framework 7fa9873f (main, 2026-11-02) on a stock showcase boot, Console built from the same tree.
Reproduction
- Boot the showcase. Mint a member B (invite, then sign-up).
- In a fresh browser context, sign in through the form as admin (A). Open
/_console/apps/com.example.showcase/showcase_task/view/in_progress. - Add two filter-builder conditions (Estimate (h) greater than 10, Progress less than 80), search "pipeline", and sort Due Date newest first.
- Account menu → Sign out. The URL is
/_console/login?redirect=…uf__filter…uf__sort…uf__search…. - Sign in as B on that page.
- Expected: B's view opens with B's own state, or the view default. Nothing A authored should appear.
- Actual: B's toolbar shows A's two conditions, A's sort and "pipeline". B's first
GET /api/v1/data/showcase_taskcarriessearch=pipeline&sort=-due_date&filter=[…estimate_hours>10…progress<80…].
The user-keyed storage is clean. A's list-state:<A's id> key and sys_user_preference row belong to A, B has neither, and B gets 404 on A's preference row. The URL is the only vehicle.
Mechanism (read at 7fa9873)
ui/packages/app-shell/src/console/auth/LoginPage.tsxL95 reads?redirect=and follows it verbatim after any successful sign-in.- The signed-out guard composes that redirect from the full path and query. The list mirrors its state into the query (
ui/packages/app-shell/src/views/userFilterUrlState.ts:uf__filter,uf__search,uf__sort), so sign-out hands the user's list state to whoever signs in next.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: records-forms.adhoc-filter-sort-builder
Triage:
domain:ui· p1 ·pm:queue· 2026-11-02T09:06Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Claim: PM loop round 5 · 2026-11-02T09:11Z
Session:
cse_01QsR2jTDksad4P3chz5nFKY(card thread, os-dev); dispatched by seat sessionsession_01QsR2jV4dWohojjbTJTQq5mAccount:
zhuangjianguoBranch:
claude/project-thread-jfxofh(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29174Domain:
domain:uiSeat:
domain:ui#3File surface: the sign-out / signed-out-guard redirect composition (
ui/apps/console/src/components/ProtectedRoute.tsxand the account-menu sign-out it pairs with), and/orui/packages/app-shell/src/console/auth/LoginPage.tsxandui/apps/console/src/pages/auth/LoginPage.tsx, a small helper beside them that reuses theuf_naming fromui/packages/app-shell/src/views/userFilterUrlState.ts, their tests, and a changeset (stop on breach; explain in the report). The os-dev names the exact files in its report before editing anything else.Container & model: M,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility:
open — recorded at reviewThread-read: 6573237605
Premise: holds (at
origin/main692e0dc8:ui/packages/app-shell/src/console/auth/LoginPage.tsx:95reads?redirect=and follows it after sign-in, withisInvitationRedirectat :132;userFilterUrlState.ts:27-30mirrorsuf__filter/uf__search/uf__sort/uf__groupinto the query). Triage 6573237605 graded p1: A3 and N2 ofrecords-forms.adhoc-filter-sort-builderfail in RC run #29172.Serial constraints cleared: both LoginPage files are in the area of seat 1's PR #29136 (#29080, open) and seat 2's #29105 (TOTP sign-in step, claim amended 6572905231, no PR yet). This card keeps its LoginPage hunk small, merges main right before
pr_create, and whichever of #29174 and #29105 opens its PR second merges main first. Seat 3's #29118 editsui/apps/console/src/lib/auth-preflight.ts, which is not on this surface.objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jTDksad4P3chz5nFKY
os-dev-report
Reported 2026-11-02T11:28Z. Draft PR #29305 is open at head
a1fa3999; CI is the seat's from here.{ "issue": 29174, "status": "done", "branch": "claude/project-thread-jfxofh", "pr": "https://github.com/objectstack-ai/objectstack/pull/29305", "head": "a1fa3999", "session": "session_01QsR2jTDksad4P3chz5nFKY", "premise_still_valid": true, "summary": "The Console's signed-out guard (LoginRedirect in ProtectedRoute.tsx) is the only composer of a list URL into /login?redirect=, and the Console LoginPage follows it after any sign-in. The guard now drops every uf_* param from the return link and keeps the path and all other params (byte for byte when there was no list state). The list then seeds from the signing-in user's own stored state or the view default. Live on a showcase boot: after A signs out of a list carrying uf__filter/uf__search/uf__sort, the login URL is /_console/login?redirect=%2Fapps%2Fcom.example.showcase%2Fshowcase_task%2Fview%2Fin_progress, B lands on that path with no uf_, and B's first data request carries only the view filter status=in_progress; the control (verbatim redirect) carries A's search, sort and both conditions.", "files_changed": [ "ui/apps/console/src/components/ProtectedRoute.tsx (LoginRedirect body and doc comment; +12 -1)", "ui/packages/app-shell/src/views/userFilterUrlState.ts (new withoutListStateParams helper; +17)", "ui/packages/app-shell/src/index.ts (export of the helper; app-shell is private: true; +3)", "ui/apps/console/src/components/ProtectedRoute.signOutListState-29174.test.tsx (new, 238 lines, 4 cases)", "ui/packages/app-shell/src/views/ObjectView.listUrlState-11860.test.tsx (one new describe, 3 cases; +44 -1)", "ui/packages/app-shell/src/views/userFilterUrlState.withoutListState-29174.test.ts (new, 89 lines, 6 cases)", ".changeset/29174-sign-out-drops-list-state.md (new; @object-ui/app-shell patch, @object-ui/console patch; Clause-② no; Metadata-protocol none)", "content/docs/ui/views.mdx (one bullet; +2)" ], "line_budget": "diff vs origin/main 74a19dd8: 8 files, +420 -2 (source +32 -1; tests +371 -1; changeset 15; docs 2); under the 3000-line human-merge threshold", "declaration": "Clause-②: no · Metadata-protocol: none (copied from the claim into the PR body and the changeset)", "tests": "19 files / 194 passed at 97150043, 13da5477 and a1fa3999 (3 test files of this change plus 16 neighbours: Console App route tests, RootLandingRedirect, SetupRoute, StudioRoute x3, FormPage.predicateScope, AcceptInvitationRoute, i18nAfterSignIn, internalFormShell, ObjectView.ufViewSwitch, userFilterUrlState.listState, product-neutral ratchet). Ablation 1 (scripts/ablation-replace.mjs, verbatim redirect restored in LoginRedirect): console file 2 failed / 2 passed (sign-out path and cold uf link red; invitation and plain deep link green), restored blob 30df279f == HEAD. Ablation 2 (helper appends every param): 7 failed / 34 passed across the 3 files, restored blob 44703363 == HEAD. Type-check app-shell + console exit 0 at a1fa3999. Root eslint --no-inline-config on the 6 ts/tsx files: 0 errors, 8 pre-existing no-explicit-any warnings outside the hunks. turbo build 117/117. Real browser on a showcase boot at 97150043: see summary; same-user sign-out and sign-in got the user's own search back from per-user storage.", "gates": "dispatch-gates --commands: 66 derived, the same 66 at 13da5477 and a1fa3999; all 66 ran at both heads with exit 0 (check:type-check-debt and ui vitest run scripts/ under the lock; scripts suite 169 passed 2 skipped, 4900 tests). --ran at a1fa3999: 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN. check:new-line-citations 0 new. Body gates against the stored body: changeset-no-major 0, adr-0087-registration 0, empty-changeset 0, partof-closing-keyword 0, closing-keyword-parity --body binds only Fixes #29174 (exit 2 = one binding, as intended). PR check-runs: starting at report time (seat re-runs).", "merge_main": "merged origin/main twice: 2f29c84d (12 commits) then 74a19dd8 (26 commits); both clean, no regen pending; grep of the touched files found no parallel fix", "dogfood_battery": "n/a: no ! in the title and no (narrowing) arm", "deviations": [ "The helper lives in userFilterUrlState.ts and is exported from app-shell's index.ts, outside the claim's literal file list; named to the seat and accepted 09:18Z.", "Same-user session expiry also drops the uf_* state: the guard cannot tell sign-out from expiry without new @object-ui/auth state. The user's own return path is kept, and their filter panel, sort and search come back from their per-user storage; quick-filter chips and toolbar grouping fall back to the view default. Written into the PR body's acceptance notes.", "A signed-out visitor opening a shared list link lands on the list after sign-in on their own state, not the link's; an already signed-in visitor is unchanged.", "PR has no assignee and no reviewers (common rule 10)." ], "mcp_calls": "5 GitHub MCP reads, 0 writes (issue_read x3, list_pull_requests x2); session tools: get_session x1, subscribe_pr_activity x1 on #29305", "api_writes": "2: relay pr_create (objectstack#29305, read back 12088 bytes identical, assignees none) and this post-stamped comment on #29174. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "class: none (inconsistency) · app-shell's DefaultLoginPage ignores ?redirect= and navigates to '/', while the Console LoginPage, RegisterPage and VerifyEmailPromptPage follow it; a host that uses app-shell's login page loses the return path entirely · carrier: seat · noted, not filed" ] }objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Landed: PR #29305 merged at 2026-11-02T12:25Z as
62e2d070.Fixes #29174closed this card;pm:dispatchedremoved. Delivered: when a session ends, the sign-in page's return link keeps the list's path and other params but drops everyuf_*list state, so whoever signs in next opens their own stored state or the view's default, never the previous user's filter, search or sort. Nothing left on this card.