# Delivery 5 Batch D: AssignmentCard, RegionPresets, and Table

## Caller audit

`AssignmentCard` has one production caller: `admin/src/pages/my-assignments.js`, where `ListRegionPreset` renders each assignment into the assignment grid. No production caller holds an `AssignmentCard` instance or relies on replacement root identity; the page holds the `DataRegion` and its panel only. Tests reference the page loading seam, and this batch adds the primitive lifecycle suite.

`updateAssignment` has no external production or test caller. It remains public and delegates to `update({ assignment })` for compatibility.

`RegionPresets` production callers are `dashboard.js` (stat and table), `my-assignments.js` (list), `analytics-dashboard.js`, `content-translate.js`, `string-translate.js`, `languages.js`, and `translate-history.js` (table). Tests call every exported factory. `TableRegionPreset` is the only existing custom removal hook (`removeRow`); it runs once while `DataRegion` still retains an attached, usable row, then recursive owner cleanup runs in a `finally` block before detachment. Form and chart use the same optional-hook ordering without changing caller-required arguments.

The inspected seams are `Button`, `Badge`, `PriorityBadge`, `ProgressBar`, `DeadlineIndicator`, `Table.createRegionView`, `SelectRegionView`, `DataRegion` reconciliation/removal, `StatCard`, `ErrorState`, `EmptyState`, and `dom.js`. `DeadlineIndicator` exposes root `destroy()` from Batch C. `DataRegion` retains keyed nodes on update and invokes `removeItem` before detach. Table owns keyed row reconciliation; constant-key form/stat/list/chart remain wholesale shapes and do not introduce another reconciler.

## Design and compatibility

`AssignmentCard` keeps one public root from construction through update, destroy, and remount. A supplied constructor container mounts the completed initial generation through the same mount seam as `update({ container })`, without another render or reordering an already mounted root. A render generation owns its deadline, buttons, badges, priority badge, and progress bar; replacing or destroying a generation destroys every owner exactly once. If new-generation construction throws, the committed generation is restored before exhaustive partial cleanup and the original construction error wins. If old-generation retirement throws, every old owner is still attempted, then the complete new generation and markup are committed and retained before the first old teardown error is propagated. `update()` preserves the committed assignment and options in that post-commit error state, while retaining rollback for pre-commit construction failures. A failed destroy still cleans all owners and detaches the stable root before propagating its first teardown error; repeated destroy remains silent. `mount()` is the only resurrection operation. Legacy mutation/action methods are silent while destroyed. Update uses independent own-property branches, normalizes explicit `undefined` to constructor defaults, and renders once for a combined update.

`RegionPresets` treats an element with `destroy()` as a lifecycle ownership boundary: it invokes that owner once and never descends into its children. Its custom removal hooks still run before cleanup, and a callback exception remains the propagated exception after cleanup. Tree cleanup attempts every sibling boundary before propagating its first teardown error; content replacement still completes before that error is thrown. `Table` applies the same boundary-aware teardown to every child it removes or replaces while morphing retained keyed rows. `Table.createRegionView()` requires every renderer result to be an ordinary `<tr>` root without `destroy()`; lifecycle ownership is supported only below that root. Every non-null rejected detached renderer result is exhaustively destroyed to ownership boundaries before the contract error is thrown, including when teardown throws; the contract error remains primary. If an already retained row gains `destroy()`, update rejects it without destroying or replacing that installed row, while exhaustively discarding the ordinary detached next row. Ordinary same-tag nodes keep their existing identity and runtime state. Existing markup, exports, constant keys, and CSS classes are unchanged.

## Focused test inventory

- `assignment-card-lifecycle.test.js`: constructor-container mount parity with the update-container seam and one initial render generation; independent onAction callback replacement, explicit-undefined assignment/compact constructor-default normalization, separate explicit-undefined onAction normalization, combined updates, explicit undefined container normalization without a render or reorder, stable root, idempotent mount/destroy/remount with rebuilt markup, destroyed-update inertness until remount, current callback routing, partial-render rollback that preserves its original error after exhaustive partial teardown, failed retirement that commits the new self-detaching-control generation and current callback before propagating its original error, exhaustive failed-destroy cleanup with root detachment and silent repeated destroy, and deadline interval generation cleanup.
- `region-presets.test.js`: table callback-before-cleanup ordering, callback-error propagation, exhaustive throw-safe recursive sibling cleanup, and one-time invocation; a real ListRegionPreset-to-AssignmentCard ownership-boundary discriminator with non-idempotent nested deadline owners; real stat-card subtree cleanup on replacement/removal; form/chart removal hook ordering and retained-update non-cleanup. Existing list tests cover wholesale replacement/removal and nested deadline owners; existing chart integration covers same-node update.
- `table-region.test.js`: retained-key lifecycle morph cleanup for tail removal, node-type/tag replacement, exhaustive failed old-tree cleanup with deterministic replacement and unused-new-owner cleanup, nested-owner boundary teardown exactly once, ordinary same-tag-to-new-lifecycle-owner replacement with later exact teardown, wrong-tag detached-tree rejection with throwing and nonthrowing owners, installed-old-root rejection with ordinary detached-next cleanup, installed-row preservation, and plain same-tag control identity/runtime-state preservation.

## Independent review and repair

An initial independent `gpt-5.6-sol` review found three medium-severity lifecycle gaps. The constructor did not honor its configured container through the same mount seam as `update({ container })`; `RegionPresets` descended below an `AssignmentCard` owner seam and could destroy nested controls twice; and retained table-row morphing removed or replaced lifecycle descendants without cleanup. The repairs added constructor parity, ownership boundaries, self-detaching teardown tolerance, and stable replacement positioning.

A final independent `gpt-5.6-sol` review found three further medium-severity exception-path gaps: `AssignmentCard` retirement stopped after the first throwing owner, `RegionPresets` skipped sibling boundaries after a teardown exception, and `Table` could abandon a partially processed morph generation. The final repair attempts every owned sibling exactly once, reaches deterministic replacement/removal state, releases unused detached generations, and propagates the original teardown error only after cleanup. Main-thread verification then closed two adjacent `AssignmentCard` exception paths so failed construction restores committed ownership before partial cleanup and failed destruction still detaches the root. Focused integration tests and mutation discriminators cover all reviewed and adjacent repaired paths.

The definitive-review repair changes `AssignmentCard` retirement from rollback to committed recovery: failed old-owner teardown no longer releases the complete new generation or restores stale state. It also closes the table/DataRegion seam by rejecting lifecycle-owned `<tr>` roots, which DataRegion cannot safely replace because it retains the keyed row reference. The rejected detached owner is cleaned before a stable contract error, while a suspicious installed row is never destroyed by rejection.

The post-commit independent review found one remaining medium-severity integration defect: a throwing preset removal hook allowed `DataRegion` to retain an already-cleaned keyed node because detach/delete occurred only after `removeItem()` returned. The repair now exhaustively attempts every stale entry, detaches and forgets it regardless of hook failure, preserves the first error, and applies the same deterministic finalization to region destruction. A failed destroy clears all keyed ownership, listeners, schedules, pending state, DOM references, and the mounted root before propagating its first teardown error; repeated destroy remains silent.

## Orchestrator measurements

- Focused remote lifecycle run: 3 files, 96 tests passed (`assignment-card-lifecycle.test.js`: 17; `region-presets.test.js`: 40; `table-region.test.js`: 39).
- Mutation evidence: 70 meaningful source mutants killed, 0 survived, 0 anchor errors; all three restored focused baselines passed with return code 0.
- Full remote admin suite: 74 files, 948 tests passed with `--maxWorkers=2`.
- Remote ESLint passed for all four changed source files and all three focused test files.
- Two clean remote production builds were byte-identical across all 39 output files. Final regeneration rotated chunks `124.a76e2650.js` → `124.72b7af96.js`, `321.ac5cd245.js` → `321.88669bfb.js`, `714.55d0a4c9.js` → `714.ecfc2e0b.js`, and `965.b35b260b.js` → `965.c5934a90.js`; `dist/js/main.js` was unchanged at MD5 `80ddd2f04fc1445f10a7fc84392a1997` and SHA-256 `dda91662c959b98c8cab9f3f4a5f6cb8cf8365d7655c55cfd24bd80253a793d2`.
- UI inventory: 124 modules, 54 components, 17 pages, 0 unclassified modules, 0 direction violations, 1,378 legacy-class entries, 2 search controls, and 2 duplicate IDs. Remote inventory checks passed before and after the current `origin/master` merge; the UI gate passed with 4 changed files scoped before the merge and 25 changed files scoped against the merge's master parent afterward, with no new violations in either run.
- Prepared remote factory `all` completed successfully after locked Composer/npm installation both before and after the current `origin/master` merge: PHP syntax/tests/baseline, PHPStan, full admin lint/build, 45 UI architecture tests, inventory check, and UI gate all passed. The PHP baseline reported no changed PHP files.
- Coherent `admin/dist` assets and the UI inventory report were regenerated from the final source through the normal build/inventory commands; no generated file was edited by hand.
