Skip to content

localization: the single posture's Default Organization takes its first owner's default currency when that owner is bound (the same rule as a new organization's creator, #25495) — maintainer ruling B #25651

Closed

Description

@objectstack-fleet

Filing gate ③, a task the maintainer set. The maintainer, verbatim, in domain:server seat 2's session (session_01JVTd3PprStGa26aez5omLB), 2026-10-24: 「25619 C 25495 B」. Here B is the option #25495's dev raised in its report (and the seat put to the maintainer). Filed by domain:server seat 2 (seat post #20966) · sunhaolin · session session_01JVTd3PprStGa26aez5omLB. ⛔ Not a claim. Domain and grade are triage's.

Business reason: the most common self-host first run is os serve, then the first sign-up becomes the single posture's Default Organization owner. That organization is created at boot with no creator and never passes through POST /organization/create, so #25495's rule never reaches it. A zh-CN self-hoster therefore sees plain numbers instead of ¥ until an administrator sets Setup → Localization → Default currency by hand. Only the os dev seed fills it today.

Reader who acts: the domain:server seat, one PR, S, in packages/plugins/plugin-auth.

The ask (B, as the dev's report put it)

"When the Default Organization's owner is first bound (the default-org bootstrap's owner bind), apply the same rule from that owner's stored locale or sign-up request."

Done when

  • A test: the first sign-up under the single posture, by a zh-CN user, leaves the Default Organization with localization.currency = 'CNY' (source tenant). With no derivable region, nothing is stored. An administrator-set value is untouched.

Dedupe

#25495 (closed, landed as PR #25621) is the creator rule this card extends; it names this case as left open in its landing comment. New.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    area:i18nThe customer's own language, across UI, metadata and notifications
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 24, 2026
  2. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — enhancement · priority:p2 · domain:server · area:i18n · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-24T13:55Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01JVTd3PprStGa26aez5omLB
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-25651-default-org-owner-currency
    Worktree: objectstack-issue-25651
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/plugins/plugin-auth/src/** (the Default Organization's owner bind beside default-org-bootstrap-once.ts / default-organization-invariant.ts, reusing organization-default-currency.ts), tests, one changeset. ⛔ Not the organization plugin factory region of auth-manager.ts (seat 1's #25224 series) (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier: "no path-derived mandate … the tier stays the PM's per-card judgment call"), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — a ruled extension of #25495's rule (ruling B), not a defect card
    Thread-read: 6385065825
    Serial constraints cleared: no open PR touches organization-default-currency.ts, default-organization-invariant.ts, default-org-bootstrap-once.ts or auth-manager.ts (every open PR's file list read this fire). #25495 landed as PR #25621.
    Premise: the maintainer's ruling 「25619 C 25495 B」 in this seat's session; ensureDefaultOrganizationExists (default-organization-invariant.ts) creates the creator-less Default Organization at boot, so #25495's create-time rule never reaches it.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25651,
    "status": "done",
    "branch": "claude/issue-25651-default-org-owner-currency",
    "pr": "#25706",
    "session": "session_01JVTd3PprStGa26aez5omLB (subagent: the parent seat's session)",
    "premise_still_valid": true,
    "summary": "When the once-gated single-posture default-organization bootstrap (AuthPlugin runEnsure) reports the owner first bound (memberCreated or ownerPromoted), the Default Organization now gets the owner's default currency through #25495's existing seedCreatedOrganizationCurrency: same rule, same tenant write, no second copy. EnsureDefaultOrganizationResult gains an optional ownerUserId so the wiring knows who was bound. Measured: on a first email sign-up the bind runs inside the sign-up request. The sys_user insert leads to plugin-security's grant insert, and plugin-auth's middleware then binds with memberCreated true. better-auth's endpoint context is visible there, so Accept-Language is read. An email sign-up stores no sys_user.locale (only an IdP locale claim writes it), so on that path the request header is the input. A kernel:ready bind sees no request and reads the stored locale only. auth-manager.ts was not touched. Claim 6387976517 names this branch. PR is draft, assignee sunhaolin, no labels.",
    "tests": "head 1ce6154. Lock: pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 gave VERDICT command-exit 0, Test Files 193 passed (193), Tests 3670 passed | 10 skipped (3680). Targeted run (organization-created-default-currency, default-org-bootstrap-once, ensure-default-organization, grant-readers-by-name.golden, organization-default-currency) gave 5/5 files and 109/109 tests. New cases 10/11/12 run on the real kernel (single posture, sign-up via AuthManager.handleRequest): 10 zh-CN gives {value CNY, source tenant} with exactly one write; 11 has nothing derivable, source default and zero writes; 12 keeps a stored USD with zero writes. Each case first asserts the sign-up is the bound owner. Ablation: scripts/ablation-replace.mjs replaced the guard 'if ((res.memberCreated || res.ownerPromoted === true) && res.defaultOrgId && res.ownerUserId) {' with 'if (false as boolean) {'. Mutation landed: anchor 1 to 0, blob 8c554309313e to 279d104e528d. Case 10 went red (AssertionError: expected ... to match object { value: 'CNY', source: 'tenant' }), 11 and 12 stayed green. Restore: blob == HEAD 8c554309313e and git diff HEAD empty. AuthPlugin is imported from source, so no build was in the loop. Probe: a temporary console line through the same tool, restored to the same blob, printed the gate's answers no_admin, no_admin, admin_already_in_org, then memberCreated true with ownerUserId. typecheck: pnpm --filter @objectstack/plugin-auth typecheck exit 0 (after building plugin-auth for tsconfig.examples).",
    "gates": "Local, from the worktree at 1ce6154. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 74 commands. All were run and each exit was captured before any pipe: 73 exit 0, 1 exit 3 (pnpm check:dual-build-cjs-loads, PREREQUISITE NOT MET because it needs a whole-workspace dist; declared NOT MEASURED, CI runs it). --ran with exit codes gave '74 derived famil(ies) accounted for — 73 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. eslint --no-inline-config on the 3 touched source files: exit 0. check-platform-checklist was not run: no exported symbol was renamed or removed. test:repo was not run locally (declared to CI). CI on PR #25706 at the time of this report: 'No other open PR may claim the same issue' success; Packed-tarball smoke and 18.x upgrade harness skipped (opt-in); Lint (dispatch-gates, CI/release/engine, repo & tooling, docs & code guards, ESLint, scoped x3), Type Check (source, debt, consumer, workspace), Check Changeset, Check PR Size, Governed Surface Queue Guard, the claim and single-writer checks, Flag docs affected and Check Documentation Links all in_progress.",
    "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no, with no (narrowing) arm",
    "mcp_calls": "0",
    "api_writes": "2 — (1) relay pr_create: POST /repos/objectstack-ai/objectstack/dispatches (request fw-20261024T192243Z-fe1bfe, run 40125018800 success), executed by the relay as POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/25706/assignees [sunhaolin]; read-back gave 4570 bytes sent and 4570 stored, identical. (2) this os-dev-report comment through scripts/pm/post-stamped.mjs: POST /repos//issues/25651/comments. Plus git push (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (承接者:无) · noted in the PR's Acceptance notes, not filed: seedCreatedOrganizationCurrency's log lines say 'creator' while the Default Organization's first owner stands in for the creator. Wording only.",
    "carrier: none (承接者:无) · noted, not filed: the walled posture's default-organization bootstrap in @objectstack/organizations is not wired to the owner-currency rule. The card and ruling B cover the single posture only."
    ],
    "deviations": "Worktree /home/user/objectstack-issue-25651 did not exist at dispatch, so it was created here from origin/main b77bb37 and the empty branch was pushed first. The dispatch prompt named 'pnpm lint on the touched packages'; it was run as eslint --no-inline-config on the touched files, since pnpm lint is the whole-repo eslint run, which CI owns.",
    "files_changed": [
    ".changeset/default-org-owner-currency.md",
    "packages/plugins/plugin-auth/src/auth-plugin.ts",
    "packages/plugins/plugin-auth/src/ensure-default-organization.ts",
    "packages/plugins/plugin-auth/src/organization-created-default-currency.test.ts"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #25706 → 5cf1b9d2e · domain:server seat 2 · sunhaolin · session_01JVTd3PprStGa26aez5omLB · 2026-10-25T04:07Z

    reading result
    PR #25706 object merged: true, merge commit 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0, merged 2026-10-25T04:07:32Z through the merge queue
    origin/main 5cf1b9d2e is an ancestor of origin/main (5cf1b9d2e8), 1 parent(s)
    queue branch no gh-readonly-queue/main/* head for PR 25706 remains

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsdomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions