Repository navigation
feat(cli,core): the organization-ownership ceremony promotes customized email templates and moves the settings global rung (C7b, PR 3) - #23195
Conversation
…10 ceremony's apply step and post-check The read-only plan gains its writing mode. --apply asks about the backup first (--backup-taken, or a yes at the prompt; refused non-interactively), then performs the fates in order, stopping at the first blocker: 1. attribution — the owners the plan's own walk derives (anchors, else the Default Organization under single), to a fixed point; an unattributable row is never assigned and stays listed by id; 2. authored catalog rows — permission sets and positions become environment metadata through the metadata door via core's row-to-definition mappers, with position bindings carried into permissionSets (single only); 3. the verified id-to-name rewrite of sys_user_permission_set.permission_set, then every grant verified to name a set the security catalog resolves (permission_set_id stays); 4. mirror deletion, gated on that verification read again before the first delete; only mirror rows no named category claims; 5. column drops through the schema-drift ceremony's drop_column entries. Every write is conditioned on the state it changes, and each finished step is checkpointed in sys_migration (adr-0131-organization-ownership), so an interrupted run resumes at its step. The post-check re-reads the plan; the ledger row's applied_at / verified_at are the ceremony's marker. The acceptance fixture carries every fate: effective access identical, with a positive control; mirrors gone; attributed rows stamped; the orphan row present and reported; row counts reconciling. Claude-Session: https://claude.ai/code/session_01DjKf52bcyXa3fbGW723phR Co-authored-by: Claude <noreply@anthropic.com>
…remony-promotions
… as the email_template definition it carries The pure row-to-definition mapper beside permissionSetDefinitionFromRow and positionDefinitionFromRow, for the v18 ceremony's promotion of customized template rows (ADR-0131 D10, ruling C on section 6 Q1). No import, so the lean entry carries it. Pinned against EmailTemplateDefinitionSchema. Claude-Session: https://claude.ai/code/session_01DjKf52bcyXa3fbGW723phR Co-Authored-By: Claude <noreply@anthropic.com>
… the settings global rung Two steps of `os migrate organization-ownership --apply`, per the maintainer's ruling on #22937 (Q3, Q4 → A), checkpointed and resumable like the others: - template-promotion: customized and admin-authored sys_email_template rows become environment email_template items through the metadata door (core's emailTemplateDefinitionFromRow) before mirror deletion; never over an environment template that differs; reported under a wall. - settings-move: the inventory's global-rung-move departure, executed: sys_setting scope = global rows copied into sys_platform_setting by INSERT ... SELECT, then removed; a key already served with a different value is a blocker. The sys_sharing_rule declared-rule mirrors are measured and not removed: the declared-rule seeder is still live and rule grants key on the row id. Claude-Session: https://claude.ai/code/session_01DjKf52bcyXa3fbGW723phR Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 27 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 15 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 46 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 57ff8bc838e76ef424fe364396543a55b2ef66df && git checkout 57ff8bc838e76ef424fe364396543a55b2ef66df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4 49d85fc6538769d536c7e0d4699219ad3f31fa27 && git checkout -B drift-repro d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4 && git merge --no-ff 49d85fc6538769d536c7e0d4699219ad3f31fa27
node scripts/docs-audit/affected-docs.mjs --json d4aaeaf0e2eb39ac574f1ba171a88535f9a678b4
|
|
Closed unmerged by |
Part of #22937
Clause-②: yes (widening)
Stacked on PR #23034: until it lands, this diff also carries its hunks; this PR lands only after PR #23034.
PR 3 of the C7b build-ahead card. It applies the maintainer's ruling 6138591940 on Q3 and Q4 (both A) under dispatch 6139305919. PR #23034 carries the card's closing line; this one closes no card. It stays a draft until #15204 stage 8 is on
mainand PR #23034 has landed. A contract-tier review is owed before the queue.What it adds (671 changed lines against PR #23034's head, merge of
mainexcluded)emailTemplateDefinitionFromRowinpackages/core/src/security/, besidepermissionSetDefinitionFromRow/positionDefinitionFromRow.sys_email_templaterow in, theemail_templatebody out.EmailTemplateDefinitionSchema: no undeclared key, and a fullsafeParsepasses.template-promotion, a ceremony step (Q3, ruling C on ADR-0131 §6 Q1).customized: true) or authored (managed_bynot a seeder). Each becomes an environmentemail_templateitem through the real metadata door (protocol.saveMetaItem), one per(name, locale)bundle member, before mirror deletion.(name, locale)that differ; on an existing environment template that differs (the environment's edit is never overwritten); or on a door refusal.settings-move, a ceremony step (Q4, 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 ruling 6043540291). It executes the inventory'sglobal-rung-movedeparture.sys_settingrow withscope = 'global'is copied intosys_platform_settingbyINSERT … SELECT, sovalueand thevalue_encsecret handle move verbatim. The row then leavessys_setting.sys_platform_settingalready holds with an equal value is only removed fromsys_setting.secret-reference-union.ts) already reads both holder tables.singleonce the promotion has run. Every remaining template row is then a mirror. Under a wall the promotion population is excluded from deletion as well.movedOut/movedInper table, and counts reconcile as before − deleted − moved out + moved in.Step order: attribution → authored-conversion → template-promotion → settings-move → rewrite → mirror-deletion → column-drops.
Q4's sharing-rule mirrors: measured, not clean, not built
The
sys_sharing_ruledeclared-rule mirrors stay as the departure the plan reports. The measurement, onmainate81cae0149:plugin-sharing/src/sharing-plugin.ts:768callsbootstrapDeclaredSharingRuleson every boot;sys_record_sharerows withsource = 'rule'andsource_id = rule.id(sharing-rule-service.ts:231, writes at:1081/:1102);sweepOrphanedRuleGrants(:845-880) revokes every rule grant whosesource_idnames no rule row.So deleting a declared-rule row today revokes its grants at the next boot. The seeder then re-seeds the rule under a new id and reconcile re-grants under that id. The row is the runtime's source of truth, not a mirror of code, until D2 retires the seeder. The removal belongs after that retirement; the report carries it as an open question.
Tests and evidence
email-template-row-definition.test.ts(5) and the wholesrc/security/directory: 39 files, 877 tests passed.organization-ownership-apply.test.ts, now 10 cases. The acceptance fixture gains four template rows (a seeded mirror, an organization's seeded copy, a customized seeded row and an admin-authored row) and the settings rung. Two new cases: the settings-conflict blocker, and the "never overwrite an environment template" blocker. The resume and gate cases were updated for the new step order.organization-ownership.apply-flags.test.ts;vitest-tiers-partition.-t "organization-ownership|every module", 6 passed.@objectstack/coreand@objectstack/cli(tsc plus test-typecheck), both exit 0.scripts/ablation-replace.mjs, each restored to the HEAD blob withgit diff HEADempty:email_template/crm.renewalinsys_metadata;sys_platform_setting, and the template rows were deleted after the promotion;alreadyPromoted: 1).dispatch-gates --commandsderived 68 at this head, and--ranreports "68 derived, 68 run, 0 NOT-MEASURED (a DERIVED zero)".check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 on unbuilt sibling dists in this fresh worktree. They were built under the verify lock and re-run green.check:type-check-debthit this runner's 400 s per-gate timeout. Re-run alone, it exits 0.Also in this diff
organization-ownership.apply-flags.test.ts(from PR #23034) now pays the command's first load inbeforeAlland gives each case 60 s. On the merged tree, its first case crossed vitest's 5 s default (5,011 ms), which also broke the second case's stdout capture. PR #23034 carries the same file at 4.4 s. If CI ever reddens it there, this is the fix.Not touched
packages/plugins/plugin-security/**, the files of D / S8-T / S8,packages/spec, andorganization-ownership-inventory.ts. The departure is executed from its own data.Generated by Claude Code