Skip to content

feat(cli): os migrate organization-ownership reports the declared sharing-rule mirrors as advisory while this release's boot still seeds them - #23988

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23948-declared-rule-mirrors-advisory
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23948-declared-rule-mirrors-advisory

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23948

Clause-②: yes (widening — the apply report file, the plan file and both --json payloads gain an advisory field on a departure entry, and the run's advisory count includes that departure's rows)

What changes

On a v17 database whose packages declare sharing rules, os migrate organization-ownership --apply ended with complete: true, blocking: 11 and never certified. The adr-0131-organization-ownership marker kept verified_at null. The post-check waited for the declared-rule-mirrors departure on sys_sharing_rule to leave. That departure is the uncustomized managed_by in (package, platform) rows with no organization. A served boot of this release writes exactly those rows, so on this release they never leave.

Reach. Measured on cloud staging by the cloud#2868 PM (card comment 6218950893, 15:32Z): environment 9930e6cf runs framework f7e836da1ed3 with HotCRM 5.0.0. Its tenant upgrade ran the ceremony to complete: true and stopped on sys_sharing_rule: 11 row(s) remain (uncustomized declared sharing rules — …: 11), the environment's only blocker. All 10 staging environments with HotCRM and the 5 production demos are expected to land there.

The maintainer's order (quoted on the card) is 「框架先放行这一项」: "在启动流程还会种声明共享规则的版本上,仪式不把这些镜像当成阻断项,只列为提示;等 D2 随 #15204 落地后再恢复阻断". The director's ordering ruling for C3 stage 9 (#23952, "The order (ruled)") scopes it: the zero-NULL gate "is read as satisfied by a D10 report whose only remaining NULL rows are the declared-rule-mirrors the ceremony itself charts for this stage; any other NULL row keeps blocking". This PR implements that, and nothing wider:

  • The plan (organization-ownership-plan.ts) marks a departure advisory when no step of this release can carry its rows out (PlannedDeparture.advisory: why, and what removes them). Their NULL rows keep the table's NOT NULL reading willReceive: false with N row(s) stay NULL on this release, reported as advisory: LABEL. Before this change the plan read willReceive: true for sys_sharing_rule on such a database, which was not true on this release. One warning per such departure tells the operator before the first write, in the plan and again in --apply: sys_sharing_rule holds N organization-less row(s) of LABEL, which stay: REASON ....
  • The apply's post-check (organization-ownership-apply.ts) leaves advisory rows out of remaining, so they are not discrepancies and not blocking. It counts them in the run's advisory, beside the unattributable rows, so the ledger verdict { complete, blocking, advisory } carries them. Nothing new is deleted or attributed: the rows keep organization_id NULL.
  • The command's human summary (organization-ownership.ts) prints N row(s) advisory, not held against certification — LABEL: REASON under the table. Its closing warnings now name the unattributable rows and the advisory rows separately. Before, it said "N row(s) whose owner cannot be derived" for the whole advisory count.
  • What still blocks: global-rung-move, template-promotion, every host fate supplement departure (even one named declared-rule-mirrors: the rule is keyed on the table AND the id), and every other remaining row.

The --json / report-file / plan-file shape change (additive)

  • report.tables[].departures[] (--json and the --out report file): { id, label, as, rows } gains advisory?: string. Present means these rows stay on this release and do not hold certification. Absent means the departure blocks, exactly as before.
  • report.tables[].remaining: no longer counts an advisory departure's rows.
  • report.advisory and the sys_migration row's advisory: unattributable rows plus advisory departure rows. The field's doc already said "and rows held for another stage", and now the count includes them.
  • plan.tables[].departures[] (plan file and plan --json): the same advisory?: string. plan.tables[].notNull for the table reads as above, and plan.warnings and report.warnings gain the sentence above.
  • The inventory digest is unchanged: 05ba6e8512e2… before and after this change. The verdict rule is not inventory data. So a plan reviewed on the parent commit still executes, and an interrupted ceremony still resumes.

The restore condition (the hand-off to #23952)

DECLARED_SHARING_RULE_SEEDER_RUNS (packages/cli/src/utils/organization-ownership-inventory.ts, true) states that a served boot of this release still seeds declared sharing rules. advisoryDepartureReason(object, departureId) beside it is the one rule: it answers only for sys_sharing_rule + declared-rule-mirrors, and only while the constant is true. The flip point for #23952: set the constant to false in the change that retires bootstrapDeclaredSharingRules. From then the mirrors block again until that stage's ceremony step deletes them.

What makes that flip mechanical rather than remembered is a behavioural pin, not a source-text pin. organization-ownership.declared-rule-mirrors.integration.test.ts, case 1, boots a served stack of this release over a SQLite file: @objectstack/verify's bootStack, which is os serve's composition with plugin starts. The package declares one sharing rule. The case asserts the rows that boot wrote equal what the constant says: one managed_by 'package', uncustomized, organization-less row while true, and none once false. When the seeder retires, the boot writes nothing, and the case fails with a message naming the constant until it flips.

Alternatives weighed:

Measurements

All on this branch unless stated. Base 161aee1047, head 98f87644e1. Not merged with main: the head merge-trees clean against it, including #23874's sys_scim_provider row in the same inventory file (disjoint hunks).

  • Positive control on unchanged code, at the door (CLI dist built at 161aee1047). The served boot plus 17.x leftovers fixture is HotCRM's shape: ten declared rules a 17.x seeder left, plus one the served boot of this release seeded, plus an organization's rule, a customized declared rule and a sys_job still carrying organization_id. Plan and --apply (--dev) gave complete: true, blocking: 11, advisory: 0 and exit 1. The marker read verified_at NULL, applied_at set, blocking 11. The unit-level positive control: the old test at organization-ownership-apply.test.ts:885 passed on unchanged code with blocking: 1.
  • The fix, at the door (organization-ownership.declared-rule-mirrors.integration.test.ts, case 2). --apply --json exits 0 with complete: true, blocking: 0, advisory: 11. The sys_sharing_rule departure carries rows: 11 with advisory, and remaining is 0. sys_migration reads verified_at set, blocking 0, advisory 11. All 13 rule rows are kept, 11 still NULL. Then a served boot of the certified file starts, with OrganizationOwnershipBootGuardPlugin composed and sys_job's organization_id put back so the guard has a v17-shaped table and decides on the marker. findV17ShapeTables answers ['sys_job'], and readOrganizationOwnershipMarker, os migrate apply's drop gate, reads verified: true.
  • A database that already ran --apply and stopped at blocking: 11, both upgrade paths:
    • Same inventory digest (resume). The pre-fix fixture above, re-run with this branch's CLI and the same reviewed plan file: every step checkpointed, resumedFrom all nine, post-check only. Result complete: true, blocking: 0, advisory: 11, the marker verified, 13 rules kept.
    • Another digest (restart). This is staging's case: f7e836da1ed3's inventory differs from this base's (git diff --stat 38+/1- on the inventory file), and main has since added fix(cli): os migrate organization-ownership plans a v17 database still holding the retired sys_scim_provider, as fate 4 (ADR-0131 D10) #23874's row. Same fixture with the checkpoint's digest set to another value and the marker set back to blocking 11 / verified_at NULL. Plan again, then --apply, with this change built over main 2e0e4517a6: restartedFrom names all nine steps, each runs again as done (idempotent), and the result is complete: true, blocking: 0, advisory: 11, verified, 13 rules kept.
  • Remote face (sqld). @sqld/linux-x64 0.24.1-pre.42 (sqld 0.24.32), reached as libsql://127.0.0.1:PORT?tls=0. organization-ownership.remote-face.integration.test.ts now carries 11 organization-less declared rules. Plan, then --apply: blocking 0, the departure advisory with rows: 11, the marker applied 1, verified 1, 11 NULL rules kept. Run locally with OS_TEST_SQLD_BIN set; the test starts and stops its own sqld. CI skips it without the binary.
  • Ablation (the card's negative control). On committed 5c0aa5c752, scripts/ablation-replace.mjs replaced (d.advisory ? 0 : d.rows) with (d.rows) in departingRows (anchor 1 to 0, blob e3237c0dc629 to a5f7d694d01e). organization-ownership-apply.declared-rule-mirrors.test.ts then went red as predicted: expected [ true, 11, 11 ] to deeply equal [ true, 0, 11 ] ([complete, blocking, advisory], so the apply's blocking was back at 11). The keyed case went red as [ true, 13, 11 ], and the restore case stayed green. The restore was proven: blob equal to HEAD (e3237c0dc629) and git diff HEAD empty. The subject resolves by relative import to src/, so no rebuild was involved.
  • Every reader of the marker (dispatch item 3). The boot guard (organization-ownership-boot-guard.ts:260), the drop gate (organization-ownership-drop-gate.ts:68), platform-objects' isDataMigrationVerified, and objectql's two reads (engine.ts:11525, :11685) all go through isDataMigrationFlagVerified: verified_at set and blocking === 0. None reads advisory. The command writes passed = complete && blocking === 0 (organization-ownership.ts:772). So blocking 0 certifies everywhere the marker is read.
  • What the ceremony's own boot does to those rows (dispatch item 2). Nothing. The apply boot composes SharingServicePlugin through composeProviderForDeclarations, with start() suppressed (schema-migration-plugins.ts). The plan and apply runs left sys_sharing_rule byte-identical, apart from the customized rule's attribution. A served boot does seed: measured in case 1 and in the fixture's eleventh row. That boot is os serve / os dev, the one the v18 refusal stops at kernel:ready, after plugin starts.
  • The NOT NULL reading for sys_sharing_rule (dispatch item 4). willReceive: false with 11 row(s) stay NULL on this release, reported as advisory: uncustomized declared sharing rules — …, in both the plan and the report. The ceremony has no NOT NULL DDL step; the reading is the report's statement, and it stays true.

Tests

  • src/utils/organization-ownership-apply.test.ts: the test formerly titled "does not certify a ceremony whose departures have not left: the declared sharing-rule mirrors keep verified_at null" now reads "certifies a ceremony whose only remaining NULL rows are the declared sharing-rule mirrors this release still seeds: advisory, named, kept". It asserts blocking 0, the verdict, the departure's advisory, remaining 0, the NOT NULL reason, the warning and the rows kept.
  • src/utils/organization-ownership-apply.declared-rule-mirrors.test.ts (new, integration tier: it value-imports better-sqlite3), HotCRM's eleven:
    • certifies: [true, 0, 11], nothing deleted, the customized rule attributed;
    • the restore pin: with the seeder retired (advisoryDepartureReason answered with seederRuns: false through a vi.mock wrapper; the other cases go through the shipped constant), the same fixture gives blocking 11, the verdict not passing, and no warning;
    • the negative leg: a host fate supplement departure named declared-rule-mirrors on its own table still blocks (blocking 2) beside the advisory mirrors.
  • src/commands/migrate/organization-ownership.declared-rule-mirrors.integration.test.ts (new, integration tier): the served-boot tie (case 1) and the door (case 2) above.
  • src/commands/migrate/organization-ownership.remote-face.integration.test.ts: 11 mirrors in its 17.x leftovers, and their advisory departure asserted.

Local runs

All on 5c0aa5c752 unless stated. Every build and test went through os-verify-lock.sh, with the exit read from its VERDICT line.

  • CLI unit tier: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 gave 305 files and 4558 tests passed, VERDICT command-exit 0.
  • Every test file reaching the ceremony's modules, both tiers (26 files: organization-ownership*, sqlite-wasm, schema-migrate.one-shot-family, schema-migrate.sql-driver-lookup), with OS_TEST_SQLD_BIN set: 26 files and 342 tests passed, VERDICT command-exit 0.
  • pnpm --filter @objectstack/cli typecheck (tsc --noEmit plus check:test-typecheck, the new test files included) on 98f87644e1: exit 0, test layer unchanged at its pinned 3 files / 28 errors. The workspace dist/ it read was built from main 2e0e4517a6 plus this change.
  • pnpm exec eslint --no-inline-config over the eight changed .ts files: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 66 families, and all 66 ran with exit 0 (--ran with an exit code per line: 66 derived, 66 run, 0 NOT-MEASURED). 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 packages, both answered 0.

After the last commit (98f87644e1, which changes only the changeset text), the 20 families the derivation names for the changeset file ran again on that head, all exit 0. The other 46 read none of the changed bytes, and their readings stand from 5c0aa5c752. The families CI runs are cited from this PR's check-runs in the report.

Acceptance notes


Generated by Claude Code

…haring-rule mirrors as advisory while this release's boot still seeds them

A served boot of this release still writes the package-declared sharing
rules into sys_sharing_rule (plugin-sharing's seeder), organization-less
under single -- the population the declared-rule-mirrors departure waits
for. On this release that departure cannot happen, so the ceremony never
certified a database whose packages declare sharing rules.

DECLARED_SHARING_RULE_SEEDER_RUNS (the inventory) states that the seeder
still runs; advisoryDepartureReason keys the rule on the table and the
departure id. The plan marks those departures advisory, keeps the table's
NOT NULL reading false with the reason, and warns before the first write;
the apply's post-check counts their rows in advisory, never in remaining
or blocking. Every other departure and remaining row still blocks.

Claude-Session: https://claude.ai/code/session_01PFB9ZGRWYvjhTXsd8i6KBp
Co-authored-by: Claude <noreply@anthropic.com>
…estore and its keying

The existing "departures have not left" case reads the new rule: the one
rule mirror is advisory, named and counted, the verdict is blocking 0 and
nothing is deleted. A HotCRM-shaped fixture (eleven mirrors) pins blocking
0, the restore once the seeder retires (blocking 11 again), and that a host
departure under the same id on its own table still blocks.

Claude-Session: https://claude.ai/code/session_01PFB9ZGRWYvjhTXsd8i6KBp
Co-authored-by: Claude <noreply@anthropic.com>
…haring rules, at the door and on the remote face

A served boot of this release (verify's bootStack) of a package declaring
one rule writes it organization-less as package-managed, which is what
DECLARED_SHARING_RULE_SEEDER_RUNS states; that case fails until the
constant flips when the seeder retires. Plan and --apply --json then
certify HotCRM's shape (eleven mirrors): blocking 0, advisory 11, the
sys_migration marker verified, every rule kept, and a served boot with the
v18 boot guard composed starts while a v17-shaped table stands. The sqld
remote-face case now carries eleven mirrors too.

Claude-Session: https://claude.ai/code/session_01PFB9ZGRWYvjhTXsd8i6KBp
Co-authored-by: Claude <noreply@anthropic.com>
…plans again; resume or restart both certify

Claude-Session: https://claude.ai/code/session_01PFB9ZGRWYvjhTXsd8i6KBp
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 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 16 documentable anchor(s).

4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))
  • content/docs/deployment/cli.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))
  • content/docs/permissions/capabilities.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))
  • content/docs/permissions/permission-sets.mdx (via os migrate organization-ownership (command, read off packages/cli/src/commands/migrate/organization-ownership.ts))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via sys_sharing_rule (literal, a string literal in DECLARED_RULE_MIRRORS))
  • content/docs/releases/v17/17-1.mdx (via sys_sharing_rule (literal, a string literal in DECLARED_RULE_MIRRORS))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 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 — 29 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 022c6e799e88ae192e124c80e1cc343bda54d7bf → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 37a78b1282b1a3d535221e4095f33ee05577d80d — the merge of head 98f87644e16e6bd5894cfa3ae956ac640dc5eee9 into base 022c6e799e88ae192e124c80e1cc343bda54d7bf, 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 37a78b1282b1a3d535221e4095f33ee05577d80d && git checkout 37a78b1282b1a3d535221e4095f33ee05577d80d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 022c6e799e88ae192e124c80e1cc343bda54d7bf 98f87644e16e6bd5894cfa3ae956ac640dc5eee9 && git checkout -B drift-repro 022c6e799e88ae192e124c80e1cc343bda54d7bf && git merge --no-ff 98f87644e16e6bd5894cfa3ae956ac640dc5eee9

node scripts/docs-audit/affected-docs.mjs --json 022c6e799e88ae192e124c80e1cc343bda54d7bf

⚠️ 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 022c6e799e88ae192e124c80e1cc343bda54d7bf → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 98f87644e16e6bd5894cfa3ae956ac640dc5eee9
Local-runs: none

Scope: owed by the Clause-②: yes declaration alone (no packages/spec/src/**, no governed path, no (narrowing) declaration on this PR's committed tree). Read: the PR's diff at this head, card #23948 with its claim 6217499012 and the staging reach 6218950893, #23952's ordering ruling, the os-dev-report 6219947656, and the head's check-runs.

① Derived judgments

  1. What is now accepted. A database whose only remaining organization-less rows are the declared-rule-mirrors of sys_sharing_rule certifies: blocking 0, verified_at set. Before, the post-check held certification on rows this release's own served boot keeps writing (bootstrapDeclaredSharingRules), so the ceremony could never certify such a database. That is the widening the declaration names. It matches the maintainer's order on the card (「框架先放行这一项」) and C3 stage 9 (#15204): declared sharing rules read from the registry, bootstrapDeclaredSharingRules retires, and the ceremony deletes the declared-rule mirrors; the verified marker follows (director ordering ruling) #23952's ordering ruling ("the D13/D14 zero-NULL gate is read as satisfied by a D10 report whose only remaining NULL rows are the declared-rule-mirrors").
  2. Nothing else loosens. advisoryDepartureReason is keyed on the table AND the departure id. A host fate supplement's departure of the same id on another table still blocks (pinned), and so do global-rung-move, template-promotion and every other remaining row. remainingFor drops only the advisory rows (departingRows); advisoryRows adds the same rows to the run's and the ledger's advisory. Nothing is deleted, and the rows keep organization_id NULL.
  3. The output shape. In the plan file, the apply report (--json and the --out file), the entry of tables[].departures[] gains an optional advisory string, and the plan's warnings gain one line before the first write. tables[].remaining excludes those rows, and the run's advisory count includes them. All of it is additive. The plan's notNull for sys_sharing_rule now reads willReceive: false with its reason, where it read true. The old reading was untrue: the rows stayed NULL. notNull.willReceive is report-only; no step reads it to write. No published schema carries these documents.
  4. The restore condition is declared, not inferred. DECLARED_SHARING_RULE_SEEDER_RUNS = true (the inventory module) decides the verdict, as the card prefers ("a declared capability or constant … over a version string"). A behavioural pin (the door file's case 1: a served boot of this release writes exactly the row the departure names) fails, naming the constant, once the seeder retires. C3 stage 9 (#15204): declared sharing rules read from the registry, bootstrapDeclaredSharingRules retires, and the ceremony deletes the declared-rule mirrors; the verified marker follows (director ordering ruling) #23952 flips it in the change that retires the seeder. Rejected with reasons in the PR: a source pin over sharing-plugin.ts:775, a capability exported from plugin-sharing (an edit outside the claim's surface and a new published export), and a guessed ADR-0087 entry id.
  5. Published surface. DECLARED_SHARING_RULE_SEEDER_RUNS and advisoryDepartureReason are new exports of src/utils/organization-ownership-inventory.ts. None of @objectstack/cli's entries (., ./console, ./hook-body) re-exports that module, so neither is a published symbol. No inventory row changes; the report reads the inventory digest as unchanged (05ba6e8512e2 before and after). The verdict rule changes no write, so a plan reviewed under either rule executes the same writes.

② Semver level

Clause-②: yes takes at least minor. The changeset declares @objectstack/cli: minor, carries the line-initial Clause-②: yes (widening — …), and names the advisory field, the remaining / advisory counts, the notNull reading, the warning, what still blocks, the path for an environment already stopped at blocking: 11, and when blocking returns. Consistent.

③ Boundary flags

Implemented-by: claude/issue-23948-declared-rule-mirrors-advisory
Reviewed-by: session_01PFB9ZGRWYvjhTXsd8i6KBp

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

✅ ACCEPT: PR #23988 at 98f87644e1 (#23948, Fixes #23948, p0 target:v18). Contract review of record: PASS (6220022536); it queues on a true-green head

domain:cli seat 1 (#6024) · marchtian · session session_01PFB9ZGRWYvjhTXsd8i6KBp · 2026-10-16T16:42Z · holder of claim 6217499012. Report: os-dev-report 6219947656.

席位核对

  1. Shape, read on GitHub: draft against main, head 98f87644e1. Line 1 Fixes #23948, the only closing keyword. Line 3 Clause-②: yes (widening — …), line-initial. Assignee marchtian. 9 files, all under the claim's surface: organization-ownership-inventory.ts, -plan.ts, -apply.ts, commands/migrate/organization-ownership.ts, the updated organization-ownership-apply.test.ts, two new test files, the remote-face file (11 mirrors added), and one changeset. Nothing under packages/plugins/plugin-sharing/** or packages/spec. check-governed-merges --pr 23988: NOT governed. git merge-tree against current origin/main is clean (it carries PR fix(cli): os migrate organization-ownership plans a v17 database still holding the retired sys_scim_provider, as fate 4 (ADR-0131 D10) #23874's inventory row). Five single-parent commits, with no merge.
  2. The change, read against the diff: advisoryDepartureReason(object, departureId) marks only sys_sharing_rule's declared-rule-mirrors departure as advisory, while DECLARED_SHARING_RULE_SEEDER_RUNS is true. The plan carries the reason on the departure, warns before the first write, and reads the table's notNull as willReceive: false with its reason. The post-check's departingRows leaves those rows out of remaining, and advisoryRows counts them in the run's and the ledger's advisory. The human summary names advisory rows and unattributable rows separately. Every other departure and remaining row still blocks. Nothing is deleted.
  3. Contract review: the Clause-②: yes declaration owes a TIER_DEFAULT review by the lane seat; posted as 6220022536 on this head, PASS.
  4. Pins and measurement: the premise was reproduced at the door on base 161aee1047 first (HotCRM's 10 + 1 shape: complete true, blocking 11, verified_at NULL).
    • The door file boots a served stack of this release over a SQLite file. Case 1 is the behavioural tie of the constant to the seeder. Case 2: plan, then --apply --json gives blocking 0, advisory = unattributable + 11, the marker verified, and 13 rule rows kept. Then a served boot with OrganizationOwnershipBootGuardPlugin composed starts, and the drop gate reads the marker as verified.
    • The unit file pins the 11-row verdict [true, 0, 11], the RESTORE with the seeder retired ([true, 11, 0]), and a same-id host departure that still blocks.
    • Remote face on a real sqld 0.24.32: blocking 0, marker verified (local; CI has no sqld).
    • RESUME and RESTART upgrade paths, measured through the CLI binary: both certify.
    • Ablation of departingRows through ablation-replace.mjs: the 11-row case is red with blocking back at 11, and the keyed case is red; restored to the HEAD blob.
  5. CI at 98f87644e1: 40 check-runs, 34 success, 6 skipped (each on the EXPECTED_SKIPS roster); the five required are success, the six Test Core shards included. mergeable_state clean; git merge-tree against current origin/main 2e1562091c is clean.
  6. The changeset (@objectstack/cli minor, line-initial Clause-②: yes (…)): what an operator saw (blocking: 11, never certified), what they see now, the new advisory field and counts, the notNull reading, what still blocks, the path for an environment already stopped at blocking: 11, and when blocking returns. Each matches the diff.
  7. Out of scope: out_of_scope_findings[0] (does C3 stage 9 (#15204): declared sharing rules read from the registry, bootstrapDeclaredSharingRules retires, and the ceremony deletes the declared-rule mirrors; the verified marker follows (director ordering ruling) #23952's "any other NULL row keeps blocking" mean to make the already-advisory unattributable rows block?) and [1] (the boot keeps seeding mirrors after certification; nothing re-blocks until C3 stage 9 (#15204): declared sharing rules read from the registry, bootstrapDeclaredSharingRules retires, and the ceremony deletes the declared-rule mirrors; the verified marker follows (director ordering ruling) #23952's deletion) are carried to C3 stage 9 (#15204): declared sharing rules read from the registry, bootstrapDeclaredSharingRules retires, and the ceremony deletes the declared-rule mirrors; the verified marker follows (director ordering ruling) #23952 by this seat at landing, together with the flip point of DECLARED_SHARING_RULE_SEEDER_RUNS. [2] (the door and remote-face files are over the 10 s line, no budget class fits): Acceptance notes; the hourly census lists them under test budget (domain:cli): 119 test file(s) over 10 s of wall in the hourly full battery (Part of #23151) #23181.

Landing: this seat flips ready and arms auto-merge through the relay.


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 size/l tests tooling

Projects

None yet

2 participants