Skip to content

feat(platform-objects): sys_exchange_rate — an organization's dated exchange rates, admin-written and audited, with a Setup list under Localization - #27246

Merged
os-zhuang merged 12 commits into
mainfrom
claude/project-thread-ozmi9m
Oct 30, 2026
Merged

os-zhuang merged 12 commits into
mainfrom
claude/project-thread-ozmi9m

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread
Fixes #27185

⛔ not released alone — pairs with #27192 (R2); merges in the R2 window

Clause-②: no
Metadata-protocol: none

维护者速读

What changes

  • sys_exchange_rate (packages/platform-objects/src/system/sys-exchange-rate.object.ts), managedBy: 'system-data':
    • currency — text, valueDomain: 'iso_4217_currency', required.
    • rate — number (the platform's exact-decimal column, never float), scale: 12, precision: 24, min = 10⁻¹² (strictly positive), required (ruling 6469382727).
    • effective_date — date, required. source — select manual / import / app, default manual.
    • Unique (currency, effective_date) spelled unique: 'organization' (ADR-0120 D1), so two organizations each hold their own EUR rate for a day.
    • No tenancy opt-out: organization_id is injected and the Layer 0 wall scopes it.
    • enable.trackHistory: true plus per-field trackHistory on the four ruled columns; the object is not on plugin-audit's skip list, so every write lands in sys_audit_log.
    • Who writes: organization_admin's '*' grant reaches it (not private, no requiredPermissions); member_default names no business object, so members neither read nor write it (ruling parameter 2).
  • Rate direction stated once: EXCHANGE_RATE_DIRECTION (exported) is the rate field's description and the all_rates list view's description, which the console renders on the list page. EXCHANGE_RATE_SCALE (12) and EXCHANGE_RATE_PRECISION (24) exported beside it.
  • Registration: PlatformObjectsPlugin registers it in its first manifest (project database, no routing triple), beside sys_migration / sys_migration_journal / sys_secret / sys_trusted_key: its reader is the ObjectQL engine's write path, present in every composition.
  • Registry (@objectstack/spec): PLATFORM_OBJECTS_BY_PACKAGE['platform-objects'] lists sys_exchange_rate, so lint, the engine and the CLI treat it as a real platform object; the spec ratchet platform-object-names.test.ts requires it.
  • Upgrade ceremony (@objectstack/cli): the ADR-0131 D10 organization-ownership inventory gives sys_exchange_rate fate 3 (attribution, no anchors: the Default Organization under single, otherwise reported), like the other organization-authored tables added since the first census; without the row os migrate organization-ownership refuses every plan as carrying an uninventoried platform table.
  • Setup: nav_exchange_rates in group_configuration directly under nav_settings_localization, opening all_rates.
  • Translations: object, fields, options, view, empty state and the nav label in en / zh-CN / ja-JP / es-ES (the three translated locales hand-written; bundles regenerated with check-i18n-bundles.mjs --write, extract config gains the object).
  • Docs: content/docs/ui/apps.mdx (where main moved the Setup anchors when it retired ui/setup-app.mdx) — the group_configuration row and one paragraph saying how finance records a month's rates.
  • Changeset: .changeset/27185-exchange-rate-table.md, @objectstack/platform-objects, @objectstack/spec and @objectstack/cli patch.

Why

Multi-currency shape C′ (ruling 6465431966 on #27119; epic #8345, M1-1): a record-mode amount is converted to the home currency at write from the rate in force on its document date. This is that rate table. Parameters 2 (admin-only) and 3 (1 home = rate foreign, so home = amount ÷ rate) are applied as ruled; parameter 4 as ruled in 6469382727 (scale 12, precision 24).

Tests

  • New src/system/sys-exchange-rate.object.test.ts (15 cases) pins the declaration each enforcing mechanism reads, on the built value: one unique key on (currency, effective_date) equal to 'organization'; resolveInjectedSystemColumns plans organization_id; not private and no requiredPermissions (the org-admin wildcard reaches it); resolveCrudAffordances gives New/Edit/Delete and no import; trackHistory on object and columns; iso_4217_currency with the write predicate's member / non-member / lowercase answers; rate is number with scale 12, precision 24 and min 10⁻¹²; the direction sentence on field and view; the nav entry sits right after Localization and names a declared view.
  • Updated: plugin.test.ts (registration roster), platform-objects.test.ts (nav target classified as owned and listable), bundle-ownership.test.ts (bundle roster); packages/cli/test/connect-agent-both-halves-wire.pin.test.ts (the Setup tree the platform admin is served: 31 → 32 entries, 22 → 23 contributions). The spec ratchet platform-object-names.test.ts and the CLI organization-ownership-inventory.test.ts pass unchanged with the two registry rows.

Acceptance notes

Verification

Head 7ad4554 (after merging main, fresh install and full build); the verification on a3a5fd9 is in comment 6510686991
pnpm --filter @objectstack/platform-objects test            Test Files 98 passed · Tests 1381 passed
pnpm --filter @objectstack/spec exec vitest run src/system/constants/platform-object-names.test.ts   12 passed
pnpm --filter @objectstack/spec check:generated             all 14 generated artifacts up to date
pnpm --filter @objectstack/cli exec vitest run <inventory test, Setup-nav pin, 8 organization-ownership integration suites>   10 files · 164 passed
pnpm --filter @objectstack/{cli,platform-objects} typecheck  OK
48 scoped re-checks after the merge (docs, i18n, census, changeset, citation gates and the above) :: all exit 0
Before the merge, on 3e26252e6: pnpm --filter @objectstack/cli test  521 files · 6493 passed
dispatch-gates --commands: 115 families; the 3 added by the CLI paths (i18n-coverage, i18n-walk-parity,
  cli-test-child-env) :: exit 0; the list did not change across the merge.
Earlier heads: package test + typecheck (lock VERDICT)
head 86e39faf0 (final):
pnpm --filter @objectstack/platform-objects test
  Test Files  97 passed (97)
  Tests  1377 passed (1377)
  os-verify-lock: VERDICT command-exit 0
pnpm --filter @objectstack/platform-objects typecheck
  check:test-typecheck: OK; os-verify-lock: VERDICT command-exit 0
node scripts/check-i18n-bundles.mjs :: exit 0 (platform-objects in sync, 11 bundles)
Derived gates (dispatch-gates --commands, --ran reconciliation)
dispatch-gates --commands: 98 families (90 derived first, +8 after the census artefact joined the diff).
90 run on head faf1b6e3 (before the scale/census commit): 86 exit 0 at first run;
  4 refused/red, then resolved:
  - check-plugin-teardown-shape --self-test :: exit 3 (shallow clone) -> fetched pinned commit -> exit 0
  - spec check:skill-examples :: exit 3 (client-react unbuilt) -> built -> exit 0
  - check:dual-build-cjs-loads :: exit 3 (unbuilt packages) -> exit 0 after the build
  - check:platform-object-tenancy-census :: exit 1 (census drift, new object) -> --write -> exit 0
On head 86e39faf0: the 8 new families :: exit 0; re-run on the final tree, all exit 0:
  check:platform-object-tenancy-census, check:i18n (bundles), check:keyed-text-bounds,
  check-changeset-no-major, check-empty-changeset, check:nul-bytes, check:org-identifier,
  check:system-context-census, check:issue-citations, check:doc-authoring, check:i18n-stale-fill
dispatch-gates --ran: 98 derived famil(ies) accounted for - 98 run, 0 NOT-MEASURED
  (a DERIVED zero - all 98 recorded an exit code and none of them is 3).
Not run locally (CI's): pnpm lint, test:repo, the full farm.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (The spec change is one name in a constant list.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint is CI's.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched only to list the new object in PLATFORM_OBJECTS_BY_PACKAGE, with its patch bump in the changeset.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

Generated by Claude Code

…up list under Localization (WIP)

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
…arget classification (WIP)

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
…, its list and the Setup entry

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
…nancy census counts it

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 28, 2026
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/platform-objects, @objectstack/spec, touching 29 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/platform-objects/src/system/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

55 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 968cacfd10fdec64b6089d0da4f721f1a8a10925.

⛔ 13 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/platform-objects/src/system/index.ts) — pages documenting those are invisible to this run
  • 1 cross-cutting symbol(s) contributed no route anchor: isSystem (5 routes)
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 176 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 968cacfd10fdec64b6089d0da4f721f1a8a10925 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5efd91ed00f2efcd73745be63ce94fc3c4dbac85 — the merge of head a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 into base 968cacfd10fdec64b6089d0da4f721f1a8a10925, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5efd91ed00f2efcd73745be63ce94fc3c4dbac85 && git checkout 5efd91ed00f2efcd73745be63ce94fc3c4dbac85
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 968cacfd10fdec64b6089d0da4f721f1a8a10925 a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 && git checkout -B drift-repro 968cacfd10fdec64b6089d0da4f721f1a8a10925 && git merge --no-ff a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104

node scripts/docs-audit/affected-docs.mjs --json 968cacfd10fdec64b6089d0da4f721f1a8a10925

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 968cacfd10fdec64b6089d0da4f721f1a8a10925 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ency ADR by its card, not an unwritten number

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
… 24 (ruling 6469382727)

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
…platform-objects

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
The new tenant-scoped platform table carries organization_id, so
`os migrate organization-ownership` refused every plan as carrying an
uninventoried platform table. It takes fate 3 (attribution, no anchors:
the Default Organization under single, otherwise reported), as the other
organization-authored tables added since the first census do.

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
SETUP_NAV_CONTRIBUTIONS gains nav_exchange_rates, so the composition's
Setup tree grows from 31 entries to 32.

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head 3e26252e655f21fb2e990efc111c16e383e38774 · 2026-10-28T14:58Z

Triage seat, under gap rule (b) (#27062): the PR has been open since 12:33Z with no record or claim on any head (named as due in R731). The review runs at CONTRACT_REVIEW_TIER. If the lane claims or posts a record on this head first, triage withdraws.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3e26252e655f21fb2e990efc111c16e383e38774
Local-runs: none
Scope: packages/spec/src/system/constants/platform-object-names.ts + packages/spec/src/system/constants/platform-object-names.test.ts, packages/platform-objects/src/system/sys-exchange-rate.object.ts, packages/platform-objects/src/plugin.ts, packages/platform-objects/src/plugin.test.ts, packages/cli/src/utils/organization-ownership-inventory.ts, packages/cli/src/utils/organization-ownership-inventory.test.ts, packages/objectql/src/engine-lifecycle-health.ts, packages/lint/src/validate-object-references.ts, packages/lint/src/validate-nav-access.ts, packages/spec/src/stack.zod.ts

Governing shape: ADR-0144 (PR #27239, head 775d8c09, not yet on main) D3, D7 and Open items 7 and 11; ruling 6465431966 (#27119) and ruling 6469382727 (#27185: rate column decimal scale 12, total precision 24, never float). The tree was judged, not the PR body; where the two differ it is said below.

① Derived judgments

  • Registry entry — sys_exchange_rate added to PLATFORM_OBJECTS_BY_PACKAGE['platform-objects'], sorted between sys_email_template and sys_identity_email. RIGHT, and forced: the spec ratchet (platform-object-names.test.ts) scans every packages/**/*.object.ts, derives the owner from the path segment before src (packages/platform-objects/src/system/sys-exchange-rate.object.ts → platform-objects) and asserts set equality per group; without this hunk the ratchet reds on the new object file.
  • Accept-set widening at lint — isPlatformProvidedObjectName('sys_exchange_rate') now answers true, so a stack reference to it is admitted at rung ③ (validate-object-references.ts:238, validate-translation-references.ts:1848, stack.zod.ts:4581 action objectName) and validate-nav-access.ts:162 skips the grant check for a nav item naming it ("granted by its own package"). RIGHT: PlatformObjectsPlugin registers the object unconditionally in its first manifest (plugin.ts:198, pinned by plugin.test.ts), so what lint admits resolves at runtime wherever platform-objects composes; the nav skip is right because the object's own grant is the organization_admin wildcard (not access.default: 'private', no requiredPermissions), the same posture as the other object-type entries in group_configuration.
  • Runtime consumer — engine-lifecycle-health.ts#resolvePrimaryDatasource iterates PLATFORM_PROVIDED_OBJECT_NAMES; sys_exchange_rate now votes as a witness when registered. RIGHT: it declares no lifecycle.class (not a system-ledger object) and rides the first manifest with no routing triple exactly like sys_migration / sys_migration_journal / sys_secret, so it lands on their datasource and cannot by itself produce system-objects-split.
  • @objectstack/cli inventory — organization-ownership-inventory.test.ts:123 requires a row for every registered name; the diff adds sys_exchange_rate (census PMAIN, fate attribution, no anchors). RIGHT; the cli patch in the changeset follows from it.
  • Tenancy census — scripts/platform-object-tenancy-census.json 87→88 registered, 44→45 in reach, tenantField: organization_id. RIGHT, derived artefact consistent with the hunk and with the object's no-opt-out tenancy.
  • Cloud repo at the next pin move — service-cloud control-plane-fate-supplement.test.ts filters its supplement against the platform set (an added name changes nothing) and objectos-runtime tenant-v18-upgrade-retired-tables.test.ts only asserts retired names are absent. RIGHT: additive, neither breaks.
  • No new spec export — api-surface/system.json and export-origins/system.json unchanged; dangling-reference-audit.ts reads only the plugin-security group. RIGHT, unaffected.
  • Published surface outside hits (lane's ACCEPT) — @objectstack/platform-objects gains SysExchangeRate, EXCHANGE_RATE_DIRECTION, EXCHANGE_RATE_SCALE, EXCHANGE_RATE_PRECISION (system/index.ts), a Setup nav entry and four locales. Read for ②, not reviewed here.
  • Wrong: none found in the hits hunk.

② Semver level

  • Changeset .changeset/27185-exchange-rate-table.md: @objectstack/platform-objects: patch, @objectstack/spec: patch, @objectstack/cli: patch; body carries Clause-②: no and Metadata-protocol: none. Those three packages are exactly the released packages the diff moves (spec: registry membership; cli: inventory row; platform-objects: the object and its exports).
  • Metadata-protocol: none is right: sys_exchange_rate is a system object an author cannot write (ADR-0029 D4), no authorable key, value or type changes. AGENTS.md Post-Task Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3: "none takes patch" — so patch on all three is the rule's level. Precedent in packages/spec/CHANGELOG.md is split (17.2.0 sys_flow_dispatch under Patch Changes; 17.7.0 sys_flow_credential and sys_comment_reaction under Minor), the written rule governs. Clause-②: no matches the card's own declaration (Clause-②: no · Metadata-protocol: none) and appears line-initial in both PR body and changeset. Clause-②: no — matches the diff.
  • The PR body's "Changeset: @objectstack/platform-objects patch" names one of the three bumps; the changeset itself is complete. Body correction only (③).

③ Boundary flags

④ Design judgment

packages/spec/src/system/constants/platform-object-names.ts (schema face: the curated platform object registry, read by lint, the engine and the cli).

  • Against the code it cites. The file's own maintenance contract ("adding an object to a platform package means adding its name here; the owning package's conformance test fails otherwise") is enforced by platform-object-names.test.ts, which derives the owning group from the file path and compares sets. The hunk adds the one name that test now finds in packages/platform-objects/src/system/sys-exchange-rate.object.ts (ObjectSchema.create({ name: 'sys_exchange_rate' … })), under the group that path yields, in sorted position. HOLDS.
  • Against its first consumer (lint rung ③ and the nav-access skip). Tried to break it: (i) a composition without platform-objects — then every name in that group is equally absent, which is the registry's stated contract ("registered by a package in this repo"), identical to sys_user; (ii) a composition with platform-objects — the object registers unconditionally in the first manifest, so a lint-admitted reference resolves; (iii) the engine's primary-datasource vote — the new witness lands with its three manifest siblings; (iv) the cloud repo's two registry readers — additive and tolerant. No reader is made wrong by the entry. HOLDS.
  • Against the ADR it implements (ADR-0144 D3, Open items 7 and 11). D3 names sys_exchange_rate { currency, rate, effective_date, source } as a platform system object; the registry names it under the package ADR-0144's M1-1 row and the epic territory name. Open item 7 (uniqueness and audit) is answered in the object the entry points at: one unique key (currency, effective_date) spelled unique: 'organization' (ADR-0120 D1), trackHistory on object and the four columns, not on plugin-audit's skip list. Open item 11 is answered by placement, see ③. HOLDS; the settlement wants writing down (should-fix, ③).
  • Against ruling 6469382727. The hits file carries no numeric claim; the object it names declares number / scale 12 / precision 24 / min 1e-12, and the engine refuses rather than rounds at both seams. The PR body's scale 18 account is the only disagreement, and it is the body that is wrong. HOLDS.
  • Must-fix: none.
  • Should-fix: (1) PR body stale against the tree — scale 18 → scale 12 / precision 24, Human Minimum Add metamodel interfaces for ObjectQL/ObjectUI contract #1/Migrate from npm to pnpm #6 and the changeset line must name the packages/spec and packages/cli edits; (2) the ADR-0029 D1/D3/D6 ownership settlement is stated nowhere the ADR can read it — state it on the card so docs(adr): ADR-0144 multi-currency — record currency, dated rates, write-time home projection; dated note on ADR-0104 #27239 amends Open item 11; (3) [lane seat] the setup-app.mdx hunk conflicts with main's deletion of that page — rebase and move the docs line to content/docs/ui/apps.mdx.

Implemented-by: claude/project-thread-ozmi9m
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: PASS


Generated by Claude Code

main deleted content/docs/ui/setup-app.mdx (its Setup anchors moved to
content/docs/ui/apps.mdx); the Exchange Rates row and the entry on
recording a month's rates move there with it.

Claude-Session: https://claude.ai/code/session_01Utp6CZ8iiopaXHsTY7SiRf
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Regen-provenance: 6472775027 · 3e26252e655f21fb2e990efc111c16e383e38774 → 7ad4554c39a4142810ebdcfab462967d638d6e49 · diff <(git diff cdec7473 3e26252e) <(git diff ebfadb02 7ad4554c) -- . ':(exclude)content/docs/**' → (empty)

The head moved by one merge of origin/main (ebfadb02). The PR's net change on top of main is the same 26 files and byte-identical hunks before and after, except that the Setup-tree doc row moved from content/docs/ui/setup-app.mdx to content/docs/ui/apps.mdx with main's file rename. That is a prose face outside the review surface, so record 6472775027 (PASS) keeps governing this head; the moved doc sentence is the lane ACCEPT's to check line by line.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 29, 2026 20:26
@os-zhuang
os-zhuang enabled auto-merge October 29, 2026 20:26
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

server#3 · os-elon-musk · session_016WV4cwrrQFbCFFcuqx91Bx

Regen-provenance: 6472775027 · 7ad4554c39a4142810ebdcfab462967d638d6e49 → a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 · diff <(git diff ebfadb02 7ad4554c) <(git diff 81d00a4e a3a5fd92) -- . ':(exclude)content/docs/**' → NOT empty; the non-empty hunks are listed below, so this hop is not a pure regeneration.

Takeover merge (card #27185, server seat 3)

The head moved by one plain merge of origin/main at 81d00a4ed9 (no rebase, no force push). Three paths conflicted:

Net delta against the new base, per contract-review Scope file:

  • packages/spec/src/system/constants/platform-object-names.ts, packages/platform-objects/src/system/sys-exchange-rate.object.ts, packages/cli/src/utils/organization-ownership-inventory.ts: byte-identical hunks (the inventory hunk only shifts its line offset).
  • plugin.ts, plugin.test.ts: same added lines; the removed and context lines now carry main's SysTrustedKey. Nothing else differs.
  • The census JSON: the same added sys_exchange_rate row; the totals moved with main's own census.

PR body: what the review flagged, against the current body

Two body lines went stale with this merge. They should read:

  • Registration: "... beside sys_migration / sys_migration_journal / sys_secret / sys_trusted_key: its reader is ..."
  • Census bullet: "... joins the census population (on the current main: 89 → 90 registered, 44 → 45 in reach)."

Verification on a3a5fd927e (fresh install, whole-tree build)

pnpm --filter @objectstack/platform-objects test            Test Files 98 passed · Tests 1385 passed
pnpm --filter @objectstack/platform-objects typecheck       exit 0
pnpm --filter @objectstack/cli typecheck                    exit 0
pnpm --filter @objectstack/spec exec vitest run src/system/constants/platform-object-names.test.ts   exit 0
pnpm --filter @objectstack/spec check:generated             exit 0
pnpm --filter @objectstack/cli exec vitest run <inventory test, Setup-nav pin>                       exit 0
cli organization-ownership / migrate suites (43 files)      42 passed, 1 skipped · 573 tests passed, 7 skipped
pnpm check:platform-object-tenancy-census                   exit 0
node scripts/check-i18n-bundles.mjs                         exit 0
dispatch-gates --ran: 115 derived families, 115 run, 0 NOT-MEASURED, all exit 0
  (3 exited 3 before the whole-tree build: spec check:skill-examples, check:dual-build-cjs-loads,
   check:i18n-coverage; each exit 0 after it)
Not run locally (CI's): pnpm lint, test:repo, the full farm.

The release pairing is unchanged: not released alone, it pairs with #27192 (R2).


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Oct 29, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 · 2026-10-29T23:32Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104
Local-runs: none
Scope: packages/spec/src/system/constants/platform-object-names.ts + packages/spec/src/system/constants/platform-object-names.test.ts, packages/platform-objects/src/system/sys-exchange-rate.object.ts, packages/platform-objects/src/system/index.ts, packages/platform-objects/src/plugin.ts, packages/platform-objects/src/plugin.test.ts, packages/platform-objects/src/apps/setup-nav.contributions.ts, packages/cli/src/utils/organization-ownership-inventory.ts, scripts/platform-object-tenancy-census.json, scripts/platform-object-tenancy-census.mjs, packages/lint/src/validate-object-references.ts, packages/lint/src/validate-nav-access.ts, packages/spec/src/stack.zod.ts, packages/objectql/src/engine-lifecycle-health.ts, packages/spec/src/data/field.zod.ts, packages/spec/src/shared/value-domain.zod.ts, packages/plugins/plugin-security/src/objects/default-permission-sets.ts, packages/plugins/plugin-audit/src/audit-writers.ts, docs/adr/0144-multi-currency-record-currency-dated-rates-write-time-home-projection.md

Full review of the net diff against main at 81d00a4e (the merge's second parent): 26 files, the same list the GitHub files endpoint returns for the PR. Governing shape: ADR-0144 (now on main) D3, D7, D9 and Open items 7 and 11; ruling 6465431966 (#27119) and ruling 6469382727 (#27185, scale 12 / precision 24, never float). Record 6472775027 on 3e26252e was read for orientation; every item below was judged again on this head. The tree was judged, not the PR body; where they differ it is said.

The takeover merge (comment 6510686991), judged on the tree. a3a5fd92 is a plain merge of origin/main at 81d00a4e into 7ad4554c (no rebase, no force push). Comparing the PR-side hunks before and after the hop (git diff ebfadb02 7ad4554c against git diff 81d00a4e a3a5fd92, content/docs/** excluded) confirms the comment's account: the only non-offset differences are in plugin.ts, plugin.test.ts and the census JSON, and in each the PR's own added lines are unchanged while the removed and context lines now carry main's SysTrustedKey (#27123). Resolution judged RIGHT: plugin.ts:205 registers [SysMigration, SysMigrationJournal, SysSecret, SysExchangeRate, SysTrustedKey]; plugin.test.ts pins the same five by reference and by name and its routing-triple comment reads "The five above"; system/index.ts exports both SysTrustedKey (main's line 19) and the four exchange-rate symbols (lines 29 to 33). Nothing of main was dropped: of the 1875 paths main moved between ebfadb02 and 81d00a4e, the seven the net diff also touches carry only this PR's additive hunks. The census JSON is a regeneration, not a hand edit: its rows are name-sorted (the generator's rows.sort at line 399), sys_exchange_rate sits between sys_email_template and sys_file, the totals partition the rows (90 = 45 in reach + 45 out of reach, computed here by reading the JSON, not by re-running the generator), reasonTotals and the three out-of-reach reasons are byte-identical to main's, and the new row (reach: in, tenantField: organization_id, no reasons) is what the predicate yields for an object with no tenancy opt-out. main's sys_trusted_key row (out of reach, systemFields.tenant: false) is intact.

① Derived judgments

  • Registry entry (the hits hunk): sys_exchange_rate added to PLATFORM_OBJECTS_BY_PACKAGE['platform-objects'] between sys_email_template and sys_identity_email. RIGHT and forced: platform-object-names.test.ts walks every packages/**/*.object.ts, derives the owning group from the path segment before src (packages/platform-objects/src/system/sys-exchange-rate.object.ts yields platform-objects) and asserts set equality per group; without the hunk the ratchet reds on the new file. The file's own maintenance contract ("adding an object to a platform package means adding its name here") is met. The platform-prefix test also holds (sys_).
  • Accept-set widening at lint rung ③ (validate-object-references.ts:238): a stack reference to sys_exchange_rate is admitted where it was a rung-④ advisory warning (object-reference-unregistered-platform). RIGHT: PlatformObjectsPlugin.init registers the object unconditionally in its first manifest (plugin.ts:199-206), so what lint now admits resolves at runtime wherever platform-objects composes, which is the registry's stated contract ("registered by a package in this repo"), identical to sys_user.
  • Accept-set widening at the stack action door (stack.zod.ts:4726-4735): a top-level action whose objectName is sys_exchange_rate is accepted at defineStack where it was refused as "not defined in objects". RIGHT and the same shape as every registry row before it: the door accepts a known platform object because the engine attaches the action when the object is served, and a target no loaded package registers is refused at boot. Named here so the ② judgment is made on it, not past it.
  • Nav-access skip (validate-nav-access.ts:162): a nav entry naming sys_exchange_rate is no longer judged against the stack's own grants. RIGHT: the object's grant is organization_admin's wildcard (not access.default: 'private', no requiredPermissions), the posture nav_exchange_rates relies on, and the same as the other object-type entries in group_configuration.
  • Runtime consumer (engine-lifecycle-health.ts:107-121, resolvePrimaryDatasource): the object now votes as a witness when registered. RIGHT: it declares no lifecycle.class (not a system ledger), rides the first manifest with no routing triple exactly like sys_migration, sys_migration_journal, sys_secret and sys_trusted_key, so it lands on their datasource and cannot by itself yield system-objects-split.
  • @objectstack/cli inventory: organization-ownership-inventory.ts:784-786 adds the row organization-ownership-inventory.test.ts requires for every registered name (census PMAIN, fate attribution, no anchors), the same fate as the other organization-authored tables added since the first census. RIGHT; the cli patch follows from it, and the Setup-tree pin (connect-agent-both-halves-wire.pin.test.ts, 31 to 32 entries, 22 to 23 contributions) follows from the one new nav entry.
  • Tenancy census: 89 to 90 registered, 44 to 45 in reach. RIGHT, derived artefact consistent with the hunk (judged above).
  • Published surface outside hits (the lane's ACCEPT at TIER_DEFAULT): @objectstack/platform-objects gains SysExchangeRate, EXCHANGE_RATE_DIRECTION, EXCHANGE_RATE_SCALE, EXCHANGE_RATE_PRECISION, one Setup nav entry and four locales. Read for ②, not reviewed here.
  • No new spec export: the spec hunk adds one string to an existing constant; api-surface and export-origins are untouched, and Lint · repo & tooling gates and Type Check · workspace are green on this head.
  • Wrong: none found.

② Semver level

  • Changeset .changeset/27185-exchange-rate-table.md: @objectstack/platform-objects: patch, @objectstack/spec: patch, @objectstack/cli: patch, with Clause-②: no and Metadata-protocol: none line-initial in its body. Those three are exactly the released packages the diff moves (spec: one registry row; cli: one inventory row and a test; platform-objects: the object, its exports, the nav entry and the locales). scripts/ and content/docs publish nothing.
  • Metadata-protocol: none is right: no key, accepted value or metadata type that an author writes changes. The two accept-set effects named in ① (lint rung ③ and the defineStack action door) follow from the platform registering one more object, and the registry row records that fact; the repo's own precedent for this exact shape is .changeset/27123-integrity-reverified-at-unpack.md (sys_trusted_key joins the same group: Clause-②: no, Metadata-protocol: none, @objectstack/spec: patch), and .changeset/27123-local-plugin-install.md declares the new object and its exports in platform-objects as "platform code, not authorable metadata". ADR-0144 Consequences leaves each consumer's semver to its own changeset. AGENTS.md Post-Task Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3: none takes patch, so patch on all three is the rule's level.
  • Clause-②: no — matches the diff. It also matches the card's own declaration (Clause-②: no · Metadata-protocol: none) and the PR body's line-initial Clause-②: no / Metadata-protocol: none.
  • Check Changeset is green on this head.

③ Boundary flags

④ Design judgment

packages/spec/src/system/constants/platform-object-names.ts (schema face: the curated platform-object registry, read by lint, the engine and the cli).

  • Against the code it cites. The entry names exactly the object packages/platform-objects/src/system/sys-exchange-rate.object.ts declares (ObjectSchema.create({ name: 'sys_exchange_rate', ... })), in the group the ratchet derives from that path, in sorted position. The ratchet (platform-object-names.test.ts, "registers exactly the objects each package declares") and Test Core are green on this head. HOLDS.
  • Against its first consumers, trying to break it. (i) A composition without platform-objects: every name in the group is equally absent, which the registry header declares ("registered by a package in this repo"); no worse than sys_user. (ii) A composition with platform-objects but no manifest service (lean kernels): init swallows the miss and registers nothing, the same silence its four siblings have had since sys_migration 由 service-storage 注册——账本是平台基础设施,不该由一个可选服务持有 #4243; lint's admission then mirrors theirs. (iii) The engine's primary-datasource vote: the new witness lands beside its four manifest siblings on the project database; no split. (iv) The defineStack action door: accepting objectName: 'sys_exchange_rate' is correct only because the object is served unconditionally wherever the package composes, which plugin.ts:205 and plugin.test.ts pin. (v) The nav-access skip: correct because the object's own grant is the organization-admin wildcard, which sys-exchange-rate.object.test.ts pins by asserting access.default is not private and requiredPermissions is undefined. No reader is made wrong by the entry. HOLDS.
  • Against the ruling and the ADR it implements (ADR-0144 D3, D7, Open item 7; rulings 6465431966 and 6469382727). D3 names sys_exchange_rate { currency, rate, effective_date, source } as a platform system object; the object the entry points at carries exactly those four columns plus id (pinned by the test). Open item 7 (uniqueness and audit) is answered there: one unique key (currency, effective_date) spelled unique: 'organization' (ADR-0120 D1) so two organizations hold their own EUR row for a day; enable.trackHistory plus per-field flags on the four columns; not on the audit skip list. Parameter 2 (admin-only): not private, no requiredPermissions, and member_default grants no business object, so the organization-admin wildcard is the only non-platform writer. Parameter 3: EXCHANGE_RATE_DIRECTION states "1 unit of the home currency = Rate units of this currency", D7's wording, on both the field and the list view; the docblock says division. Parameter 4: number / scale 12 / precision 24 / min 1e-12, D7's decimal (24, 12), refused at the write seam rather than rounded. The hits file carries no numeric or directional claim of its own, and the object it names agrees with the ruling at every point. HOLDS.
  • Against the scenario it is accepted against (the card's two-minute check: an admin records a row, a non-admin is refused, a same-currency same-day second row is refused): each is a mechanism outside this package, pinned in its own package and read above; the declaration they read is pinned here. The behavioural run is NOT MEASURED, declared, and carried by examples + QA: showcase invoice goes multi-currency (code field, record amount, home field, document date), seeded rates, a rate-sync flow template, a platform-checklist item and a browser dogfood run (multi-currency M3-3, epic #8345) #27197. Accepted.
  • Must-fix: none.
  • Should-fix: (1) PR body: the Registration bullet names sys_trusted_key among the first-manifest siblings, the census bullet reads 89 to 90 registered and 44 to 45 in reach, and the Verification heading names a3a5fd92 (or points at 6510686991). (2) The ADR-0029 D1/D3/D6 ownership settlement is stated on card platform-objects: sys_exchange_rate { currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185 so ADR-0144 Open item 11 can be amended in its own PR.

Implemented-by: claude/project-thread-ozmi9m
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract-review-claim: session_016WV4czuTWNgKR22fKv7jia · head a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 · 2026-10-29T23:43Z

The lane seat runs the CONTRACT_REVIEW_TIER delta read this head owes on the record at 6472775027, since regen-provenance 6510686991 says the hop is not a pure regeneration.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104
Local-runs: none
Scope: delta on the record at comment 6472775027 (regen-provenance 6510686991) + packages/spec/src/system/constants/platform-object-names.ts, packages/spec/src/system/constants/platform-object-names.test.ts, packages/platform-objects/src/plugin.ts, packages/platform-objects/src/plugin.test.ts, packages/platform-objects/src/system/index.ts, packages/platform-objects/src/system/sys-exchange-rate.object.ts, packages/cli/src/utils/organization-ownership-inventory.ts, packages/cli/src/utils/organization-ownership-inventory.test.ts, packages/objectql/src/engine-lifecycle-health.ts, scripts/platform-object-tenancy-census.json, docs/adr/0144-multi-currency-record-currency-dated-rates-write-time-home-projection.md

Read-only delta: the regen-provenance claim was recomputed from the fetched refs, and each judgment of 6472775027 was re-read on the a3a5fd92 tree against origin/main at 561b7642e1 (23 commits past the merge's second parent 81d00a4e). Triage's own full re-read on this head (6511163838, PASS) was read afterwards; nothing below depends on it.

① Derived judgments

  • Regen-provenance 6510686991 — VERIFIED. git merge-base 7ad4554c origin/main = ebfadb02; git merge-base a3a5fd92 origin/main = 81d00a4e (the merge's second parent; one first-parent commit, a plain merge, no rebase). diff <(git diff ebfadb02 7ad4554c) <(git diff 81d00a4e a3a5fd92) -- . ':(exclude)content/docs/**' is 98 lines, all in the three paths the comment names plus index/offset lines: plugin.ts and plugin.test.ts (removed/context lines now carry SysTrustedKey; the PR's own added lines unchanged; "four above" → "five above"), the census JSON (registered 89→90, outOfReach 45 context). Same 25 files, same 581(+)/14(−) on both sides. The hop is what the comment says: one merge, three conflict resolutions, no new behaviour.
  • Hop 3e26252 → 7ad4554 (triage regen-provenance 6500764508): one merge of origin/main at ebfadb02, diff-of-diffs empty outside content/docs/**. The full record carries over to 7ad4554c unchanged; this delta is owed only for the second hop.
  • First-manifest roster. plugin.ts:205 objects: [SysMigration, SysMigrationJournal, SysSecret, SysExchangeRate, SysTrustedKey]; plugin.test.ts:59 pins the same five by reference, :71/:75 by name, :77 "The five above must NOT acquire the ledger's routing triple". system/index.ts exports SysTrustedKey (main's line 19) and the four exchange-rate symbols (29–33). Both sides of the conflict kept; nothing of ADR-0025 M1 — local T0 install loop for first-party, self-signed plugins: os plugin install (signature + integrity, materialize, record), boot-time loader with the tier dispatch point, disable/uninstall, Stripe sample as acceptance #27123 dropped. Judgment "registers unconditionally in the first manifest, no routing triple" HOLDS with one more sibling.
  • Registry and ratchet. platform-object-names.ts:55 sys_exchange_rate between sys_email_template and sys_identity_email; the ratchet still derives the group from the path segment before src and asserts set equality per group (platform-object-names.test.ts:78-79, :115). main added no *.object.ts since 81d00a4e (two modified: sys-api-key, system-file; no name change), so the group is exactly main's plus this one row. HOLDS.
  • cli ownership inventory. organization-ownership-inventory.ts:786 row (PMAIN, attribution, no anchors); the test's "a platform object without a row reds here, by name" still runs over Object.values(PLATFORM_OBJECTS_BY_PACKAGE).flat(). Offset-only shift on this hop. HOLDS.
  • Tenancy census. scripts/platform-object-tenancy-census.json totals {registered: 90, inReach: 45, outOfReach: 45}; counted from the rows: 90 rows, 45 reach: in, 45 out. The sys_exchange_rate row is reach: in, tenantField: organization_id, no reasons; main's sys_trusted_key row intact. Regeneration, not a hand edit (CI's Lint · repo & tooling gates runs the census check and is green). HOLDS; only the numerals moved with main.
  • engine-lifecycle-health vote. resolvePrimaryDatasource (:107-121) still iterates PLATFORM_PROVIDED_OBJECT_NAMES, skips unregistered and ledger-class names. sys_exchange_rate declares no lifecycle.class, rides the first manifest with sys_trusted_key as a fifth sibling on the same datasource; cannot by itself yield system-objects-split. HOLDS.
  • Object declarations unchanged on this hop: EXCHANGE_RATE_SCALE = 12, EXCHANGE_RATE_PRECISION = 24, rate scale/precision/min 10**-12, unique: 'organization' on (currency, effective_date), trackHistory on object and columns, valueDomain: 'iso_4217_currency' (byte-identical hunk). Ruling 6469382727 still applied.
  • main after 81d00a4, interaction check. git log 81d00a4e..origin/main on every Scope path above (plus stack.zod.ts, the two lint validators, setup-nav.contributions.ts, the Setup-tree pin, field.zod.ts, ADR-0029, ADR-0144): no commits. git diff a3a5fd92 origin/main on those paths is exactly the PR's own hunks reversed (8 files). Nothing on main interacts; a re-merge would be a pure regeneration.
  • Wrong: none found.

② Semver level

③ Boundary flags

④ Design judgment

packages/spec/src/system/constants/platform-object-names.ts (schema face), re-judged on this head.

  • Against the code it cites. Same one row, same group the ratchet derives from packages/platform-objects/src/system/sys-exchange-rate.object.ts, sorted position; the ratchet and Test Core are green on this head. HOLDS.
  • Against its first consumers, with the extra sibling. (i) composition without platform-objects: group-wide absence, the registry's stated contract; (ii) composition with it: plugin.ts:205 registers the object unconditionally beside sys_trusted_key, so a lint-admitted reference and a defineStack action target resolve; (iii) engine vote: five witnesses on one datasource, no split; (iv) nav-access skip: grant is the organization_admin wildcard, pinned by the object test. No reader is made wrong by SysTrustedKey joining the roster. HOLDS.
  • Against the ADR it implements, now canonical on main. D3 columns, Open item 7 (unique (currency, effective_date) per organization, history on), Open item 11 (placement under platform-objects with its rationale in plugin.ts, defensible under ADR-0029 D2 even as D6 shrinks the package). HOLDS; the settlement still wants writing on the card.
  • Against ruling 6469382727. The hits file carries no numeric claim; the object it names is unchanged on this hop. HOLDS.
  • Must-fix: none.
  • Should-fix: (1) PR comment carrying the two corrected body lines (Registration siblings incl. sys_trusted_key; census 89→90 / 44→45) and naming a3a5fd92 as the verified head; (2) ADR-0029 D1/D3/D6 ownership settlement stated on card platform-objects: sys_exchange_rate { currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185 so ADR-0144 Open item 11 can be amended in its own PR.

Implemented-by: claude/project-thread-ozmi9m
Reviewed-by: session_016WV4czuTWNgKR22fKv7jia (seat subagent)

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

PR body corrections at head a3a5fd927e6eb4f9b6a0a117ba9499e0798e1104 · 2026-10-29T23:49Z

The body cannot be edited through the lane relay, so the two lines that went stale with the takeover merge (regen-provenance 6510686991) are corrected here. The delta contract read 6511268457 (PASS) and triage's full read 6511163838 (PASS) are on this head.

  • Registration should read: PlatformObjectsPlugin registers it in its first manifest (project database, no routing triple), beside sys_migration / sys_migration_journal / sys_secret / sys_trusted_key: its reader is the ObjectQL engine's write path, present in every composition.
  • Census bullet should read: scripts/platform-object-tenancy-census.json, regenerated with node scripts/platform-object-tenancy-census.mjs --write because the new tenant-scoped object joins the census population (on the current main: 89 → 90 registered, 44 → 45 in reach). Derived artefact, no hand edit.
  • The body's Verification block was taken on an earlier head; the verification on a3a5fd92 is in 6510686991.

Merged via the queue into main with commit 194e66c Oct 30, 2026
54 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-ozmi9m branch October 30, 2026 00:04
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

⛔ main red after this PR landed: shard 2/3 · 2026-10-30T00:57Z

Since this PR's landing, 194e66c0, main's Dogfood Regression Gate (2/3) fails at packages/qa/dogfood/test/platform-app-object-entry-views.test.ts:371: named objects neither source declares: ['sys_exchange_rate'].

Owning seat (domain:server#3, the #27185 claim): claim the stop-bleed on #27813 (the full-run anchor) this round. Do what the test's own remedy comment says: add the plugin that registers sys_exchange_rate to the roster, and to check-app-nav-i18n's. Never a fallback. If it is unclaimed for one round, triage escalates by name, and after that triage dispatches the stop-bleed.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation protocol:system size/l tests tooling

Projects

None yet

3 participants