You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1bede35
Browse filesBrowse the repository at this point in the historyBrowse files
fix(cli): the template delete stays inside fate 2 on every run; walled promotion with operator-chosen conflicts (#22937)
- Mirror deletion re-runs the idempotent template promotion before its first
template delete, then deletes only the seeded, uncustomized mirrors and, by
id, the rows whose environment copy that pass verified. A resumed run can no
longer delete a row customized or authored between runs.
- A customized or authored template row with no name blocks: kept, named.
- The promotion runs under every posture. Rows of one (name, locale) that
differ are a conflict: listed in the plan and the --apply report with each
candidate's row id and organization, counted as remaining, and blocking
until the operator chooses one row with the repeatable --template-choice
ROW_ID. An unknown or ambiguous choice is refused before anything is written.
- The settings move selects organization_id IS NULL and reads each destination
row back before deleting its source; the human face prints the moves.
- emailTemplateDefinitionFromRow falls back to EMAIL_TEMPLATE_FLOOR_LOCALE.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DjKf52bcyXa3fbGW723phR
Copy file name to clipboardExpand all lines: .changeset/22937-cli-ceremony-promotions.md
+5-6Lines changed: 5 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,16 +8,15 @@ Clause-②: yes (widening)
8
8
9
9
Two steps join the ceremony, after the authored catalog conversion and before the verified id→name rewrite. Each is checkpointed and resumable like the others.
10
10
11
-
-**`template-promotion`** (ruling C on ADR-0131 §6 Q1). Every `sys_email_template` row the mirror deletion keeps for it (customized, or with `customized` unknown, and every row no seeder wrote: `managed_by` NULL or other than package / platform / system, with or without an organization) becomes an environment `email_template` item through the metadata door, one per `(name, locale)`, before mirror deletion removes the rows. Seeded, uncustomized copies are not promoted. A promotion that already landed is skipped. The step stops, keeping every row, in three cases:
12
-
- two rows of one `(name, locale)` that differ;
11
+
-**`template-promotion`** (ruling C on ADR-0131 §6 Q1). Every `sys_email_template` row the mirror deletion keeps for it (customized, or with `customized` unknown, and every row no seeder wrote: `managed_by` NULL or other than package / platform / system, with or without an organization) becomes an environment `email_template` item through the metadata door, one per `(name, locale)`, before mirror deletion removes the rows. It runs under every posture. Seeded, uncustomized copies are not promoted. A promotion that already landed is skipped. The step stops, keeping every row of the template, in four cases:
12
+
- a conflict: rows of one `(name, locale)` that differ. Each conflict is listed in the plan and in the `--apply` report, with every candidate's row id and organization. It counts as remaining and holds back `verified_at` until the operator chooses one row with the new repeatable flag `--template-choice ROW_ID`. A choice that names no candidate, or two candidates of one conflict, is refused before anything is written. The chosen body is promoted, and the other candidates are deleted only after that promotion is verified;
13
+
- a customized or authored row with no name;
13
14
- an environment template that already exists and differs, since the environment's edit is never overwritten;
14
15
- a door refusal.
15
-
16
-
Under a walled posture the rows are kept and not promoted: promoting them would apply one organization's edit to every organization. They count as remaining and are named as a `template-promotion` departure, so the ceremony's `verified_at` stays null.
17
-
-**`settings-move`** (ADR-0131 D7). Every `sys_setting` row with `scope = 'global'` is copied into `sys_platform_setting` by the database itself (`INSERT … SELECT`), so `value` and the `value_enc` secret handle move verbatim. The row then leaves `sys_setting`. A key `sys_platform_setting` already holds with the same value is only removed from `sys_setting`. The step stops, moving nothing for that key, in two cases:
16
+
-**`settings-move`** (ADR-0131 D7). Every `sys_setting` row with `scope = 'global'` and no organization is copied into `sys_platform_setting` by the database itself (`INSERT … SELECT`), so `value` and the `value_enc` secret handle move verbatim. The destination row is read back, and only when it equals the source does the row leave `sys_setting`. A key `sys_platform_setting` already holds with the same value is only removed from `sys_setting`. The step stops, moving nothing for that key, in two cases:
18
17
-`sys_platform_setting` already holds the key with a different value (the one the runtime serves);
19
18
-`sys_setting` holds differing rows for the key.
20
19
21
20
The `global-rung-move` departure is now executed, so it reads zero after the run and no longer holds back `verified_at`. The `declared-rule-mirrors` departure still counts until the declared-rule seeder is retired.
22
21
23
-
Mirror deletion now also deletes the promoted template rows: under `single`, once the promotion step has finished, every remaining `sys_email_template`row is a mirror. The apply report gains `movedOut` / `movedIn` per table, and row counts reconcile as before − deleted − moved out + moved in.
22
+
Mirror deletion now also deletes the promoted template rows, and only those. Before its first template delete it runs the promotion pass again, so a row customized or authored between runs (on a resumed ceremony) is promoted, not deleted. It then deletes the seeded, uncustomized mirrors and, by id, the rows whose environment copy that pass verified. Any other promotion row stays and blocks. The apply report gains `movedOut` / `movedIn` per table, and row counts reconcile as before − deleted − moved out + moved in.
Copy file name to clipboardExpand all lines: .changeset/22937-core-email-template-row-definition.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,8 +6,8 @@ feat(core): `emailTemplateDefinitionFromRow` — a `sys_email_template` row as t
6
6
7
7
Clause-②: yes (widening)
8
8
9
-
A new pure export beside `permissionSetDefinitionFromRow` and `positionDefinitionFromRow`: one row in, one body out, with no engine, no I/O and no import. The upgrade ceremony (`os migrate organization-ownership --apply`) uses it to promote a customized or admin-authored template row to an environment `email_template` item through the metadata door before the row is deleted. It runs only in that ceremony, never at boot.
9
+
A new pure export beside `permissionSetDefinitionFromRow` and `positionDefinitionFromRow`: one row in, one body out, with no engine and no I/O. Its one import is the spec's floor-locale constant. The upgrade ceremony (`os migrate organization-ownership --apply`) uses it to promote a customized or admin-authored template row to an environment `email_template` item through the metadata door before the row is deleted. It runs only in that ceremony, never at boot.
10
10
11
-
The mapping: `body_html` → `bodyHtml`, `body_text` → `bodyText`, `from_address` / `from_name` → `fromOverride`, `reply_to` → `replyTo`, `variables_json` → `variables`, and `active` keeps its stored meaning. `label` falls back to the name and `locale` to `en-US`. The retired `category` / `is_system` columns and every provenance or state column are left out. It does not validate; the door does.
11
+
The mapping: `body_html` → `bodyHtml`, `body_text` → `bodyText`, `from_address` / `from_name` → `fromOverride`, `reply_to` → `replyTo`, `variables_json` → `variables`, and `active` keeps its stored meaning. `label` falls back to the name and `locale` to `EMAIL_TEMPLATE_FLOOR_LOCALE` (`en-US`). The retired `category` / `is_system` columns and every provenance or state column are left out. It does not validate; the door does.
awaitrefuse({reason: 'flag-without-apply',detail: '--template-choice is only meaningful with --apply'},'--template-choice is only meaningful with --apply.');
187
+
return;
188
+
}
177
189
if(flags['backup-taken']){
178
190
awaitrefuse({reason: 'flag-without-apply',detail: '--backup-taken is only meaningful with --apply'},'--backup-taken is only meaningful with --apply.');
0 commit comments