Skip to content

cli + objectql: record-currency backfill scoped per organization — --org, refuse multi-org runs without it, --all-organizations explicit; recompute takes the same --org (multi-currency, epic #8345, ruling C item A) #30674

Open

Description

@objectstack-fleet

epic:#8345#1 · os-bill · session_01Utp6CZWjnSeed8H4dMjt9d

Part of #8345. Filed by the epic PM from ruling 6670707546 (letter C, maintainer 「同意」 2026-11-05T21:57Z), item A and item 3. ⛔ Not a claim.

Ruling-ref: 6670707546

维护者速读

  • 改了什么: 币种码回填按组织走:引擎函数加 organizationId,CLI 加 --org;库里有多个组织而没给 --org 就拒跑,并列出各组织的无码行数(可旁显该组织本位币作参考,绝不写入);--all-organizations 显式才一码写全库;单组织库不变。重算命令 recompute-home-amounts 同样加 --org。
  • 为什么改: 现在一个 --code 会写进所有组织的无码行,多组织库上对某些组织是静默写错。
  • 风险与回滚: 小,CLI 行为在多组织库上从「写」变「拒」;单组织库不变。回滚即撤 PR。
  • 先看: packages/objectql 的 backfillRecordCurrencyCodes,packages/cli/src/commands/migrate/record-currency-codes.ts。
  • 两分钟验证: 两个组织的库:无 --org 退出 1 且零写入;--org A 只写 A 的行。
  • 需要您做的: 无。

Task

  • backfillRecordCurrencyCodes gains an organizationId option; the walk and every engine.update run under { isSystem: true, tenantId: organizationId } (the mechanism engine-driver-read-scope.test.ts already pins). The report counts per organization.
  • os migrate record-currency-codes --org <id>. On a database holding more than one organization, a run without --org is refused before any write (exit 1); the refusal lists each organization's codeless row count, and MAY show that organization's current localization.currency beside it as a reference only. ⛔ It is never written (ADR-0144 D8 rule 4).
  • --all-organizations is the explicit way to write one code into every organization. --org and --all-organizations together are a usage error.
  • A single-organization database behaves exactly as today (pin it).
  • Ruling item 3: os migrate recompute-home-amounts (PR feat(objectql,cli): recompute stored home-currency amounts at the rates in force now, report first, rows without a rate listed and skipped (multi-currency M2-3) #30183) takes the same --org shape, passed to recomputeHomeAmounts as an organization filter; without it the run still covers every organization, each in its own home currency (no refusal: recompute writes no caller-chosen value).
  • Keep all logic in the objectql functions; the CLI only parses and prints, so the REST door (the B card) reuses them unchanged.
  • Tests: two organizations on memory, SQLite and Postgres: refusal lists both counts and writes nothing; --org writes only that organization; --all-organizations writes both; single-org unchanged; --json envelope for the refusal. Docs beside the commands in content/docs/deployment/cli.mdx.

Lane: server · Size: S · Model: opus, effort medium · Review: default. Clause-②: yes (widening: new optional option on two exported objectql functions) · Metadata-protocol: none.

Out of scope: the REST door (B card); docs/adr/** and skills/** (the seat's Tier H close-out card).

Dedupe: #8345 thread; PR #28128 review 6525881524 S1; no open card carries organization scoping for these commands.

Activity

  1. added
    enhancementNew feature or request
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    pm:queueReady for the PM dispatch loop
    and removed
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    on Nov 5, 2026
  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL

    Handover: epic #8345 → domain:server queue. The epic seat signed off (6670841501); the maintainer agreed the remaining cards go to the server lane. Labels and dependencies only; this seat is signing off and does not dispatch it.
    Maintainer: 「同意」, project chat, 2026-11-05T22:14Z.
    State change: pm:epic off, pm:queue on. It goes first: dispatch order epic-8345/dispatch-30674.md in the project files. The next server seat claims it with the full claim protocol and re-reads the thread.

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    Claim: PM loop round 1 · 2026-11-05T22:40Z
    Session: session_016WV4cytLmJxfAmgPVjUbbW
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it
    Worktree: objectstack-issue-30674
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/objectql/src/record-currency-backfill.ts (organizationId option, per-organization walk and report), packages/objectql/src/home-amount-recompute.ts (organization filter), packages/cli/src/commands/migrate/record-currency-codes.ts and recompute-home-amounts.ts (--org, --all-organizations, the multi-organization refusal), their tests, one root .changeset/ file. ⛔ packages/spec/**; ⛔ writing any organization's localization.currency as a code (ADR-0144 D8 rule 4) (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the record-currency backfill in packages/objectql and its CLI (one --code writes every organization's codeless rows) | none (no per-organization scope exists today) | an operator running os migrate record-currency-codes on a multi-organization database, which silently writes a wrong currency code for some organizations
    Thread-read: 6671016825
    Premise: holds per the card's ruling-derived task list (ruling 6670707546, letter C, maintainer 「同意」); the dev re-reads both engine functions and both commands on origin/main first
    Serial constraints cleared: no open PR touches the two engine files or the two migrate commands (open PR file lists read at claim time; #30696 edits other migrate/ commands, not these). Handover from epic #8345 by domain:server#2 (6671016825), maintainer 「同意」 22:14Z.

  5. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    席位核对 · Amendment · 2026-11-05T22:43Z · claim 6671428099 branch, docs, clause arm

    Branch: claude/project-thread-erl0zi

    Clause-②: yes (widening)

    • Branch: this is the card thread's harness-designated branch (the project-thread platform exception). The dev thread is session session_016WV4cyT9hHRjH6qFYychv1.
    • File surface: adds content/docs/deployment/cli.mdx, the docs beside the two commands that the card's Task list names.
    • Clause arm: the card body declares yes (widening: new optional option on two exported objectql functions), and that wins over the claim's no. The option widens the exported signatures of backfillRecordCurrencyCodes / recomputeHomeAmounts; it touches no published schema. The review owed is the lane seat's own review of record, at the default tier. The dev re-derives the arm from the diff and flags any difference.
    • Everything else in claim 6671428099 stands.
  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    席位核对 · Amendment · 2026-11-05T22:58Z · claim 6671428099 clause arm and test surface

    Clause-②: yes (narrowing)

    • The card thread re-derived the arm from its diff. Called with no organization over rows of two organizations, the exported backfillRecordCurrencyCodes now refuses (VALIDATION_ERROR / 400) where main wrote, and os migrate record-currency-codes without --org exits 1. That is the narrowing the ruling orders, and it dominates the new optional options. This replaces the (widening) arm of 6671472723.
    • The arm owes: the BREAKING banner and the ADR-0087 disposition marker in the changeset, the full dogfood battery on the final head, and one contract-review-tier review before the queue.
    • File surface, tests only: packages/objectql/src/federated-injected-column-readers.test.ts (registers the new organization_id reader) and the SQLite/Postgres cells under packages/qa/dogfood/test/.
    • Mechanism note, measured: { isSystem: true, tenantId } scopes the driver only under a walled posture, so the walk also filters on the row's own organization_id.
    • Everything else in claim 6671428099 stands.
  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cyT9hHRjH6qFYychv1
    os-dev-report

    {
      "issue": 30674,
      "status": "done",
      "branch": "claude/project-thread-erl0zi",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30737",
      "session": "session_016WV4cyT9hHRjH6qFYychv1",
      "premise_still_valid": true,
      "summary": "backfillRecordCurrencyCodes takes organizationId / allOrganizations. An unscoped run over the rows of more than one organization is counted read-only and refused with 400 VALIDATION_ERROR (field organizationId, per-organization counts on the error) before any write, on dry and apply runs alike. Rows of one organization run exactly as before, with the same report text. os migrate record-currency-codes gains --org and --all-organizations as an oclif exclusive pair (exit 2 together), the refusal prints each organization's codeless count, exit 1. recomputeHomeAmounts gains organizationId and the recompute command gains --org, never refused. No localization.currency is read. Measured correction to the brief: { isSystem, tenantId } narrows the driver only under a walled posture, and a CLI shell boots single, so both scoped walks also filter where organization_id = the organization.",
      "tests": "objectql whole package 556 files / 11079 passed; cli --project integration migrate tests 2 files / 26 passed; dogfood record-currency-backfill-organizations + home-amount-projection 2 files / 57 passed incl. PostgreSQL 16 cells; type-check objectql, cli, dogfood exit 0. Ablations: (1) refusal threshold > 999 -> objectql pin 'two organizations and no scope' red, restored green; (2) scoped where filter removed -> 2 objectql pins red, then rebuilt into dist with ablation-dist-preflight confirming the mutation -> dogfood scoped pin red on better-sqlite3, sqlite-wasm and pg; restored + rebuilt, preflight marker absent, clean tree, green. A first src-only dogfood ablation was a false green (dogfood reads dist) and is not counted.",
      "gates": "dispatch-gates --commands derived 94 families, each run with its exit code captured; --ran: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN. Three exit-3 prerequisites on the first pass (plugin-teardown-shape --self-test: shallow clone; check:skill-examples and check:dual-build-cjs-loads: missing dist) cleared by git fetch --unshallow plus a build of those packages, rerun exit 0. pnpm lint exit 0. CI check-runs on the PR: pending at report time; the seat thread gets them when green.",
      "dogfood_battery": "f7b7814e · os-verify-lock: VERDICT command-exit 0 · held the lock 1466s · Test Files 422 passed | 3 skipped (425) · Tests 3547 passed | 17 skipped (3564) · no failures",
      "mcp_calls": "2 — mcp__github__issue_read (card read), mcp__github__pull_request_read (PR read-back); no MCP write tool",
      "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no assignees, no labels) and this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/30674/comments); plus git push of the branch (not a REST write)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: skills close-out card (Tier H) · skills/objectstack-data/rules/field-types.md L174 'Backfill: os migrate record-currency-codes' names neither --org nor --all-organizations; declared in the PR as Skills-docs: follow-up owed · noted, not filed",
        "carrier: the B card (REST door) · RecordCurrencyBackfillOrganization and RecordCurrencyBackfillOrganizationsRefusal are not re-exported from the @objectstack/objectql barrel (outside this file surface) · noted, not filed",
        "carrier: seat (landing) · the four branch commits are WIP commits without the Claude-Session / Co-authored-by trailer pair; the branch was already pushed and is not force-pushed, so the pair belongs in the squash message · noted, not filed"
      ]
    }
  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    席位核对 · Amendment · 2026-11-06T00:13Z · claim 6671428099

    File surface adds content/docs/data-modeling/currency.mdx (~L140, ~L362–365): it says the code backfill writes its one code into every organization's codeless rows, which PR #30737 makes false (os-dev: a published statement the diff falsifies is fixed in the same PR). Docs only, +8/−3. The skills line stays the follow-up card #30740; content/docs/releases/** stays untouched. Everything else in the claim and its amendments stands.

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions