Skip to content

core: ResolveLocalizationInput.tenantId takes an organization id but does not say so — cloud passed the environment id for months and every hosted environment resolved UTC; rename or brand it #25555

Closed

Description

@hotlong

Filing gate ③: maintainer direct dispatch, 2026-10-24, in the cloud ops seat's chat: 「可以:在 objectstack 立这两张改进卡(时区接口参数名、跨仓公开类型改动同步落地),挂 pm:queue。」 ⛔ Not a claim.

Reader who acts: the domain:engine seat. resolveLocalizationContext and ResolveLocalizationInput live in @objectstack/core.

What happened

ResolveLocalizationInput.tenantId (packages/core/src/security/resolve-authz-context.ts:2181, on main 45f996231e) means the organization id: the settings service stores and reads the tenant rung by organization. The name does not say so.

objectstack-ai/cloud passed the environment id there from its three AI entry points: agent-routes (chatContext.environmentId), ai-routes /chat and tool-routes. The consequences:

Unit tests stayed green because they only exercised the "settings service unavailable → read sys_setting directly" fallback, which did not filter by organization until #25301 narrowed it. cloud fixed its callers in cloud#3150 (merged 1b26a28b) and pinned the fix with a real hosted-kernel e2e.

A rig measurement on a real hosted kernel, with the organization timezone Asia/Shanghai:

tenantId passed Timezone resolved
the environment id UTC
the organization id Asia/Shanghai
absent Asia/Shanghai

Wanted

Make the parameter say what it takes, so this class of mistake cannot type-check. The seat picks the shape:

  • (a) Rename tenantId → organizationId on ResolveLocalizationInput. It is a published @objectstack/core export, so the rename needs this repo's deprecation and ADR-0087 treatment. There are 4 in-repo callers: request-execution-context.ts:274, mcp/src/plugin.ts:669, plugin-audit/src/audit-plugin.ts:387, plugin-hono-server/src/current-user-endpoints.ts:742.
  • (b) Keep the key and brand the type: an OrganizationId brand that an environment id cannot satisfy without a cast.
  • (c) Both.

The settings service's own { tenantId, userId } scope carries the same naming. Whether it moves with this change is the seat's call; say so on the card.

Acceptance

  • The input's name or type states it takes an organization id.
  • Passing an environment-shaped value is caught at compile time (b) or is impossible to spell (a).
  • In-repo callers are migrated.
  • A changeset declares Clause-② / Metadata-protocol per this repo's rules.

Dedupe

Searched with gh api search/issues, closed included:

Query Hits
repo:objectstack-ai/objectstack resolveLocalizationContext tenantId 29
repo:objectstack-ai/objectstack localization tenantId organizationId rename 7

The nearest hits are #25301 and #25226, both merged: they narrowed the fallback and the settings rungs to the caller's organization (ADR-0131 D8) but kept the name. #22318 and #24235 are plugin-audit locale seams. None renames or brands the parameter. New.


🤖 Generated with Claude Code

Activity

  1. added
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    area:i18nThe customer's own language, across UI, metadata and notifications
    on Oct 24, 2026
  2. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    on Oct 26, 2026
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2 · card thread
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_014xf92fjdDKjycyVbyxMKm8
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-fwgt86 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer's direct dispatch quoted on the card (「可以:在 objectstack 立这两张改进卡(时区接口参数名、跨仓公开类型改动同步落地),挂 pm:queue。」); the seat's shape pick is (a), rename retired at once, unless a named external caller forces a staged alias (then needs_decision); intake of a p3 card rides the maintainer's 「p1 p2 处理完了,就可以处理 p3」 (2026-10-26T10:29Z): the lane's p1/p2 queue has no dispatchable card at this act
    File surface: packages/core/src/security/resolve-authz-context.ts (ResolveLocalizationInput), its in-repo callers (request-execution-context.ts, mcp/src/plugin.ts, plugin-audit/src/audit-plugin.ts, plugin-hono-server/src/current-user-endpoints.ts, and any found on re-grep), their tests, one changeset. ⛔ No packages/spec/** (stop on breach; explain in the report)
    Container & model: S, mode:thread, default model and effort (set at thread open)
    Clause-②: no (narrowing) (expected: a key removed from a published input, contract review owed; the dev measures, and yes (narrowing) if the diff also widens)
    Metadata-protocol: none
    Responsibility: unmeasured; cloud#3150 fixed the cloud callers, this card makes the mistake untypeable
    Thread-read: none
    Premise: holds (read 2026-10-26T10:36Z at origin/main: card open, unassigned, pm:queue, no claim on the thread)
    Serial constraints cleared: no open PR names #25555; no in-flight lane claim declares this file surface (read across seat 1's claims and its split note at this act); the dev re-measures open PRs on its surface before pr_create


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 25555,
      "status": "done",
      "branch": "claude/project-thread-fwgt86",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26078",
      "session": "session_014xf92fjdDKjycyVbyxMKm8",
      "premise_still_valid": true,
      "summary": "Shape (a) as the seat picked it: ResolveLocalizationInput.tenantId in @objectstack/core is renamed organizationId and retired at once, with no alias and no dual spelling. The interface doc says it takes an organization id, never an environment id; the resolver reads no other key at run time. All four in-repo callers are migrated (request-execution-context.ts, mcp/src/plugin.ts, plugin-audit/src/audit-plugin.ts, plugin-hono-server/src/current-user-endpoints.ts); a re-grep of origin/main at bbf70614 (ui/ included) and of hotcrm found no others. 100 test call sites across 4 core localization test files are migrated mechanically. A new pin (resolve-localization-organization-id.pin.test.ts) holds the type half (@ts-expect-error on tenantId, compiled by tsconfig.test.json) and the runtime half (the org timezone resolves through organizationId on the settings-service leg and the direct sys_setting leg; a smuggled tenantId reaches neither and resolves UTC). Changeset: patch for core, mcp, plugin-audit, plugin-hono-server, BREAKING banner, FROM/TO line, ADR-0087 not-required (runtime-export-retired @objectstack/core#ResolveLocalizationInput.tenantId), verified by the gate. The settings service's own { tenantId, userId } scope stays as it is: it is a separate public surface with its own callers across services and is outside this card's acceptance; the resolver passes the organization id into it unchanged and the pin asserts that scope. External caller objectstack-ai/cloud: this session cannot read that repo (add_repo: no access), so its @objectstack/core pin is NOT MEASURED; per the card its three AI entry points pass an object literal, so the rename reds there as TS2353 at cloud's next core bump (loud, one key per call site), which is not the silent same-release break that would justify a staged alias; no needs_decision raised.",
      "tests": "head bcf5c8adf (source change at 4c667539; bcf5c8adf only reshapes the new pin's engine double). pnpm --filter PKG test exit 0 under os-verify-lock: core Test Files 120 passed / Tests 2772 passed; mcp 43 / 538; plugin-audit 60 / 993; plugin-hono-server 35 / 448. New pin on bcf5c8adf: 1 file / 4 tests passed. pnpm --filter PKG typecheck exit 0 for all four (core includes check:test-typecheck OK, ledger 4 files / 4 errors unchanged). Reverse verification of the type pin: tenantId?: string re-added to the interface under an EXIT trap, check-test-typecheck exit 1 naming exactly resolve-localization-organization-id.pin.test.ts with 1 error (unused @ts-expect-error); restore proved by git hash-object equal to the HEAD blob and git diff HEAD empty. No ablation: the runtime behaviour pinned is the key rename itself, which the reverse check covers. eslint --no-inline-config over the four packages' src exit 0. check-platform-checklist --changed-from origin/main exit 0 (8 anchors judged, 0 lost).",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack derived 76 at bcf5c8adf; all 76 run on bcf5c8adf, every exit 0; --ran reconciliation: 76 derived, 76 run, 0 NOT-MEASURED, 0 UNRUN. Along the way on 4c667539: check:objectql-double-limit flagged the first pin double as UNJUDGED (filtered only on organization_id, so the control probe could not seat it), fixed in bcf5c8adf with a where-applying, bound-holding double; check-plugin-teardown-shape --self-test exit 3 on the shallow clone, green after git fetch of the positive-control commit; check:dts-closure and check:dual-build-cjs-loads needed the dogfood closure build, green after it. check-adr-0087-registration exit 0 (runtime-export-retired verified, member). CI on bcf5c8adf: 47 check-runs, none failing; required set: Lint & Repo Gates success, TypeScript Type Check success, Test Core success (8/8 shards), Build Core success, Governed Surface Queue Guard success, Type Check UI success, Build & E2E UI success, Lint UI skipped; Check Changeset success, Dogfood Regression Gate success.",
      "dogfood_battery": "4c667539 (bcf5c8adf differs only in the new core pin file, not a dogfood input) · os-verify-lock: VERDICT command-exit 0, held 1193s · Test Files 296 passed, 3 skipped (299) · Tests 2523 passed, 18 skipped (2541) · no failures",
      "mcp_calls": "0 GitHub MCP calls (one claude-code-remote add_repo read attempt for objectstack-ai/cloud, refused: no access)",
      "api_writes": "2 relay writes: pr_create (fleet-write run 40313705586, PR #26078, body 4924 bytes read back identical, assignee os-project-manager) and this os-dev-report comment via post-stamped. git push x3 to the harness branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: seat 2 · noted, not filed: objectstack-ai/cloud's three AI entry points need the one-key fix (tenantId to organizationId) at their next @objectstack/core bump; this is the maintainer's sibling card topic (cross-repo public type changes landing in sync), not a defect here"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #26078 comment 6415424146 (head bcf5c8adf3, contract review 6415407627 PASS) · domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-26T11:57Z. The head is green; landing through the queue now. The cloud call-site check for the release that carries this is routed to the cloud lane.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    Contributor

    Landed

    PR #26078 merged at 2026-10-26T12:39Z by the merge queue as 65145d34fb on main (Fixes #25555; ACCEPT 6415424146, contract review 6415407627 PASS). The merge closed this card as completed. · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-26T12:43Z

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26078-*' 0 refs (left the queue)
    Ancestry git merge-base --is-ancestor 65145d34fb origin/main holds
    Card state GET /repos/objectstack-ai/objectstack/issues/25555 closed, completed

    Generated by Claude Code

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

Metadata

Metadata

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions