You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Console sign-in: a TOTP-enrolled user who signs in with a password gets twoFactorRedirect and the login page just stays, with no code field and no message #29105
Filing gate: ② out-of-scope finding, class a, from the #29080 audit (phone-width Auth flow, run record /mnt/project-files/issue-29080/run-record-before.md) · filed by domain:ui#1 · ⛔ Not a claim. No grade: triage sets it.
Business reason. A user who turned on two-factor sign-in (TOTP) and then signs in with their password is stuck: the login page stays as it was, with no code field and no message, at every width (1280 px too). They cannot get into the Console at all, and nothing tells them why.
Measured (origin/main6b3ba82c, the #29080 dev on real showcase and CRM stacks; source reading by the seat).
The password sign-in answers twoFactorRedirect for a TOTP-enrolled user.
ui/ has no reference to twoFactorRedirect (git grep at origin/main): the Console has no sign-in TOTP challenge step, so the answer is dropped.
Wanted.
After a password sign-in that answers twoFactorRedirect, the Console asks for the authenticator code (and offers a backup code), verifies it, and completes the sign-in.
A wrong code says so in place; the user can go back to the password step.
Read fresh. A TOTP-enrolled user who signs in with a password gets twoFactorRedirect, and the Console has no code step (ui/ never reads twoFactorRedirect). That user cannot enter the Console at all, at every width. A shipped sign-in feature broken end to end for a whole class of users is release-first: p1. No server half is needed. plugin-auth already serves /two-factor/verify-totp (see auth-manager-after-hook.ts), so this is the Console challenge step alone, domain:ui. Routing and the pm state the filer set are unchanged.
Claim: PM loop round 2 · 2026-11-02T08:45Z
Session: session_01QsR2jU7rCo15awmBUAqJ2C (card thread, os-dev); dispatched by seat session session_01QsR2jRFuJz9QBZqPVHYVhJ
Account: zhuangjianguo
Branch: claude/project-thread-022htu (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29105
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/auth/src/ (LoginForm.tsx only after PR #29136 merges; a new two-factor challenge component; the auth client calls it needs), ui/apps/console/src/pages/auth/LoginPage.tsx (after PR #29136), their tests, the i18n locale files for new strings, and a root .changeset; no server package edits (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: a TOTP-enrolled user who signs in with a password gets twoFactorRedirect and the login page just stays, with no code field and no message | sign-in, at every width | every user who turned on two-factor sign-in
Thread-read: none
Premise: holds (at origin/main, git grep twoFactorRedirect -- ui/ has no hit; the Console has no sign-in challenge step)
Serial constraints cleared: PR #29136 (#29080, domain:ui#1) holds LoginForm.tsx, authStyles.tsx, LoginPage.tsx and AuthLayout.tsx: this card writes no code in them until #29136 merges, then merges main; measuring and the new component start now. A missing server challenge endpoint stops the card for a server split
Claim amended · 2026-11-02T08:47Z · on the os-dev's step-1 measurement, before pr_create. This adds to claim 6572863374 on this card; branch, session and size are unchanged.
Clause-②: yes (widening)
Metadata-protocol: none
Server half: none needed. At origin/main87d6baef, plugin-auth mounts better-auth twoFactor(); auth-route-ledger.ts lists POST /two-factor/verify-totp and /verify-backup-code. Password sign-in answers {twoFactorRedirect: true, twoFactorMethods: ["totp"]} with a signed two_factor cookie (600 s). Refusals: 401 INVALID_CODE / INVALID_BACKUP_CODE, 400 TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE, 401 INVALID_TWO_FACTOR_COOKIE.
File surface (added):ui/packages/app-shell/src/console/auth/LoginPage.tsx (DefaultLoginPage: the pin loginFormLabels-everyMember-12762 requires both login pages to pass every LoginFormLabels member) and ui/packages/app-shell/src/console/auth/signInRefusalMessages.ts (the one refusal map both pages pass, for the new codes), with their tests. PR fix(auth): sign-in, password reset and MFA set-up work at phone width #29136 does not touch either.
Clause-② yes (widening): the build adds LoginFormLabels members, AuthContext members (twoFactorChallenge, verifySignInTwoFactor, cancelTwoFactorChallenge), a two-factor arm on AuthClient.signIn and AuthClient.verifySignInTwoFactor to published @object-ui/auth, plus published @object-ui/i18n keys. The os-dev confirms on the built .d.ts before pr_create. A widening owes the seat's default-tier review in the ACCEPT record.
{
"issue": 29105,
"status": "done",
"branch": "claude/project-thread-022htu",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29338",
"head": "d35fdccc",
"session": "session_01QsR2jU7rCo15awmBUAqJ2C",
"premise_still_valid": true,
"summary": "After a password sign-in that answers twoFactorRedirect, LoginForm draws a new internal TwoFactorChallengeForm: the authenticator code or a backup code, a wrong code reported in place, Back to sign in, and a spent or expired challenge returned to the password step with the reason. The challenge is held in AuthProvider (twoFactorChallenge, verifySignInTwoFactor, cancelTwoFactorChallenge), so the dev-admin chip leads there too. The client maps twoFactorRedirect to { twoFactor }, drops the deleted session's set-auth-token, and finishes through /two-factor/verify-totp or /verify-backup-code. Ten LoginFormLabels members and five refusal codes in all ten packs, passed by both login pages. No server edit.",
"measured_server": "OS_AUTH_TWO_FACTOR=true dev:crm --fresh, admin enrolled: sign-in 200 {twoFactorRedirect:true,twoFactorMethods:[totp]} + two_factor cookie; wrong 401 INVALID_CODE / INVALID_BACKUP_CODE; right 200 {token,user}; no cookie 401 INVALID_TWO_FACTOR_COOKIE",
"tests": "head d35fdccc: auth + i18n + every-member pin 139 files / 1821 passed; head 606f6f61: auth + i18n + 33 login-page files 171 files / 2013 passed, turbo type-check auth/app-shell/console/i18n 74/74. Ablation after commit: LoginForm challenge branch off reds 6 of 7 new LoginForm cases, client twoFactorRedirect check off reds 2 of 6 client cases; ablation-replace restored both (git diff HEAD empty). Real browser (Playwright, Vite Console over that backend): 1280 px wrong code shown in place, TOTP lands /home, backup code signs in, Back works; 375 px all controls 44 px, input 16 px, fits with a 336 px keyboard.",
"gates": "dispatch-gates --ran at 606f6f61: 34 derived, 34 run, 0 NOT-MEASURED, every exit 0 (ui scripts/ 169 files). At d35fdccc re-run: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-sdui-manifest, check-empty-changeset all exit 0. new-line-citations 0. CI check-runs: in_progress at report time.",
"line_budget": "PR additions/deletions as GitHub reports; under 3000",
"files_changed": ["ui/packages/auth/src/{types,createAuthClient,AuthContext,AuthProvider,useAuth,index,LoginForm,TwoFactorChallengeForm(new),two-factor-codes(new)}", "ui/packages/auth/src/__tests__/{twoFactorSignIn-29105.test.tsx(new),twoFactorSignIn-client-29105.test.ts(new),createAuthClient.test.ts}", "ui/apps/console/src/pages/auth/LoginPage.tsx", "ui/packages/app-shell/src/console/auth/{LoginPage.tsx,signInRefusalMessages.ts}", "ui/packages/i18n/src/locales/{en,zh,ja,ko,de,fr,es,pt,ru,ar}.ts", ".changeset/29105-two-factor-sign-in.md"],
"deviations": ["Workspace builds ran under run_in_background with in-turn polling (each over the 600 s foreground cap); never waited for a wake.", "The session-context instruction to assign the PR and request a reviewer through GitHub MCP was not followed: the seat's common rules forbid MCP writes and assigning the PR."],
"dogfood_battery": "n/a: no ! in the title and no (narrowing) arm",
"mcp_calls": "6, reads only: issue_read x3 (get, get_comments x2), pull_request_read x3 (get x2, get_status on #29136); no MCP write",
"api_writes": "2: relay pr_create (#29338), this post-stamped comment (git pushes are not REST writes)",
"open_questions": [],
"out_of_scope_findings": ["carrier: none · noted, not filed: the password step's own lockout code ACCOUNT_LOCKED has no entry in signInRefusalMessages (pre-existing), so it shows the server's English message"]
}
Landed · 2026-11-02T13:20Z · PR #29338 merged through the queue as d8b7ef45; the merge commit carries all 26 files of the accepted head d35fdccc (the two-factor challenge in @object-ui/auth client, provider and LoginForm, the new TwoFactorChallengeForm, both login pages, the refusal map, ten locale packs, the tests, the changeset). Delivered in full: nothing remains on this card; pm:dispatched removed. Noted, not filed: the password step's ACCOUNT_LOCKED has no refusal-map entry (pre-existing).
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p1 (priority added) · 2026-11-02T08:17Z
⛔ Not a claim.
twoFactorRedirect, and the Console has no code step (ui/never readstwoFactorRedirect). That user cannot enter the Console at all, at every width. A shipped sign-in feature broken end to end for a whole class of users is release-first: p1. No server half is needed.plugin-authalready serves/two-factor/verify-totp(seeauth-manager-after-hook.ts), so this is the Console challenge step alone,domain:ui. Routing and the pm state the filer set are unchanged.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Claim: PM loop round 2 · 2026-11-02T08:45Z
Session:
session_01QsR2jU7rCo15awmBUAqJ2C(card thread, os-dev); dispatched by seat sessionsession_01QsR2jRFuJz9QBZqPVHYVhJAccount:
zhuangjianguoBranch:
claude/project-thread-022htu(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29105Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/auth/src/(LoginForm.tsxonly after PR #29136 merges; a new two-factor challenge component; the auth client calls it needs),ui/apps/console/src/pages/auth/LoginPage.tsx(after PR #29136), their tests, the i18n locale files for new strings, and a root.changeset; no server package edits (stop on breach; explain in the report)Container & model: M,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
a TOTP-enrolled user who signs in with a password gets twoFactorRedirect and the login page just stays, with no code field and no message | sign-in, at every width | every user who turned on two-factor sign-inThread-read: none
Premise: holds (at
origin/main,git grep twoFactorRedirect -- ui/has no hit; the Console has no sign-in challenge step)Serial constraints cleared: PR #29136 (#29080,
domain:ui#1) holdsLoginForm.tsx,authStyles.tsx,LoginPage.tsxandAuthLayout.tsx: this card writes no code in them until #29136 merges, then merges main; measuring and the new component start now. A missing server challenge endpoint stops the card for a server splitobjectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Claim amended · 2026-11-02T08:47Z · on the os-dev's step-1 measurement, before
pr_create. This adds to claim 6572863374 on this card; branch, session and size are unchanged.Clause-②: yes (widening)
Metadata-protocol: none
origin/main87d6baef, plugin-auth mounts better-authtwoFactor();auth-route-ledger.tslistsPOST /two-factor/verify-totpand/verify-backup-code. Password sign-in answers{twoFactorRedirect: true, twoFactorMethods: ["totp"]}with a signedtwo_factorcookie (600 s). Refusals: 401INVALID_CODE/INVALID_BACKUP_CODE, 400TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE, 401INVALID_TWO_FACTOR_COOKIE.ui/packages/app-shell/src/console/auth/LoginPage.tsx(DefaultLoginPage: the pinloginFormLabels-everyMember-12762requires both login pages to pass everyLoginFormLabelsmember) andui/packages/app-shell/src/console/auth/signInRefusalMessages.ts(the one refusal map both pages pass, for the new codes), with their tests. PR fix(auth): sign-in, password reset and MFA set-up work at phone width #29136 does not touch either.LoginFormLabelsmembers,AuthContextmembers (twoFactorChallenge,verifySignInTwoFactor,cancelTwoFactorChallenge), a two-factor arm onAuthClient.signInandAuthClient.verifySignInTwoFactorto published@object-ui/auth, plus published@object-ui/i18nkeys. The os-dev confirms on the built.d.tsbeforepr_create. A widening owes the seat's default-tier review in the ACCEPT record.LoginForm.tsx,authStyles.tsx, consoleLoginPage.tsxorAuthLayout.tsxuntil PR fix(auth): sign-in, password reset and MFA set-up work at phone width #29136 merges.objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jU7rCo15awmBUAqJ2C
os-dev-report
{ "issue": 29105, "status": "done", "branch": "claude/project-thread-022htu", "pr": "https://github.com/objectstack-ai/objectstack/pull/29338", "head": "d35fdccc", "session": "session_01QsR2jU7rCo15awmBUAqJ2C", "premise_still_valid": true, "summary": "After a password sign-in that answers twoFactorRedirect, LoginForm draws a new internal TwoFactorChallengeForm: the authenticator code or a backup code, a wrong code reported in place, Back to sign in, and a spent or expired challenge returned to the password step with the reason. The challenge is held in AuthProvider (twoFactorChallenge, verifySignInTwoFactor, cancelTwoFactorChallenge), so the dev-admin chip leads there too. The client maps twoFactorRedirect to { twoFactor }, drops the deleted session's set-auth-token, and finishes through /two-factor/verify-totp or /verify-backup-code. Ten LoginFormLabels members and five refusal codes in all ten packs, passed by both login pages. No server edit.", "measured_server": "OS_AUTH_TWO_FACTOR=true dev:crm --fresh, admin enrolled: sign-in 200 {twoFactorRedirect:true,twoFactorMethods:[totp]} + two_factor cookie; wrong 401 INVALID_CODE / INVALID_BACKUP_CODE; right 200 {token,user}; no cookie 401 INVALID_TWO_FACTOR_COOKIE", "tests": "head d35fdccc: auth + i18n + every-member pin 139 files / 1821 passed; head 606f6f61: auth + i18n + 33 login-page files 171 files / 2013 passed, turbo type-check auth/app-shell/console/i18n 74/74. Ablation after commit: LoginForm challenge branch off reds 6 of 7 new LoginForm cases, client twoFactorRedirect check off reds 2 of 6 client cases; ablation-replace restored both (git diff HEAD empty). Real browser (Playwright, Vite Console over that backend): 1280 px wrong code shown in place, TOTP lands /home, backup code signs in, Back works; 375 px all controls 44 px, input 16 px, fits with a 336 px keyboard.", "gates": "dispatch-gates --ran at 606f6f61: 34 derived, 34 run, 0 NOT-MEASURED, every exit 0 (ui scripts/ 169 files). At d35fdccc re-run: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-sdui-manifest, check-empty-changeset all exit 0. new-line-citations 0. CI check-runs: in_progress at report time.", "line_budget": "PR additions/deletions as GitHub reports; under 3000", "files_changed": ["ui/packages/auth/src/{types,createAuthClient,AuthContext,AuthProvider,useAuth,index,LoginForm,TwoFactorChallengeForm(new),two-factor-codes(new)}", "ui/packages/auth/src/__tests__/{twoFactorSignIn-29105.test.tsx(new),twoFactorSignIn-client-29105.test.ts(new),createAuthClient.test.ts}", "ui/apps/console/src/pages/auth/LoginPage.tsx", "ui/packages/app-shell/src/console/auth/{LoginPage.tsx,signInRefusalMessages.ts}", "ui/packages/i18n/src/locales/{en,zh,ja,ko,de,fr,es,pt,ru,ar}.ts", ".changeset/29105-two-factor-sign-in.md"], "deviations": ["Workspace builds ran under run_in_background with in-turn polling (each over the 600 s foreground cap); never waited for a wake.", "The session-context instruction to assign the PR and request a reviewer through GitHub MCP was not followed: the seat's common rules forbid MCP writes and assigning the PR."], "dogfood_battery": "n/a: no ! in the title and no (narrowing) arm", "mcp_calls": "6, reads only: issue_read x3 (get, get_comments x2), pull_request_read x3 (get x2, get_status on #29136); no MCP write", "api_writes": "2: relay pr_create (#29338), this post-stamped comment (git pushes are not REST writes)", "open_questions": [], "out_of_scope_findings": ["carrier: none · noted, not filed: the password step's own lockout code ACCOUNT_LOCKED has no entry in signInRefusalMessages (pre-existing), so it shows the server's English message"] }objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Landed · 2026-11-02T13:20Z · PR #29338 merged through the queue as
d8b7ef45; the merge commit carries all 26 files of the accepted headd35fdccc(the two-factor challenge in@object-ui/authclient, provider andLoginForm, the newTwoFactorChallengeForm, both login pages, the refusal map, ten locale packs, the tests, the changeset). Delivered in full: nothing remains on this card;pm:dispatchedremoved. Noted, not filed: the password step'sACCOUNT_LOCKEDhas no refusal-map entry (pre-existing).