# Phase 3.1 Consolidated Decision Brief

> ## ✅ DECIDED 2026-08-25 — this brief is now a historical record
>
> **U-10 APPROVE · U-14 APPROVE · U-15 APPROVE · U-16 APPROVE · U-3 YES · U-5 REMOVE**
> Recorded as decisions **D3-4 … D3-9** in `DECISIONS_PHASE_3.md`, which is the operative record.
> Freeze preconditions **P-3, P-4, P-5, P-6 → MET.**
>
> 🛑 **P-7 was returned as the template's unfilled placeholders** — `[date] [time] IST,
> [freeze mechanism]`. No freeze instant has been inferred. **P-7 remains the single outstanding
> precondition**; see `D7_FREEZE_CHECKLIST.md` §2a.

Project: CE Online Training Courses · Drupal 7.105 → Drupal 10.6.15
Prepared 2026-08-25 · **Nothing implemented. No reconciliation target changed. No D7 access performed
for this brief** — every figure is drawn from measurements already recorded in
`DECISIONS_PHASE_3.md`, `CHECKPOINT_PHASE_3_1.md` and the `.logs/` evidence.

> **All six Phase 3.1 gates are met.** Tasks 01–11 are complete. What remains is not technical work:
> it is ten decisions, of which **four block the D7 freeze** and therefore Phase 3.5.

---

## Executive recommendation

Six of the ten items are **not really open** — the authoritative documents already answer them, or
the safe default requires no decision at all. Four are genuine decisions, and all four share one
shape: **an acceptance criterion was written before the data was fully measured, and now needs to be
expressed so it matches reality without loosening it.**

**None of the recommendations below deletes, repairs, or reactivates anything.**

| | Recommendation in one line |
|---|---|
| **U-10** | Keep R-5 = 33,003. Express it as `live grants + orphan-log entries = 33,003`. Nothing changes about which users hold access |
| **U-14** | **Keep R-23 = 443 unchanged** as the historical audit gate, and add a **new, separate R-24** for the full 1,213. Preserves audit meaning; makes the new 770 an explicit requirement |
| **U-15** | **VARCHAR destination.** It is the only option that cannot silently reject the 649 dead-uid rows. Enforce integrity in the migration process, not the column type |
| **U-16** | **Preserve update/delete semantics** and log them explicitly. The documents describe view grants but never authorise discarding the others — silence is not permission |
| **U-11** | Confirmed, no decision needed: promotion state follows `rules_config.active`. The nine mismatched entities migrate as **disabled** |
| **U-17** | Preserve + log. **Repair would require a business decision** and I recommend against attempting one |
| **U-18** | No decision — a migration requirement. `completed` targets both video and course; the distinction must survive |
| **U-3** | **Genuine decision**, small. Not required by any document |
| **U-5** | **Genuine decision**, small. Not required by any document |
| **P-7** | Yours alone. Schedule the freeze window |

---

## Decision table

| ID | Decision | Recommended option | Why | Blocks freeze? |
|---|---|---|---|:--:|
| **U-10** | How R-5's "exactly 33,003" is expressed | **Composite assertion**: 32,745 live + 258 logged = 33,003 | Preserves the integrity target exactly while telling the truth about what can exist in D10 | **YES (P-3)** |
| **U-14** | R-23 443 vs 1,213 measured | **Option A** — keep R-23 = 443, add **R-24 = 1,213** | The 443 is a verified historical audit result; overwriting it destroys that provenance | **YES (P-4)** |
| **U-15** | Wistia uid destination type | **Option A** — VARCHAR, matching source | Only option where 649 rows cannot be silently rejected | **YES (P-5)** |
| **U-16** | ACL update/delete grants | **Option A** — preserve semantics, log explicitly | Avoids silently reducing privileges; no document authorises dropping them | **YES (P-6)** |
| **U-11** | Discount promotion state | Follow `rules_config.active`; nine entities migrate **disabled** | Rule state is what makes a promotion fire | no |
| **U-17** | ~481 broken evaluation→course links | Preserve + log, **do not repair** | Repair requires guessing which course a submission belonged to | no |
| **U-18** | `completed` flag dual-target | Preserve both targets distinctly | Collapsing them corrupts 53,744 flaggings | no |
| **U-3** | `field_ui` | Enable in Phase 3.2 *(your call)* | Enabled in D7; affects admin parity W1. **No document requires it** | no |
| **U-5** | `/admin/dashboard` | Accept removal *(your call)* | No successor exists; only 2 blocks were ever active | no |
| **P-7** | Freeze window | Schedule after P-3…P-6 | Your operational action | **YES** |

---

## Detailed decisions

### U-10 / P-3 · ACL orphan grants

**Evidence** — measured 2026-08-25, `.logs/orphans.log`:

```
acl_user rows                       33,003   (R-5 target, "exact")
distinct uids in acl_user           11,395
grant rows whose uid has no user       258   across 83 users (1–10 grants each)
those grants pointing at dead nodes      0   ALL 258 target LIVE video nodes
maximum recreatable live D10 grants 32,745
```

`RECONCILIATION.md` R-5 reads: *"`acl_user` grants — **33,003 — exact**"*.

**The problem is not the number, it is what "grant" can mean in D10.** A Drupal 10 grant requires a
user entity to grant to. For 83 of these users there is none — in D7 either.

**Options**

| | Option | Assessment |
|---|---|---|
| **A** ✅ | **Composite assertion.** R-5 becomes `live grants (32,745) + orphan-log entries (258) = 33,003` | Integrity target unchanged. Nothing lost. Nothing pretended |
| B | Reduce R-5 to 32,745 | ❌ Silently abandons 258 records. The total stops reconciling against D7 |
| C | Create placeholder users for the 83 | ❌ **Invents users.** Forbidden, and would corrupt R-1 (13,249) and every user-count assertion |
| D | Leave R-5 as a bare 33,003 and expect live grants to match | ❌ Guarantees a permanent, unexplained failure at acceptance |

**Recommendation — Option A.**

**Consequences**

- **Migration behaviour** — the ACL migration attempts every one of the 33,003 rows. Where the user
  resolves, a real grant is written. Where it does not, the row goes to
  `ce_migrate_orphan_log` with `disposition = PRESERVED_UNLINKED`, `missing_parent_type = user`,
  `missing_parent_id = <uid>`, and the complete source row JSON-encoded. **Nothing is skipped
  silently and nothing is deleted.**
- **Reconciliation behaviour** — R-5 is satisfied only when both parts sum to 33,003. A shortfall in
  either half fails the gate.
- **Orphan-log behaviour** — the mechanism already exists, unused, in
  `web/modules/custom/ce_migrate/ce_migrate.install`. No new design needed.
- **Reporting impact** — **none for live users.** These 83 accounts do not exist in D7 either, so no
  report about a real user changes. R-14/R-15 ("no user gains or loses access") are unaffected by
  construction.
- **Does R-5 wording change?** **Yes — the wording, not the target.** Requires your approval.

**Exact wording proposed for approval** *(not applied)*:

> **R-5** — `acl_user` grants — **33,003 — exact**, satisfied as
> **live D10 grants (32,745) + `PRESERVED_UNLINKED` orphan-log entries (258) = 33,003**.
> The 258 belong to 83 users that no longer exist in D7. They are preserved and logged, never
> deleted, and no placeholder user is created. Figures are provisional until re-measured at the
> declared freeze point.

---

### U-14 / P-4 · R-23 orphan population

**Evidence** — `RECONCILIATION.md` R-23 asserts `163 + 38 + 12 + 225 + 4 + 1 = **443**`.
**All six categories verified EXACT** against live D7:

| Documented category | Target | Live |
|---|---:|---:|
| Orders with a dead uid | 163 | **163** ✅ |
| Payment transactions with no order | 38 | **38** ✅ |
| Line items with no order | 12 | **12** ✅ |
| `wistia_media` with a dead uid | 225 | **225** ✅ |
| Quiz results with a dead uid | 4 | **4** ✅ |
| Flaggings pointing at a missing node | 1 | **1** ✅ |
| **R-23 total** | **443** | **443** ✅ |

**Newly discovered — 770 rows, in no document:**

| New category | Rows | Found by |
|---|---:|---|
| `wistia_media_track` with a dead uid | **424** | orphan register |
| **`acl_user` grants with a dead uid** | **258** | orphan register — **entitlement data** |
| Webform submissions with a dead uid | **73** | orphan register |
| `product → field_video_reference` pointing at a missing video | **7** | L-9 relationship pass |
| `ce_referral_discount` with a dead uid | **6** | L-8 relationship pass |
| `course → field_videos` pointing at a missing video | **1** | L-9 relationship pass |
| `wistia_media_track` with no `wistia_media` row | **1** | orphan register |
| **New total** | **770** | |

**Grand total: 443 + 770 = 1,213.** The undocumented population is larger than the documented one.

> This does **not** undermine the audit. Every one of its six categories verified exactly — strong
> evidence it was done carefully. It simply never extended the missing-parent test to `acl_user`,
> `wistia_media_track`, `webform_submissions`, the field tables or the referral ledger.

**Options**

| | Option | Assessment |
|---|---|---|
| **A** ✅ | **Keep R-23 = 443 unchanged. Add a new R-24 = 1,213** for total referential-integrity orphans | Preserves the audit's verified result and its provenance. Makes the new 770 an explicit, separately-testable gate. Two assertions, two meanings |
| B | Redefine R-23 as 1,213 | ❌ Destroys the historical meaning of a **verified** figure. A future reader could no longer tell that the original six categories reconciled exactly, nor when the other seven were found |

**Recommendation — Option A.** R-23 answers *"did the audited orphan population survive?"*
R-24 answers *"did **every** referential-integrity orphan survive?"* Both matter; conflating them
loses information.

**Consequences** — all 1,213 are logged as `PRESERVED_UNLINKED` or `PRESERVED`. None is deleted.
Two gates must pass instead of one. `RECONCILIATION.md` gains R-24; **R-23 is not touched**.

**Exact wording proposed for approval** *(not applied)*:

> **R-24** — Total referential-integrity orphans preserved and logged — **1,213**, comprising the
> **443** of R-23 plus **770** discovered during Phase 3 relationship reconciliation
> (`wistia_media_track` dead uid 424 · `acl_user` dead uid 258 · webform submissions dead uid 73 ·
> product→video 7 · referral ledger 6 · course→video 1 · track without media 1).
> Every row present in the `ce_migrate` orphan log. **None dropped.** Provisional until the freeze point.

⚠️ **Housekeeping note, not a decision:** `scripts/reconcile/d7_orphans.sh` §O-3 still summarises the
new population as **756**. It predates the L-8/L-9 findings that added 14. It should be updated to
**770** — a comment change only, no query affected. Not done, pending your instruction.

---

### U-15 / P-5 · Wistia UID destination type

**Evidence**

```
wistia_media       (id_wistia_media, uid VARCHAR, video_hash_id, completed)   32,737 rows
wistia_media_track (id_wistia_media_track, video_hash_id, uid VARCHAR, time)  62,137 rows

dead uids: wistia_media 225 + wistia_media_track 424 = 649 rows
non-numeric uid values: 0 in BOTH tables
```

`DATABASE_ANALYSIS.md` §72 already warned: *"`uid` is a VARCHAR, not an FK. All values are numeric,
but **225 rows reference uids that no longer exist**"*, and §172: *"an FK-typed destination will
reject or silently drop them"*. The audit anticipated this; it simply had not measured
`wistia_media_track`.

**Options**

| | A — VARCHAR destination | B — integer/FK destination | C — integer + separate orphan table |
|---|---|---|---|
| **Preservation** | ✅ all 94,874 rows land | ❌ 649 rejected or nulled | ⚠️ 94,225 land, 649 diverted |
| **Referential integrity** | ⚠️ not enforced by the column — same as D7 | ✅ enforced | ✅ enforced for the main table |
| **Migration complexity** | ✅ lowest — straight copy | ⚠️ needs exception handling | ❌ highest — two destinations, two reconciliations |
| **Risk of silent loss** | ✅ **none** | ❌ **high** — FK rejection can be silent | ⚠️ low, but split data invites drift |
| **Reconciliation** | ✅ single count matches source | ❌ 649 must be explained every run | ⚠️ two counts to reconcile |
| **Matches D7 behaviour** | ✅ identical | ❌ stricter than the source | ⚠️ different shape |

The project documents no option C design. It is included only for completeness.

**Recommendation — Option A, VARCHAR.**

The governing principle is functional equivalence: D7 does not enforce this integrity, so D10
enforcing it *is a behaviour change*. Integrity belongs in the **migration process and the
reconciliation gates**, where a violation is visible and logged — not in a column type, where it is
enforced by silent rejection. Adding an FK would also make the 649 rows unrepresentable, which
directly conflicts with decision 7.

**Consequences** — all 94,874 rows migrate. The 649 dead-uid rows are additionally recorded in the
orphan log so they appear in R-24. Reconciliation compares single counts against source. No data is
unrepresentable. Since all values are numeric today, a future tightening remains possible.

**No wording change required** — this is a schema decision, not an acceptance criterion.

---

### U-16 / P-6 · ACL update/delete grants

**Evidence** — measured 2026-08-25, `.logs/relationships.log`:

```
acl_node grant flags in use:
  grant_view   = 1  ->  134 ACL-to-node bindings
  grant_update = 1  ->   53
  grant_delete = 1  ->   52
                        --- 105 carry update or delete
```

**What the documents say** — I searched every project document:

- `RULES_MIGRATION_ANALYSIS.md` §369: *"ACL **view** grant on the **video** nid to **order owner**"*
- `RULES_BEHAVIOR_MAPPING.md` RB-14: *"assert ACL **view** grant on the video nid"*
- **`grant_update` and `grant_delete` appear in ZERO documents.**

The documents consistently describe the *entitlement* mechanism as a view grant. They **never state
that update/delete grants do not exist, and never authorise discarding them.** This is silence, not
permission — the audit measured `acl_user` totals and grant targets, not the grant-flag columns.

⚠️ **One thing is NOT yet measured:** how many *users* hold update/delete through those 105 bindings.
I measured `acl_node` flags, not the user population behind them. That should be quantified at the
freeze point before implementation.

**Options**

| | Option | Assessment |
|---|---|---|
| **A** ✅ | **Preserve update/delete semantics**, reproduce in D10, log explicitly | No user silently loses a privilege. Honours R-14/R-15 in spirit as well as letter |
| B | Migrate view only | ❌ **Silently reduces privileges** on 105 bindings. Directly contradicts *"no user loses access they had in D7"* |
| C | Preserve as logged unsupported/extended entitlement | ⚠️ Acceptable fallback **if and only if** D10 `content_access`/`acl` cannot express them. Must be proven, not assumed |

**Recommendation — Option A, with C as a documented fallback.**

Both `acl` ^2.0 and `content_access` ^2.1 are installed and were chosen precisely because the
mechanism survives like-for-like (`D10_ARCHITECTURE.md` §3). The natural expectation is that
update/delete grants map directly. **That expectation must be verified, not assumed** — and if it
fails, option C keeps the data visible rather than dropping it.

**Consequences** — the ACL migration must carry all three grant flags, not just `grant_view`. RB-14
and its test skeleton need extending to assert update/delete where present. R-14/R-15 gain a
per-flag dimension: no user loses *any* grant type.

**No R-target wording change required**, but `RULES_BEHAVIOR_MAPPING.md` C-10 and RB-14 describe
view grants only and would need a documentation amendment to match — **your call whether that is in
scope now**.

---

### U-11 · Discount promotion state

**Evidence** — 9 `commerce_discount` entities have `status = 1` while their `rules_config` rows have
`active = 0`.

**Is the rule documented?** Not as an explicit statement — but it is **inherent to how Rules works**
and consistent with every document. `RULES_BEHAVIOR_MAPPING.md` §1 describes promotions firing by
rule weight and rule state; a rule with `active = 0` does not fire, regardless of the entity's
status flag. `RECONCILIATION.md` R-17/R-18 count **active promo rules** (`rules_config.active`),
never the entity status — which is exactly why my first R-17 measurement was wrong.

**Confirmed required behaviour:** promotion firing state derives from **`rules_config.active`**.

**Required migration behaviour for the nine** — they become Commerce 3 promotions in the
**disabled** state, preserved but not firing, exactly as in D7. Migrating from
`commerce_discount.status` would **silently re-enable nine historical promotions** and change what
customers pay.

**No decision required.** Recorded so the implementation cannot drift.

---

### U-17 · Broken evaluation → course links

**Evidence** — measured 2026-08-25, `.logs/relationships.log`:

```
submissions on webform node 27      21,064
data rows carrying cid 30           20,873   ->  191 submissions have NO course value
  of those 20,873:
    resolve to a course node        20,580
    resolve to a NON-course node         3
    do not resolve to any node         286
    non-numeric values                   4
```

**~481 submissions cannot be linked to a course**, and 3 point at the wrong node type.

**Authoritative documentation** — `DATABASE_ANALYSIS.md` §263.3 and `PHASE_2_MIGRATION_DECISIONS.md`
§10 both flag this as *"the single highest-risk detail in the whole migration"*:
`course_actions.module:243` reads `$submission->data[30]['value'][0]` as the course nid.
`webform_migrate` converts numeric `cid` values to machine names, and **the mandated mitigation —
capture `webform_component` (28 rows) verbatim before migrating and build an explicit
cid→machine-name map — remains mandatory and unchanged.**

**What is broken:** the *link*, not the submission. The submission data is intact; the course
reference is missing, unresolvable, or malformed.

**Preserve, repair, or log?** **Preserve + log.** Every submission migrates; the broken reference is
recorded in the orphan log with its class.

**Does repair require a business decision?** **Yes — and I recommend against attempting one.**
Repair means inferring which course a submission belonged to from timestamps, user history or
enrolment. That is **reconstructing business records from circumstantial evidence**, it changes
historical data, and it would violate the standing rule against normalising historical data. If the
business ever wants those 481 reviewed, that is a separate, deliberate exercise with human
judgement — not a migration step.

⚠️ **4 non-numeric `cid=30` values would break a CAST-based nid lookup**, the same failure mode as
the Wistia uid columns (U-15). The migration must tolerate them rather than assume numeric.

---

### U-18 · Dual-target `completed` flag

**Evidence** — measured 2026-08-25:

```
flag          entity type   flaggings   users     entities
completed     video            32,050   \
completed     course           21,693   /  53,744 total
bought        video            32,992    11,469      133
enrolled      course           24,783    11,752      114
submitted     course           20,397    10,630       99
in_progress   video               340       250       92
```

**Authoritative definition** — `RECONCILIATION.md` R-6 gives per-flag totals
(`completed 53,744`) and **matches exactly**. But no document breaks `completed` down by entity
type. The dual-target nature is a **Phase 3 measurement**, not previously documented.

**The two targets**

- **`completed` on a video** (32,050) — the learner finished watching that video. Set by
  `VideoPlaybackSubscriber::onVideoEnded()` (RB-12).
- **`completed` on a course** (21,693) — the learner finished the whole course. Set by
  `CourseCompletionSubscriber` (RB-11) once every video in the course is complete.

They are different business facts sharing one flag name. `completed` is the **only** dual-target
flag; the other four are single-target.

**How migration must preserve the distinction** — the flag migration must carry `entity_type` and
`entity_id` per flagging row and must not assume one entity type per flag. A migration that maps
`completed` to a single target would corrupt whichever half it did not model — either 32,050 video
completions or 21,693 course completions.

**No decision required.** Recorded as a migration requirement, and it should become an explicit
assertion in the flag migration test.

---

### U-3 · `field_ui`

**Evidence** — `field_ui` is **enabled in D7**. I searched every project document: **no document
mentions it.** `PHASE_3_IMPLEMENTATION_PLAN.md` §3 is explicitly the *Phase 3.1 baseline* module
list, not the final set, and does not include it.

**Is it already approved?** **No.** Nothing requires it, and I will not infer a requirement merely
because the module is useful.

**What is affected** — without `field_ui` there is no UI at `/admin/structure/types/manage/*/fields`.
Configuration still imports and exports normally; only human field management is unavailable. This
touches admin-parity check **W1** (course node editing) only insofar as editors need to *manage
fields*, not merely edit content.

**This is a genuine decision.** If approved, the exact implementation is:

```bash
ddev drush -y pm:install field_ui
ddev drush -y config:export --destination=/var/www/html/config/sync
```

**Small, reversible, and not a gate.** Recommendation withheld — it is a workflow preference.

---

### U-5 · `/admin/dashboard`

**Evidence** — measured from the D7 `block` table:

| Module / delta | Region | Status |
|---|---|---|
| `user` / `new` ("Who's new") | `dashboard_sidebar` | **active** |
| `search` / `form` | `dashboard_sidebar` | **active** |
| `node` / `recent` | `dashboard_inactive` | never rendered |
| `comment` / `recent` | `dashboard_inactive` | never rendered |
| `user` / `online` | `dashboard_inactive` | never rendered |

**What the documents require** — nothing. **No project document mentions the D7 core `dashboard`
module.** It was removed from Drupal core in D8 and has no successor.

**Separating the three concerns, as requested:**

| Concern | Assessment |
|---|---|
| **Existing core/admin capability** | Both active blocks have D10 core equivalents. Drupal 10 provides `/admin/content`, `/admin/people` and a search block. The *information* remains available |
| **Custom functionality actually required** | **None.** No custom code, view or report depended on `/admin/dashboard`. The five blocks are all stock core blocks |
| **Anything requiring new custom code** | **None**, unless you want the landing page itself recreated — which would be **new construction**, not migration |

**This is a genuine decision**, and a small one: accept removal, or place the two active blocks on an
admin page. Recommendation withheld — recreating a page that no document requires would be new
functionality, which the migration otherwise forbids.

---

### P-7 · D7 freeze

**Prerequisites already satisfied**

| | | |
|---|---|---|
| **P-1** | Task 11 backups exist and verified | ✅ `d7-20260825-123510`, 8/8 checks |
| **P-2** | Backups contain *current* data | ✅ `commerce_order` 87,640 + four corroborating counts |
| **P-8** | D7 connection provably SELECT-only | ✅ proven Task 06 — 12 CLI + 4 Drupal write probes all denied |
| **P-9** | Reconciliation suite runs clean | ✅ 6 scripts covering R-1…R-23 and L-1…L-10 |

**Remaining prerequisites**

| | | |
|---|---|---|
| **P-3** | U-10 decided | ❌ this brief |
| **P-4** | U-14 decided | ❌ this brief |
| **P-5** | U-15 decided | ❌ this brief |
| **P-6** | U-16 decided | ❌ this brief |
| **P-7** | **Freeze window scheduled** | ❌ **yours alone** |

**What you must explicitly schedule and approve**

1. A **freeze date and time**, with timezone.
2. The **mechanism** — maintenance mode, blocked admin logins, or an operational instruction to staff.
3. **Notification** of whoever administers D7. Note the evidence: business activity ceased
   2026-08-11, then a single order appeared on **2026-08-24 13:31:48**, two weeks later. Someone or
   something can still write.
4. Confirmation that the freeze **holds until the D10 acceptance gate passes** — releasing early
   invalidates the baseline.

**What I must NOT do — and will not**

- ❌ freeze, stop, disable, pause or modify D7 in any way
- ❌ enable maintenance mode or change any D7 setting
- ❌ modify D7 code, configuration, schema or data
- ❌ declare the freeze on your behalf

**What I will do, after you declare it** — run the four read-only reconciliation scripts at the
freeze instant to capture the acceptance baseline, then verify the freeze actually held by checking
that every newest-write timestamp precedes the declared instant. Procedure: `D7_FREEZE_CHECKLIST.md`
§4.

---

## Freeze readiness

| # | Prerequisite | Status |
|---|---|---|
| **P-1** | Task 11 backups exist and verified | ✅ **COMPLETE** |
| **P-2** | Backups contain current data | ✅ **COMPLETE** |
| **P-3** | U-10 decided — R-5 expression | ⏳ **PENDING YOUR APPROVAL** |
| **P-4** | U-14 decided — R-23 / R-24 | ⏳ **PENDING YOUR APPROVAL** |
| **P-5** | U-15 decided — Wistia column type | ⏳ **PENDING YOUR APPROVAL** |
| **P-6** | U-16 decided — update/delete grants | ⏳ **PENDING YOUR APPROVAL** |
| **P-7** | Freeze window scheduled | ⏳ **PENDING YOUR APPROVAL** |
| **P-8** | D7 connection SELECT-only | ✅ **COMPLETE** |
| **P-9** | Reconciliation suite clean | ✅ **COMPLETE** |

**4 complete · 5 pending your approval · 0 not applicable.**

Not freeze prerequisites, but open: **U-3**, **U-5** (both small, neither blocks anything),
and **U-11 / U-17 / U-18** (recorded requirements, no decision needed).

---

## Required approvals

Only these need your decision:

1. **U-10** — approve the composite R-5 wording: `32,745 live + 258 logged = 33,003`
2. **U-14** — approve **Option A**: keep R-23 = 443, add R-24 = 1,213
3. **U-15** — approve **VARCHAR** for `wistia_media.uid` and `wistia_media_track.uid`
4. **U-16** — approve **preserving update/delete ACL grants** (105 bindings), with the logged-fallback
   if D10 cannot express them
5. **U-3** — enable `field_ui`? *(yes / no — not a gate)*
6. **U-5** — `/admin/dashboard`: accept removal, or recreate the two active blocks? *(not a gate)*
7. **P-7** — declare the D7 freeze date, time and mechanism

Items **1–4 and 7** unblock the freeze, and the freeze unblocks Phase 3.5.
Items **5–6** can be answered at any time.

**U-11, U-17 and U-18 need no decision** — they are recorded migration requirements.

---

**Nothing in this brief has been implemented. `RECONCILIATION.md` is unmodified. D7 is untouched —
`course_actions.module` md5 `f9913edaad4e3feb434ee201ebb2f55c`, 11 git entries, 0 non-documentation
changes. The D10 frontend theme remains `stark` and design-locked.**
