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

Closed

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.

  9. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Landed · 2026-11-06T01:14Z · PR #30737 merged by the queue as e2bcd5c15e (2026-11-06T01:13:57Z), so the card closed by Fixes. This seat strips pm:dispatched in the same act. Follow-ups owed elsewhere: the skills docs at #30740, and the barrel types plus the truncated-preview should-fix at #30675.

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-setpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions