Repository navigation
console(auth): LoginForm defaults registerUrl to '/register', so passing undefined on a disableSignUp boot still renders "Don't have an account? Sign up" #11634
Description
Activity
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsPath: sign-in and identity | identity-auth.self-signup-gate | P2
Triage: first grade —
bug·priority:p3·domain:ui·area:identity·pm:queue. A disabled sign-up renders no register linkTriage: lands in
packages/auth/src/LoginForm.tsx⇒domain:ui; rationale: the console already withholds the URL; the component's default restores it.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-05T02:07Z. ⛔ Not a claim, ⛔ not a dispatch.- Direction.
LoginForm'sregisterUrlloses its'/register'default (packages/auth/src/LoginForm.tsxabout:147), so an absent URL means no link. The console already passes nothing when sign-up is off.- The changeset states the behavior change for
@object-ui/authcallers that relied on the default. - A
nullarm is not added: a default that comes back onundefinedis the trap, and a second "off" value would keep it.
- The changeset states the behavior change for
- Why p3. A courtesy layer: the server refuses sign-up and
/registerbounces to login. The verifier graded it low.
Generated by Claude Code
- Direction.
- addedarea:identityLogin and identity — sign-up, sessions, organization membership, SSOLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Oct 5, 2026 objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsClaim: PM loop round 2
Session:session_015W8GBu6sBiqus2L2xjMsAL
Account:os-steve
Branch:claude/issue-11634-loginform-register-default
Worktree:objectui-issue-11634
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/auth/src/LoginForm.tsx(theregisterUrldefault, about:147, and its docblock, about:139), the tests beside it, anycontent/docs/**page that states the'/register'default, and.changeset/11634-*.md(stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier)
Clause-②: no
Thread-read: 5986895176
Serial constraints cleared:none— no open PR or in-flight claim touchespackages/auth/src/LoginForm.tsxor either consoleLoginPage.tsx(objectui#11600 touches onlypackages/auth/CHANGELOG.mdandpackage.json). objectui#11633 is inapps/console/src/pages/auth/VerifyEmailPage.tsx: a different file, and no pin of either card asserts the other's behaviour. Read 2026-10-05T07:55Z.Why
Clause-②: no: the'/register'default onLoginForm'sregisterUrlis dropped (triage5986895176), so an absent URL renders no link. The prop's type is unchanged and nothing is added to the accept set or the public surface. This narrows a default. For a caller that omitted the prop and relied on the default, it is a behaviour change, so the changeset isminorwith that change stated (objectui AGENTS.md: objectui's own breaking changes ship asminor).objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11634,
"status": "done",
"branch": "claude/issue-11634-loginform-register-default",
"pr": "#11652",
"session": "session_015W8GBu6sBiqus2L2xjMsAL (mode:subagent; this is the parent PM seat's session)",
"premise_still_valid": true,
"summary": "LoginForm's registerUrl no longer defaults to '/register'. The prop type is unchanged (registerUrl?: string) and no null arm is added. Leaving the prop out, or passing undefined, now renders no sign-up link. Both console login pages already pass undefined when /auth/config reports emailPassword.disableSignUp: true, so a deployment with sign-up off no longer offers 'Sign up'. Live counts with sign-up off: [1,1,1] before, [0,0,0] after; with sign-up on, still [1,1,1]. This run resumed from 050fc0d. It kept that commit after review and added the '@object-ui/auth' minor changeset with the caller migration sentence (1895044), then merged origin/main once at e398a54 (b906a13). Draft PR #11652 is assigned to os-steve.",
"tests": "All run by this run on final head b906a13 (sha quoted in the PR body). Exit codes were written to files before any pipe. vitest from the repo root: packages/auth/ exit 0, 28 files / 285 tests passed. apps/console/src/pages/auth/ exit 0, 9 files / 52 tests passed (includes the new LoginPage.sign-up-gate-11634.test.tsx). packages/app-shell/src/console/auth/ exit 0, 3 files / 14 tests passed. pnpm --filter @object-ui/auth type-check exit 0 (tsc --noEmit && tsc -p tsconfig.test.json); --listFiles on tsconfig.test.json counts LoginForm.test.tsx once; auth has no workspace deps, so the build closure is empty. pnpm --filter @object-ui/auth lint exit 0: 0 errors, 26 warnings; the only one in LoginForm.tsx, hasSocialProviders unused, is already on main. eslint --format json on the 3 changed ts/tsx files: 3 files, 0 errors, exit 0. Lint narrowing evidence: (1) population: eslint.config.js files globs /.{ts,tsx}, plus the test blocks for the two test files; (2) count: 3 from the json output; (3) invariance: eslint.config.js sets no parserOptions.project or projectService, so linting is not type-aware and this diff cannot change the verdict on any untouched file. Checks, all exit 0: check-changeset-presence ('3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check:changeset-claims ('No pending changeset names a file this change touches.'), check:pending-changeset-literals, check:new-line-citations ('0 new citation(s)'), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers/-inherit/-override-shape. NOT MEASURED: console type-check (apps/console tsc --noEmit, the only program that compiles the new console test). Reason: it resolves @object-ui/ through built dist types, so it needs the console's whole workspace build closure, which was not built; declared to CI. ABLATION, from the committed head, through objectstack scripts/ablation-replace.mjs (the anchor must hit, on-disk counts and blob hashes are checked) plus an own trap restore that compares against the HEAD blob. No dist step: both pin files reach packages/auth/src (one through a relative import, one through the vitest alias), and the console red in leg A shows it. Leg A put the '/register' default back (default-line count 0 → 1 during → 0 after): 3 failed / 25 passed; failing: 'renders no sign-up link when registerUrl is left out', '... is passed as undefined' (both: expected a href=/register ... to be null), and console 'renders no sign-up link when the server reports disableSignUp: true'. Leg B dropped !ssoEnforced from the sign-up row guard (guard count 1 → 0 → 1): 1 failed / 25 passed; failing: the re-judged enforced-mode pin. After each leg the blob was back to the HEAD blob c62cd03ca3a4 and git diff HEAD was empty. LIVE: objectstack main 27991556 from an own worktree (turbo build of example-showcase^... and cli..., exit 0), examples/app-showcase objectstack dev --seed-admin --fresh -p 4634. This worktree's console Vite dev server on 5634 was proxied to it. Fresh Chromium contexts (/opt/pw-browsers/chromium) loaded /login, waited for network idle, the identifier field and 4 s more, then counted a[href$="/register"]. With OS_DISABLE_SIGNUP=true (config emailPassword.disableSignUp: true): before ('/register' default put back on the head via ablation-replace) [1,1,1], each '/register "Sign up"'; after (head) [0,0,0], noAccountText 0. With sign-up on (disableSignUp: false), after: [1,1,1]. In every context the dev-admin banner rendered and the page's own /auth/config response carried the disableSignUp value. Both servers were started and stopped by this run (process groups killed, ports confirmed closed).",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "3 — each through the objectstack-fleet[bot] relay (POST /repos/objectstack-ai/objectstack/dispatches): pr_create → POST /repos/objectstack-ai/objectui/pulls (#11652, draft; read-back 9283 bytes sent = stored, identical); assign via label-write.mjs → POST /repos//issues/11652/assignees (os-steve; read back matched); comment via post-stamped.mjs → POST /repos//issues/11634/comments (this report). Plus one git push of the branch (not REST). Reads: gh api single REST reads of issue 11634, its comments and PR 11652.",
"open_questions": [],
"out_of_scope_findings": [
"class: none (dormant sibling, zero pull) · reach: none — no caller passes undefined to switch the link off · evidence: LoginForm's forgotPasswordUrl still defaults to '/forgot-password', the same undefined-brings-it-back shape; both login pages pass '/forgot-password' explicitly and AuthPublicConfig has no password-reset-off flag · carrier: 承接者:无 · noted in PR Acceptance notes, not filed · dedupe words: forgotPasswordUrl default, LoginForm undefined default, forgot password link",
"class: none (polish, read from code, not measured live) · evidence: the sign-up row renders outside LoginForm's config-loading gate. The console page starts signUpDisabled at false and DefaultLoginPage starts it at undefined, which falls to '/register', so on a sign-up-off boot both pages show the link until their config read resolves. DefaultLoginPage's comment says its undefined start avoids that flicker · carrier: 承接者:无 · noted in PR Acceptance notes, not filed · dedupe words: sign-up link flicker, config pending, DefaultLoginPage signUpDisabled"
],
"gates": [
{
"name": "vitest packages/auth/",
"command": "pnpm exec vitest run packages/auth/",
"exit": 0,
"reading": "28 files / 285 tests passed"
},
{
"name": "vitest console auth",
"command": "pnpm exec vitest run apps/console/src/pages/auth/",
"exit": 0,
"reading": "9 files / 52 tests passed"
},
{
"name": "vitest app-shell console/auth",
"command": "pnpm exec vitest run packages/app-shell/src/console/auth/",
"exit": 0,
"reading": "3 files / 14 tests passed"
},
{
"name": "auth type-check",
"command": "pnpm --filter @object-ui/auth type-check",
"exit": 0,
"reading": "closure empty (no workspace deps); test project lists LoginForm.test.tsx"
},
{
"name": "auth lint",
"command": "pnpm --filter @object-ui/auth lint",
"exit": 0,
"reading": "0 errors, 26 warnings (one in LoginForm.tsx, already on main)"
},
{
"name": "eslint changed files",
"command": "pnpm exec eslint --format json (3 changed ts/tsx files)",
"exit": 0,
"reading": "3 files, 0 errors"
},
{
"name": "changeset-presence",
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0
},
{
"name": "changeset-no-major",
"command": "node scripts/check-changeset-no-major.mjs",
"exit": 0
},
{
"name": "changeset-fixed",
"command": "node scripts/check-changeset-fixed.mjs",
"exit": 0
},
{
"name": "changeset-overwrite",
"command": "node scripts/check-changeset-overwrite.mjs",
"exit": 0
},
{
"name": "check:changeset-claims",
"command": "pnpm check:changeset-claims",
"exit": 0
},
{
"name": "check:pending-changeset-literals",
"command": "pnpm check:pending-changeset-literals",
"exit": 0
},
{
"name": "check:new-line-citations",
"command": "pnpm check:new-line-citations",
"exit": 0
},
{
"name": "check:control-bytes",
"command": "pnpm check:control-bytes",
"exit": 0
},
{
"name": "check:test-path-roots",
"command": "pnpm check:test-path-roots",
"exit": 0
},
{
"name": "check:vi-mock-specifiers",
"command": "pnpm check:vi-mock-specifiers",
"exit": 0
},
{
"name": "check:vi-mock-inherit",
"command": "pnpm check:vi-mock-inherit",
"exit": 0
},
{
"name": "check:vi-mock-override-shape",
"command": "pnpm check:vi-mock-override-shape",
"exit": 0
},
{
"name": "console type-check",
"command": "apps/console tsc --noEmit",
"exit": null,
"reading": "NOT MEASURED: needs the console's whole workspace dist build closure; declared to CI"
}
],
"line_budget": "n/a — no skills/ or governed ledger touched. Branch diff against origin/main (merge base e398a54): +126 / -6 over 4 files.",
"files_changed": [
".changeset/11634-loginform-register-default.md",
"apps/console/src/pages/auth/tests/LoginPage.sign-up-gate-11634.test.tsx",
"packages/auth/src/LoginForm.tsx",
"packages/auth/src/tests/LoginForm.test.tsx"
],
"deviations": [
"Resumption: resumed from head 050fc0d (1 commit beyond merge base 76993f8). Reviewed against the dispatch and kept unchanged: the default is dropped, the prop doc says so, there are LoginForm pins for left out / undefined / '/x', the enforced-mode pin is re-judged to pass registerUrl, and there is a console LoginPage pin. Added on top: 1895044 (the changeset the lost run had not written) and b906a13 (one merge of origin/main at e398a54). Before its first push I amended the merge commit's message locally to carry the model-free trailer pair. No force push, no rebase, no amend of a pushed commit. Every reading in this report was taken by this run.",
"File surface: the claim lists LoginForm.tsx, the tests beside it, content/docs pages and .changeset/11634-. The lost run also added apps/console/src/pages/auth/tests/LoginPage.sign-up-gate-11634.test.tsx, which is outside that list. The dispatch's Zone 3 asks for exactly this console pin, and it uses the existing mock-AuthClient seam (as LoginPage.dev-admin-hint.test.tsx does). No production file outside packages/auth changed.",
"Zone 3 'fix the docblock example': measured that LoginForm's @example already passes registerUrl="/register" explicitly, which stays correct without a default, so it is unchanged. The registerUrl prop doc comment carries the new semantics instead.",
"Zone 2 item 2, callers: neither console LoginPage relies on the default; both pass an explicit URL when sign-up is on, so neither was touched. Examples that render LoginForm with no registerUrl: packages/auth/README.md (AuthGuard fallback and the forms example), the AuthShell doc comment example, and skills/objectui/guides/auth-permissions.md (governed). None states or expects a sign-up link, so none needs the URL passed, and none is edited. No story, apps/site page, content/docs page or e2e spec renders LoginForm or states the default. Zone 2 item 4: no test pinned the default positively. The enforced-mode pin rendered with no registerUrl and asserted no 'Sign up'; it was re-judged (it now passes the URL) and named in the PR. Ablation leg B shows it now measures the !ssoEnforced guard.",
"Live route: /login on this worktree's Vite dev server (basename '/'), not /_console/login. It is the same LoginPage route; the backend's /_console serves the published console, not this source. The 'before' leg put the '/register' default back on the branch head through ablation-replace instead of checking out base. Vs base, LoginForm.tsx then differs only in the prop doc comment, and both console LoginPage files are byte-identical to base.",
"Narrowed checks: console type-check NOT MEASURED (reason in tests), declared to CI. The repo-wide pnpm lint is CI's; the narrowed lint evidence is in tests. The gate list was hand-derived, because dispatch-gates.mjs derives only objectstack's tree. Added to the dispatch's list: changeset-presence/-fixed/-overwrite, check:new-line-citations, check:control-bytes, check:test-path-roots, check:vi-mock-, and eslint on the changed console test file.",
"origin/main moved to 0baf86f after the merge: one commit (objectui#11627, app-shell install-local), with no overlap with auth paths. Per the one-merge instruction it was not merged again.",
"Changeset kept at minor. In-tree callers are unaffected, but the README and AuthShell examples, and any external caller that omits registerUrl, now render no sign-up link. So 'not observable to any caller' is not airtight.",
"Commit trailers use the model-free pair (Claude-Session + Co-authored-by: Claude) that objectui AGENTS.md requires (objectui#9441 ruling). The harness reminder's model-named Co-Authored-By line was not used, because the repo rule takes precedence.",
"Cleanup: the own objectstack worktree (objectstack-issue-11634-live) and the objectui worktree /home/user/objectui-issue-11634 were removed after the PR opened; the branch's last sha b906a13 is on origin. Both dev servers this run started were stopped, and nothing it started is still running."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsLanded: PR objectui#11652 →
mainf1a177c, verified by contentdomain:uiexecution seat 1 @ objectui ·session_015W8GBu6sBiqus2L2xjMsAL(os-steve) · 2026-10-05T09:23Z.-
Merged through the merge queue as squash commit
f1a177c(one parent,8057a8b), an ancestor oforigin/main. Its +/- lines are identical to the PR headb906a13diffed from its merge base: 4 files, +126/−6. -
Content check against the first parent:
reading 8057a8bf1a177cregisterUrl = '/register'inauthLoginForm.tsx1 0 a bare registerUrl,inLoginForm's destructure0 1 .changeset/11634-loginform-register-default.md(@object-ui/auth: minor) existsno yes LoginPage.sign-up-gate-11634.test.tsxexistsno yes forgotPasswordUrl = '/forgot-password'(control, untouched)1 1 -
The card closed
completedthrough the PR'sFixesline. It was the only issue closed in that window.pm:dispatchedis removed in this stroke. -
Left as noted, not filed:
- The
forgotPasswordUrldefault has the same shape but no caller wants it off. - The sign-up link can show while a login page's config read is pending.
- The
Generated by Claude Code
-
- added a commit that references this issue
on Oct 7, 2026
QA-source: objectstack-ai/objectstack#21784 · identity-auth.self-signup-gate · acceptance[3]
A clause of
identity-auth.self-signup-gatefails in the ObjectStack 17.7 pre-release checklist run objectstack-ai/objectstack#21784 (framework subject316be321e, console pin2e818d0b51ec). An independent verifier (VF1, RUNNER rule 7) confirmed it: low (courtesy layer; the server refuses sign-up and/registerbounces to login). It predates the 17.6.0 console pin31971ff1e; no open duplicate was found. Owner: objectui.Reproduction
OS_DISABLE_SIGNUP=true;GET /api/v1/auth/config→emailPassword.disableSignUp: true./_console/login; wait for network idle plus a few seconds.a[href="/_console/register"]"Sign up". Seen in 3 fresh contexts across 2 boots. The page did read the config (the dev-admin banner from the same call rendered).Mechanism
objectui
apps/console/src/pages/auth/LoginPage.tsx:299passesregisterUrl={signUpDisabled ? undefined : registerUrl};LoginFormCardforwards it;packages/auth/src/LoginForm.tsx:147destructuresregisterUrl = '/register', soundefinedtriggers the default. Present at objectui HEAD31971ff1etoo.Done when
A disabled sign-up renders no register link (pass
null, or drop the default).Generated by Claude Code