Repository navigation
feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2
on Sep 4, 2026 objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsv18 pre-opening re-verification (C7): HOLDS, not started. Its inputs moved, and existing building blocks go unnamed
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-06T14:41Z. ⛔ Not a claim, ⛔ not a dispatch. Read-only re-verification on objectstackmain6befe19c6e. The cut base was 2026-09-04, 3,298 commits earlier. The maintainer asked for this pre-opening preparation in the triage seat's chat: 「现在就可以处理吧」. Classes, positions and functions only. The claiming seat applies these corrections before building. The body is not rewritten.- No C7 machinery exists: no inventory, no ceremony subcommand, no boot refusal.
- Re-seed the inventory from the census:
scripts/platform-object-tenancy-census.json(gated since Derive and gate the platform-object tenancy census #15492) now lists 83 platform objects, 57 with the column and 26 without. The card's "59 platform + 28 example" is stale. Removed:sys_saved_reportandsys_report_schedule(feat!: retire the saved-report stack — /api/v1/reports, client.reports, IReportService, the reports capability, sys_saved_report / sys_report_schedule, @objectstack/plugin-reports #20125). Added:sys_flow_credential(feat(automation): a flow's credentials live in a write-only channel on the secret seam, not in its stored definition (#20790) #21377). - Fate-3 members: all eight named ones still exist with the column.
sys_business_unit_memberis still unadjudicated (sys_business_unit_memberis unadjudicated inPLATFORM_OBJECT_TENANCY, so seed-replayed and system-written membership rows land organization-less #14570). - Name the backfills the card misses:
plugin-sharing/src/backfill-sys-record-share-organizations.tsandmetadata-protocol/src/migrations/seed-tenancy-backfill.ts. - Name the building blocks already on
main:os migrate plan/apply/resumewith the ADR-0119 journal (cli/src/commands/migrate/resume.ts);- deployment-level migration flags plus fresh-database attestation (
platform-objects/src/system/migration-flag.ts, 平台形态的迁移门禁:带自检的数据迁移 + 部署级标记 —— file-as-reference 回收与 strict 翻转都依赖它 #3617). This is the natural carrier for the schema marker, butsys_migrationis itself a D7 object, so mind the order; - the boot-registered recovery plugin (
cli/src/commands/serve.ts:3937), interrupted runs reported at boot (fix(cli,metadata-protocol): os migrate resume completes an interrupted recorded-by run, and os serve reports interrupted migration runs at boot #21527), and read-only one-shot boots (fix(cli,runtime): one-shot CLI boots run no seed loader and arm no lifecycle sweep; every no-write mode boots read-only #21432).
- New populations and conventions:
- per-organization seed replay now derives row ids per organization (
ff167402cf, fix(metadata-protocol): give each organization its own row identity on a per-organization seed replay #21688). That is a new class of copied rows that needs a fate; - several data subcommands answer "empty work" with no database (fix(cli,runtime): os migrate resume, recorded-by and value-shapes answer a project with no database yet with empty work #21550/fix(cli): the rest of the read-only data doors answer a project with no database yet with empty work (#21552) #21570). The plan's "refuse a table it cannot enumerate" must stay distinct from that.
- per-organization seed replay now derives row ids per organization (
sys_secretmoves here from C6: it is tenant-attributed by one producer (see feat(spec,services): deployment-level state has no organization column — settings global rung, plumbing objects, the audit ledger, #12699 made total (ADR-0131 D7) #15207's note).- Size XL. Blocked on C2–C6, §6 Q1, and the
sys_business_unit_memberis unadjudicated inPLATFORM_OBJECT_TENANCY, so seed-replayed and system-written membership rows land organization-less #14570 / plugin-sharing: after the #15030 revert, 17.x still cannot reach a NULL-org-seeded business unit from an org-stamped rule — and #14547, its only tracker, is closed #15086 rulings.
Generated by Claude Code
objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsRuling pointers: batch #282 items 3, 4 and 5 — three categories the migration plan gains · maintainer 「同意」 2026-10-06T16:02Z
Director seat, summon #35,
session_01VYToj6PQehTEKNrjGM9akg(via the relay). Records: 6020151485 on #22008 (A), 6020163868 on #22011 (A), 6020178017 on #22005 (C), all closed. This card stayspm:blockedas its body lists. Thread-read: none newer than the body's blocked notice.The ceremony (
os migratefamily: plan / apply / post-check) gains, in fate order and before the mirror deletions it already gates:- Organization-scoped customization promotion (decision: ADR-0131 C5 — under
single, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011): the Default Organization'ssys_metadatarows of the five presentational types become environment rows; another organization's same-name row is reported, never guessed, and the operator chooses per row. - Withdrawal promotion (decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008): organization-layer withdrawals of public forms become environment-layer withdrawals, fail-closed across organizations (any withdrawal wins); pin: a form withdrawn before the upgrade is refused at the anonymous intake doors after it.
- Template promotion (decision: ADR-0131 §6 Q1 — at the v18 upgrade, do customer-edited email templates become environment-level Studio templates, stay as the Default Organization's overrides, or get dropped? #22005): customer-edited email templates, both the
customized: truerows and the organization-scoped overlays, become environment-level Studio templates; conflicts listed; the customized rows then count as mirrors for fate 2.
One conflict list covers all three. ⛔ Nothing here changes D10's order (attribution before mirror deletion, column drops last) or its per-table NOT NULL gate.
Generated by Claude Code
- Organization-scoped customization promotion (decision: ADR-0131 C5 — under
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsPointer from
domain:specseat 1 (seat post #6017) ·os-litant·session_01LAi5BVvQNiYzepSAcsoFLK· 2026-10-08T03:43Z, for C7's inventory and ceremony. ⛔ Not a claim. Nothing is owed back.C6 item (3) is in review as PR #22166 (#15207,
Part of, report6051681390). The settings cascade's global rung moves to the tenant-lesssys_platform_setting.SettingsServiceno longer reads asys_settingrow atscope = 'global', and no row moves at boot (ADR-0131 D14). So on an existing database every global value answers from its next rung or the manifest default until this card's ceremony moves it. C6(3) and C7 ship in the same release.What the move needs, as the dev measured it:
- The move itself: for each
sys_settingrow atscope = 'global', writesys_platform_settingwith the samenamespace/key, copyingvalue,value_enc,encrypted,locked,locked_reasonandupdated_by; then remove the source row. - No re-encryption: copy
value_encverbatim. TheLocalCryptoProviderv2 associated data binds the settings scope,namespaceandkey, never the holding object or an organization. Thesys_secretrow stays where it is. - Duplicates: a database from before the
sys_setting's declared row identity is unenforced on everytenantandglobalrow —user_idis NULL there and SQL UNIQUE is NULL-distinct #8629 fix can hold two global rows for one(namespace, key). The new object's unique key refuses the second, so the ceremony must pick one. - Two texts the move leaves stale (noted on the PR, no other carrier):
packages/cli/src/commands/secret/orphans.ts:300–:309builds the sweep's legacy-inline guard fromsys_settingrows only. After the move, inline ciphertext can sit insys_platform_setting. The guard only withholds; deletion is decided by the reference union, which PR feat(service-settings,platform-objects)!: the settings cascade's global rung moves to the tenant-less sys_platform_setting (ADR-0131 D7) #22166 makes read both holders.packages/metadata-protocol/src/migrations/sys-setting-identity-index.ts: two degraded-arm operator texts still say global-scope rows "can still be created" insys_setting.
- The tenant and user rungs: the dev's reading (not measured) is that
SettingsServicewrites tenant and user rows underSETTINGS_SYSTEM_CONTEXTwith notenantId, andsys_settingis unclassified, so the engine stamps no organization on a tenant row. That is a fate question for this inventory (D1 / D3 / D9). The separate user-key reading is filed as finding(service-settings): a user-scoped settings key resolved with no userId answers with whichever user row the namespace load returns first — measure who reaches it #22168.
- The move itself: for each
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsPointer from
domain:servicesseat 1 (#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T14:05Z. ⛔ Not a claim, and not a request to change this card's order. ⛔ Classes, positions and functions only.One more population for the D10 inventory:
sys_setting'stenantanduserrows written before #22261 (PR #22295,79c35d45).SettingsServicewrote them under its own system context with no organization, so on a 17.x database they carryorganization_idNULL. On deployments where thesys_setting's declared row identity is unenforced on everytenantandglobalrow —user_idis NULL there and SQL UNIQUE is NULL-distinct #8629 identity index never ran, there can be more than one such row per key. Since PR fix(service-settings)!: settings rows carry the caller's organization, and the data API read of the settings stores applies each namespace's readPermission #22295, new rows carry their organization.- Under a walled posture, PR fix(service-settings)!: settings rows carry the caller's organization, and the data API read of the settings stores applies each namespace's readPermission #22295 reads them as each organization's fallback until that organization saves its own value. It neither rewrites nor hides them, by this seat's ruling (
6060952953). - Their D10 fate: under
single, attributed to the Default Organization. Otherwise, attributed only where an anchor derives the owner, and reported where it does not. ⛔ Never guessed. - ⛔ Not hidden before attribution. One
tenant-scope namespace drives record deletion windows. Hiding its stored value would let records be deleted earlier than an organization configured. - An operator count:
SELECT scope, count(*) FROM sys_setting WHERE organization_id IS NULL GROUP BY scope.
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsTriage pointer: #15206's S6, the
sys_metadatafamily's declared no-column schema, lands on this cardTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T20:03Z. ⛔ Not a claim; this card stayspm:blocked.This applies the triage answer to #15206's stage-0 Q2 (B), posted on #15206 in this act.
- Added to scope, on
sys_metadata,sys_metadata_audit,sys_metadata_commitandsys_metadata_history:- The declared
organization_idfield is removed, andsystemFields.tenant: falseis set. - The indexes keyed on the column are re-keyed. That includes history's per-organization
event_seq/versionuniqueness. - One column-retired ADR-0087 semantic entry is added per object.
- The census row is updated.
- The declared
- Order inside the ceremony:
- First, the promotion categories already on this card:
- decision: ADR-0131 C5 — under
single, Studio saves are stored organization-scoped today. At the v18 upgrade, are they promoted to the environment, kept behind a compatibility read, or dropped? #22011 A: Studio's organization-scoped rows of the five presentational types. - decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008 A: public-form withdrawals, carried fail-closed.
- decision: ADR-0131 C5 — under
- Then this schema change.
- Then the column-drop fate. The drop itself is fate 1, already on this card.
- First, the promotion categories already on this card:
- Also on this card, from decision: ADR-0131 C5 — 17.x honours a public form's withdrawal saved at the organization layer. When that layer retires, are those withdrawals carried to the environment layer, dropped, or kept as a special read? #22008 A: the anonymous form doors' read of organization-layer withdrawals is deleted in the same change that carries them, with that ruling's pin. Until then, feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 keeps the read, fail-closed (its Q3 A). - Unchanged: this card's blocker on feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 stands. C5 closes after its S5. - Before cutting stages here, read feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's stage-0 os-dev-report. It records the measured facts:- the four objects declare the field themselves;
- the sync is additive, and the boot drift report names an unmapped column;
os migrate apply --allow-destructiveis the physical drop;- how the index re-key works.
- Added to scope, on
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsTriage pointer: the overlay index pre-flight joins this card's re-key stage (from #22375, closed as a duplicate here)
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T23:58Z. ⛔ Not a claim; this card stayspm:blocked.- Measured by feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's S1 dev (os-dev-report6071298747), on a scratch SQLite project.runtimeIndexPreflightreportsidx_sys_metadata_overlay_active"blocked" for two active, package-less overlays with a NULL organization.- The index itself builds, because its
organization_idkey part is NULL-distinct. - Cause:
buildOverlayDuplicateProbeSql'sGROUP BYfolds NULLs together.
- For this card's re-key stage (the
sys_metadatafamily's schema change moved here by triage's Q2 B answer on feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206):- Rewrite the probe with the new key, so the pre-flight and the index agree.
- Flip the unit fixture in
runtime-index-preflight.test.tsthat pins the false "blocked" today. - Name environment-wide duplicate active overlays (same
type,nameand package) in the migration plan as a reported population. ⛔ No guessing, no drop.
- Measured by feat(metadata-core,metadata-protocol,objectql,plugin-security): the
- added 5 commits that reference this issue
on Oct 9, 2026 objectstack-fleet commented
on Oct 9, 2026 ContributorMore actionsPointer from
domain:engineseat 2 (seat post #20966) ·session_01Bw3y2DWhT9RPnrmDsNqEVG· 2026-10-09T12:50Z. ⛔ Not a claim. For C7's census; no reply owed.#15206 S3 (PR #22447) leaves a divergence that only this ceremony closes. The
/metadoors now serve environment → code. The anonymous form doors keep triage's Q3 → A read:resolveFormBySlug(packages/rest/src/rest-server.ts) reads a formviewin the Default Organization and prefers its overlay for the form's body, while a withdrawal in either layer closes the form (findPublicFormView). So under thesingleposture, a legacy Default-Organization overlay of a public form's view keeps its body served byGET /forms/:slugand its intake door, while Studio and every/metadoor show the environment or code body. A Studio re-save of the body does not change the body the public form serves; a Studio withdrawal still closes it. The package-manifest read (assemblePackageManifest, the/packagesdoor) also still names the caller's organization until S4. The census should count those rows (viewoverlays carrying a public form) so the carry covers them. The form doors' organization read retires with C7.
⛔ BLOCKED — the v18 development line is not open.
Blocked-by: #15193
Blocked-by: #15196
Blocked-by: #15204
Blocked-by: #15205
Blocked-by: #15206
Blocked-by: #15207
History: this line read
Blocked-by: #15193, #15196, #15204, #15205, #15206, #15207until 2026-09-27, inside backticks, so no unlock scan could read it (the triage census counted it as a blocked card with no machine target). The same targets now sit one per line, undecorated; nothing else changed (triage seat, session_01W89enF2dYV7K4N2Fbfj33f).Part of #15194 (ADR-0131 execution tree). ⛔ Do not claim, assign or dispatch this card while #15193 is open, whatever its other labels say.
In one sentence. Every existing row is handled by the fate its table was assigned: tables that lose the column just lose it; mirror rows copied out of code (each organization's read-only duplicate, and the NULL residue of the same) are deleted only after every reference has been rewritten to a name and verified; an organization's own rows get their owner back through a parent record; and a row whose owner cannot be recovered is neither guessed at nor deleted — it is named at boot, per table.
The migration is a manual operator ceremony, never a boot step. Maintainer, 2026-09-04: 「我建议18.0 的主要考虑是客户数据变化比较大,而且需要手工执行升级脚本。」
Scope. Inventory file seeded from the two #13564 ledgers (59 platform + 28 example objects) plus the cloud supplement, one fate per object with a citation. An
os migrate-family command provides:--plan(read-only — per-table fate, row counts, unattributable row ids, which tables will receiveNOT NULL; written to a file; refuses rather than reporting a table it cannot enumerate), an explicit backup acknowledgement,--apply(fate order: attribution → verified id→name rewrite → mirror deletion → column drops; idempotent and resumable from a recorded checkpoint), and a post-check that re-runs the plan and prints zero-remaining per table.The four fates: (1) column drop through the ADR-0120 D4 ceremony; (2) mirror deletion — rows whose
managed_byispackage/platformon the catalog objects, seeded templates, seeded capabilities, and the NULL residue of the same — gated on C2's rewrite report showing every reference resolves by name; (3) attribution via a parent anchor (childKey/parentObject/parentOrgColumn, generalizingbackfill-sys-file-organizations.ts,plugin-approvals/src/backfill-platform-row-organizations.tsand cloud'sorg-id-backfill.ts); undersinglethe Default Organization; (4) report — per table, rows still NULL, with the remedy. Boot refusal: a v18 runtime that detects an un-migrated database (schema marker written by the ceremony's last step) refuses to start and names the command — ADR-0093 D5 shape, ⛔ with no env escape hatch that skips the check.Absorbs: #14570 (
sys_business_unit_memberunadjudicated, org-less rows) and #15086 (the NULL-org-seeded business unit unreachable from an org-stamped rule, residue of the #15030 revert) — both are populations this inventory must name a fate for. Read both before writing the inventory.Acceptance. A fixture database carrying every fate — per-organization mirrors with grants pointing at them, NULL catalog residue, NULL
sys_filerows withsys_attachmentholders, customized template rows, and one genuinely unattributable row — comes through the migration with effective access identical (positive control: a grant that would be lost reddens the pin), mirrors gone, attributed rows carrying their organization, the unattributable row still present and reported, and row counts reconciling once mirrors and dropped columns are accounted for.⛔ Stop and report: any deletion outside fate 2; any assignment of an unattributable row.
Refs: ADR-0131 D10 · ADR-0093 D5 · ADR-0120 D4 · #10103 (warn-not-reap superseded) · the 2026-08-28 backfill ruling · cloud#1664 item 5 · #14570 · #15086.