Skip to content

os migrate organization-ownership never certifies a database whose packages declare sharing rules: it waits on the declared-rule-mirrors departure (D2) while the same release's boot still seeds them #23948

Closed

Description

@hotlong

Filing-gate category ① (a defect with a measured reach) and ③ (a maintainer direct order). Verbatim, 2026-10-16: the option 「框架先放行这一项」, which reads: "在启动流程还会种声明共享规则的版本上,仪式不把这些镜像当成阻断项,只列为提示;等 D2 随 #15204 落地后再恢复阻断". Earlier orders: 「相关任务都提到最高优先级」 · 「算了,你如果能够派云卡片,相关任务你都统一派发处理吧」.

Reader: triage routes it, expected domain:cli (the ceremony's inventory/apply). The executing lane seat claims it. Downstream, cloud's next pin bump carries the fix (objectstack-ai/cloud#2868).

Dedupe (gh api search/issues total_count, closed issues included):

What happens

On a v17 database where a package declares sharing rules, the v18 organization-ownership ceremony applies with complete: true, blocking: 11 and never certifies (verified_at null). HotCRM declares 11 sharing rules (sys_sharing_rule, managed_by='package', customized=0). The ceremony counts their rows as the departure declared-rule-mirrors ("D2 retires bootstrapDeclaredSharingRules"), and certification waits for them to leave.

At the same pin, the boot still writes them. plugin-sharing's bootstrapDeclaredSharingRules is unchanged at f7e836da1ed3 and on main; one of the 11 rows was seeded by the first v18 kernel, before the ceremony ran. So the departure the ceremony waits for can never happen on that release. D2's retirement is scoped in the open epic #15204, which is blocked by #15196.

Reach: measured on a local sqld remote face by objectstack-ai/cloud#2868 round 11 (comment on that card, "Interim evidence from round 11"). The world was v17 cloud c8eca869 + framework 56bf27affb with HotCRM 4.0.1, upgraded to cloud main (framework f7e836da1ed3) and HotCRM 5.0.0.

  • Staging: every hosted environment with HotCRM installed (10 on staging) holds these rows, so its tenant v18 upgrade cannot verify. Staging 9930e6cf is very likely in that state; not yet measured there, because the HotCRM position conflict (cloud#2868 round 11) surfaces first.
  • Production: the 5 demo environments are the same, so production v18 waits on this.

Wanted

Acceptance:

  • on a v17-shaped database with a package's declared sharing rules (HotCRM 4.0.1 or a fixture), --apply reports blocking 0 with the sharing-rule rows in advisory, and the marker verifies;
  • the existing framework test "does not certify a ceremony whose departures have not left: the declared sharing-rule mirrors keep verified_at null" is updated to the new rule, with a test that pins the restore condition;
  • an ablation turns the apply's blocking back to 11.

Related: #15204 (D2 epic), #15196, #23816 / #23843 (the remote ceremony), objectstack-ai/cloud#2868, objectstack-ai/cloud#2899.

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    Contributor

    Triage: P0 routed, domain:cli · area:access · priority:p0 and target:v18 stand · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T13:55Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop, serial dispatch (the maintainer to this seat: 「当前任务处理完,后续改为串行派发」; this seat has no dev in flight, and #23743's PR #23947 only waits in the merge queue). Label order: p0 target:v18 ranks first in domain:cli's pm:queue (read in this act). #23948: on a release whose boot still seeds declared sharing rules, the organization-ownership ceremony stops holding certification on the declared-rule-mirrors departure, reports those rows as advisory (named, counted), and pins the condition that restores the blocking count when #23952 retires the seeder · 2026-10-16T14:18Z
    Session: session_01PFB9ZGRWYvjhTXsd8i6KBp
    Account: marchtian (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-23948-declared-rule-mirrors-advisory
    Worktree: objectstack-issue-23948
    Domain: domain:cli
    Seat: domain:cli#1 (seat post #6024)
    File surface (read on origin/main 61e5ee9e57; line numbers move; stop on a breach and explain it in the report):


    Generated by Claude Code

  3. hotlong commented on Oct 16, 2026

    @hotlong
    ContributorAuthor

    Reach, measured on cloud staging (cloud#2868 PM, 2026-10-16 15:32Z). ⛔ Not a claim.

    The card's "Staging 9930e6cf is very likely in that state; not yet measured" is now measured. Staging runs framework f7e836da1ed3 with cloud f6a7af86; environment 9930e6cf has HotCRM 5.0.0 installed.

    Its tenant upgrade ran the ceremony to complete: true, with every step checkpointed, and then stopped. The reason:

    the platform ceremony ran every step and does not certify the database (sys_sharing_rule: 11 row(s) remain (uncustomized declared sharing rules — seeded copies of code (D2 retires bootstrapDeclaredSharingRules): 11))

    Every other step of cloud's sequence before it passed, including round 11's catalog cleanup. This is the environment's only blocker. All 10 staging environments with HotCRM, and the 5 production demo environments, are expected to land here once their other blockers clear.

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 23948,
    "status": "done",
    "branch": "claude/issue-23948-declared-rule-mirrors-advisory",
    "pr": "#23988",
    "session": "session_01PFB9ZGRWYvjhTXsd8i6KBp (mode:subagent, the PM's session id)",
    "premise_still_valid": true,
    "summary": "Premise reproduced on base 161aee1 at the door, before any edit. The fixture is a served boot of this release (verify's bootStack over a SQLite file) of a package declaring one sharing rule, plus a 17.x seeder's ten organization-less declared rules: HotCRM's 10+1 shape. Plan then --apply --dev gave complete true, blocking 11, advisory 0, exit 1, and the marker kept verified_at NULL. The fix stays inside packages/cli and is no wider than the order. The inventory states DECLARED_SHARING_RULE_SEEDER_RUNS = true, and advisoryDepartureReason(object, departureId) keys the rule on sys_sharing_rule + declared-rule-mirrors only. The plan marks that departure advisory, keeps the table's NOT NULL reading false with the reason (it used to read willReceive true), and warns before the first write. The apply's post-check leaves the rows out of remaining and blocking and counts them in advisory. The human summary names advisory rows and unattributable rows separately. Every other departure and every other remaining row still blocks; nothing is deleted. The inventory digest is unchanged (05ba6e8512e2 before and after). The PR body cites staging's measured reach (card comment 6218950893: 9930e6cf stops on exactly these 11 rows).",
    "restore_mechanism": "The constant DECLARED_SHARING_RULE_SEEDER_RUNS in packages/cli/src/utils/organization-ownership-inventory.ts, read by advisoryDepartureReason beside it. Flip point for #23952: set it to false in the same change that retires bootstrapDeclaredSharingRules; the mirrors then block again until that stage's ceremony step deletes them. Tied to the seeder by a behavioural pin, not a source pin: packages/cli/src/commands/migrate/organization-ownership.declared-rule-mirrors.integration.test.ts case 1 boots a served stack declaring one sharing rule and asserts that the rows that boot wrote equal what the constant says. While true, that is one managed_by 'package', uncustomized, organization-less row; once false, none. It fails with a message naming the constant when the seeder retires. Rejected: a source pin over sharing-plugin.ts:775 (a cross-package read needing declarations in scripts/cross-package-test-inputs.mjs and turbo.json, outside the surface; it also pins a call, not behaviour); a capability exported by plugin-sharing (needs an edit under plugin-sharing and a new published export, both refused); the ADR-0087 entry #23952 will write (it does not exist yet, so a guessed id would be a version string in disguise).",
    "tests": "Positive controls on unchanged code: the old apply test at organization-ownership-apply.test.ts:885 passed at blocking 1 (161aee1); the door fixture gave blocking 11 with verified_at NULL (CLI dist built at 161aee1). New and updated tests, all green:\n(1) organization-ownership-apply.test.ts 'certifies a ceremony whose only remaining NULL rows are the declared sharing-rule mirrors this release still seeds: advisory, named, kept'.\n(2) NEW utils/organization-ownership-apply.declared-rule-mirrors.test.ts, HotCRM's eleven, 3 cases: [complete, blocking, advisory] = [true, 0, 11] with nothing deleted; the RESTORE pin with the seeder retired (vi.mock wrapper passing seederRuns false) gives [true, 11, 0], verdict blocking 11 and no warning; a host supplement departure named declared-rule-mirrors on its own table still blocks [true, 2, 11].\n(3) NEW commands/migrate/organization-ownership.declared-rule-mirrors.integration.test.ts, 2 cases. The served-boot tie to the constant. The door: plan, then --apply --json exits 0 with blocking 0 and advisory 11; sys_migration verified_at set, blocking 0, advisory 11; 13 rules kept. Then a served boot with OrganizationOwnershipBootGuardPlugin composed starts while sys_job carries organization_id again: findV17ShapeTables gives ['sys_job'] and readOrganizationOwnershipMarker gives verified true.\n(4) remote-face.integration.test.ts now carries 11 mirrors; run against a real sqld (@sqld/linux-x64 0.24.1-pre.42, sqld 0.24.32): blocking 0, departure advisory with rows 11, marker applied 1 / verified 1. The test starts and kills its own sqld; none left running.\nUpgrade paths measured with the CLI binary: RESUME (same digest, same plan file) gave all nine steps checkpointed, post-check only, blocking 0, verified. RESTART (checkpoint under another digest, staging's case) re-planned, restartedFrom all nine, each step done, blocking 0, advisory 11, verified, 13 rules kept.\nABLATION on committed 5c0aa5c via scripts/ablation-replace.mjs: '(d.advisory ? 0 : d.rows)' became '(d.rows)' in departingRows (anchor 1 to 0, blob e3237c0dc629 to a5f7d694d01e). The 11-row case went red, 'expected [ true, 11, 11 ] to deeply equal [ true, +0, 11 ]', so blocking was back at 11. The keyed case went red [ true, 13, 11 ]; the restore case stayed green. Restore proven: blob == HEAD e3237c0dc629 and git diff HEAD empty. The subject resolves by relative import to src, so no dist was involved.\nSuites on 5c0aa5c under os-verify-lock: CLI unit tier 'vitest run --project unit --maxWorkers=2' gave 305 files / 4558 tests passed (VERDICT command-exit 0). The 26 test files reaching the ceremony's modules, both tiers, with OS_TEST_SQLD_BIN set, gave 26 files / 342 tests passed (VERDICT command-exit 0).",
    "gates": "Local, derived with 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' at 5c0aa5c: 66 families, all run, every exit captured before any pipe. All 66 exit 0. check:dual-build-cjs-loads and check:i18n-coverage first answered exit 3, PREREQUISITE NOT MET (packages outside the CLI closure had no dist); after building those, both answered 0. '--ran' with an exit per line: '66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED (a DERIVED zero — all 66 recorded an exit code and none of them is 3)'. Last commit 98f8764 changes only the changeset text: the 20 families derived for that file re-ran on 98f8764, all exit 0 (adr-0087-registration, changeset-no-major, empty-changeset, closing-keyword-parity, comment-mask-corpus, changeset-gate-self-tests, objectui-changeset, pm-changeset-deadline-census, nul-bytes, published-files and the rest of that list). The other 46 stand from 5c0aa5c (declared narrowing). Lane blind spot: 'pnpm exec eslint --no-inline-config' over the 8 changed .ts files, exit 0. 'pnpm --filter @objectstack/cli typecheck' on 98f8764, exit 0 (test layer at its pinned 3 files / 28 errors). check-platform-checklist not owed: no exported symbol renamed or deleted. CI on PR #23988 head 98f8764 at report time: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; every other check-run in_progress (Check Changeset, Governed Surface Queue Guard, Lint · CI release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped ×3, Type Check · consumer gates / debt ledger / source gates / workspace, filter, the claim and closing-keyword checks). Test Core and Build Core had not yet been created.",
    "line_budget": "n/a (no governed or ratcheted line ledger touched)",
    "files_changed": [
    ".changeset/23948-declared-rule-mirrors-advisory.md (new; @objectstack/cli minor; line-initial Clause-②: yes (widening — …))",
    "packages/cli/src/utils/organization-ownership-inventory.ts (DECLARED_SHARING_RULE_SEEDER_RUNS, advisoryDepartureReason; no inventory row changed)",
    "packages/cli/src/utils/organization-ownership-plan.ts (PlannedDeparture.advisory, NOT NULL reading, the warning)",
    "packages/cli/src/utils/organization-ownership-apply.ts (post-check: remaining excludes advisory rows, advisory counts them, TableOutcome.departures[].advisory)",
    "packages/cli/src/commands/migrate/organization-ownership.ts (human summary lines; doc)",
    "packages/cli/src/utils/organization-ownership-apply.test.ts (the named test updated)",
    "packages/cli/src/utils/organization-ownership-apply.declared-rule-mirrors.test.ts (new)",
    "packages/cli/src/commands/migrate/organization-ownership.declared-rule-mirrors.integration.test.ts (new)",
    "packages/cli/src/commands/migrate/organization-ownership.remote-face.integration.test.ts (11 mirrors added)"
    ],
    "deviations": [
    "Before opening, I merged origin/main 2e0e451 locally (AGENTS.md §10's pull-before-open) and rebuilt. On the coordinator's note that no merge was owed, I dropped that unpushed merge commit (reset to my pushed head 5c0aa5c); the PR carries no merge. The workspace dist left by that build (main 2e0e451 plus this change) is what the RESTART measurement and the head typecheck read.",
    "Gate narrowing after the last commit (changeset text only), as stated in 'gates'.",
    "The dispatch's suggested negative leg, 'an unattributable non-mirror row still keeps blocking above 0', is false on main: unattributable rows were already advisory (advisory = after.summary.unattributableRows at 161aee1). The negative leg used is another departure that has not left (a host departure of the same id). Recorded in out_of_scope_findings, not changed.",
    "The first typecheck ran before the test files existed; it was re-run on the head (see gates).",
    "No label written: the dispatch names none, and skip-changeset does not apply (a changeset ships).",
    "One shell loop of mine that began with 'rm -f' on a variable path was refused by the harness's removal check. It was re-issued without the removal; nothing was deleted by it."
    ],
    "mcp_calls": "0",
    "api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write workflow as objectstack-fleet[bot]. (1) fw-20261016T162308Z-ab3f54, run 38931531915: pr_create, which sent POST /repos/objectstack-ai/objectstack/pulls (draft #23988, body read back byte-identical, 16791 bytes) and POST /repos//issues/23988/assignees (marchtian, read back). (2) This os-dev-report comment, via scripts/pm/post-stamped.mjs: POST /repos//issues/23948/comments. Plus git push (not REST), 0 label writes, and no PR body PATCH.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: #23952 (its ordering ruling's owner) · noted, not filed. The ruling says 'any other NULL row keeps blocking both the marker and this stage'. On main before this change, unattributable rows (fate 4 reported rows, and fate 3 rows no anchor names under a wall) were advisory, never blocking: organization-ownership-apply.ts post-check, advisory = after.summary.unattributableRows, at 161aee1. Whether the sentence means to make them block the marker is that ruling's reading; this PR changes neither. Dedupe words: unattributable advisory marker zero-NULL ordering ruling verified_at",
    "carrier: #23952 · noted, not filed. After certification every served boot re-runs the seeder's upsert, and a rule a package adds later lands as a new organization-less mirror; the marker is not re-read at boot, so nothing re-blocks. Stage 9's deletion step covers them.",
    "carrier: none (承接者:无) · Acceptance notes only. The new door file runs about 18 s of tests plus about 37 s of import on the shared box (four boots); remote-face is similar with sqld. Both are over the 10 s per-file line, and no test-budget class fits."
    ],
    "cleanup": "Removed the worktree's root node_modules before posting. Every process I started has exited: two detached runs (PIDs 20824, 27590), sqld (stopped by its test), the background build. The worktree ../objectstack-issue-23948 is removed with 'git worktree remove' (no --force) right after this comment is read back. The branch head 98f8764 equals origin, and the tree is clean."
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #23988 comment 6220254873 · contract review 6220022536 (PASS, TIER_DEFAULT) · domain:cli seat 1 · marchtian · 2026-10-16T16:43Z. Carried to #23952 at landing: the flip point of DECLARED_SHARING_RULE_SEEDER_RUNS and the report's two readings. Landing follows on this true-green head.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #23988 merged through the queue as adce192974 · domain:cli seat 1 · marchtian · session session_01PFB9ZGRWYvjhTXsd8i6KBp · 2026-10-16T17:07Z.

    Reading Value
    PR #23988 merged: true, merged_at 2026-10-16T17:06:03Z; timeline added_to_merge_queue 2026-10-16T16:44:21Z
    origin/main adce192974 is an ancestor (fetched in this act); single parent (squash)
    Queue branch gh-readonly-queue/main/pr-23988-*: 0 refs left
    Landed content DECLARED_SHARING_RULE_SEEDER_RUNS: boolean = true: 1 hit in packages/cli/src/utils/organization-ownership-inventory.ts; advisoryRows: 2 hits in organization-ownership-apply.ts, on origin/main

    State: #23948 closed completed by Fixes #23948, the PR body's only closing keyword; pm:dispatched stripped in this act (domain:cli, priority:p0, target:v18, area:access, bug stay). Contract review of record: 6220022536 (PASS, TIER_DEFAULT). This seat's hold on the ceremony's inventory and post-check is released. The flip point and the report's two readings are handed to #23952 in the same act. Downstream, cloud's next pin bump carries this to the environments the card names (objectstack-ai/cloud#2868).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:clipriority:p0Critical: blocker, must ship before MVPtarget:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions