# Delivery 5 lifecycle batch B

## Caller audit

`AppFrame` is constructed and mounted by `admin/src/main.js` and its header factory is consumed by `components/PageHeader.js`; `components/index.js` re-exports both. Its constructor, `mount()`, `setActiveRoute()`, and public fields used by `main.js` remain compatible.

`PriorityBadge` is constructed by `components/AssignmentCard.js`, which consumes `getElement()`, and is re-exported by the component barrel. That contract remains unchanged.

`BulkActionBar` is constructed, assigned `onSelectionChange`, repeatedly sent through `render(container)`, and destroyed by `pages/string-translate.js` and `pages/content-translate.js`; it is also re-exported by the barrel. Those methods and action/modal behavior remain unchanged.

## Lifecycle design and tests

AppFrame now owns a retained frame root. `mount()` is idempotent, `update()` normalizes all constructor options and rebuilds only children, and `destroy()` unbinds the document Escape handler, deep-destroys dropped descendants, and removes the frame root. Legacy `closeMobileNavigation()` and `setActiveRoute()` are destroyed-guarded. `appframe-lifecycle.test.js` covers repeat mounting/order, all-key update, undefined parity, listener cleanup, destroyed guards, and resurrection.

PriorityBadge retains its badge root while refreshing its non-interactive children from a shared renderer. `prioritybadge-lifecycle.test.js` covers repeat mount/order, all three update keys, root identity, undefined defaults, guarded `updatePriority()`, destruction, and resurrection. `getElement()` and `fromPriority()` deliberately remain unchanged.

BulkActionBar retains the visible bar root and replaces action children wholesale after a deep destroy scan, so stale button closures cannot survive an update. Its existing `render(container)` remains the production entry point; `mount()` supplies lifecycle resurrection. All existing render/modal helper entry points are destroyed-guarded. `bulkactionbar-lifecycle.test.js` covers repeat rendering/root identity, a combined update spanning every constructor option, undefined defaults, listener/modal teardown, legacy guards, and resurrection.

The architecture inventory records `mount/update/destroy` for all three modules. Gates and mutation sweeps are the orchestrator's responsibility and were not run here, as requested.

## Mutation coverage receipt

Added AppFrame explicit-undefined activeId default parity coverage.
Added AppFrame update-after-destroy-remount coverage.
Added PriorityBadge update-after-destroy-remount coverage.
Added PriorityBadge repeated-mount sibling-order coverage.
Added BulkActionBar updated onGenerate confirmation coverage.
Added BulkActionBar selectedIds update-after-destroy-remount coverage.

## Review round 2

AppFrame no longer recreates its content or modal containers during `update()`: route children retain their node identity while navigation and version chrome refreshes. BulkActionBar now tears down an open confirmation and resets its transient language selection when any state the confirmation displays or acts on changes, requiring the user to reopen it against current state.

## Orchestrator gate receipt (measured)

- Mutation sweep: 16 mutants (every update branch per component, default-normalizer strip, mount destroyed-reset removal, PriorityBadge parentNode-guard swap, BulkActionBar onGenerate rebind) — all KILLED after one coverage-repair round (6 initial survivors: AF-activeid-default, AF/PB/BB mount-reset, PB-parent-guard, BB-ongenerate-branch); restored clean after each.
- Focused suites: appframe/prioritybadge/bulkactionbar — 33 tests after repair (27 initial + 6), all pass.
- Full admin suite with `--maxWorkers=2`: `Test Files 69 passed (69)`, `Tests 883 passed (883)`.
- eslint clean on the three components; production build twice byte-identical on `dist/js/main.js`; two lazy-chunk hashes legitimately rotated by the source changes and are committed with this change.
- `ui-inventory --check` ok (124 modules); `ui-gate` ok; `factory-gate.sh all` green.
- Base note: implementation was authored against a pre-Batch-A base and merged with origin/master afterward; post-merge diff against origin/master contains only Batch B files.

## Review-round-2 orchestrator gate receipt (measured)

Two confirmed review defects repaired: AppFrame rebuilt contentElement/modalElement on every update (a route's live DOM, including focused inputs, was destroyed by an unrelated `update({version})`) — update now repaints only the chrome the changed keys affect, via `_renderNavigation`/`_renderVersion`, leaving content children untouched; BulkActionBar left an open confirmation modal showing stale languages/items while confirmation would act on the new state — update now closes the modal and resets its transient selection whenever a key the modal displays or acts on changes.

- Targeted mutants for both fixes: killed (content-preservation across update({version}/{items}/{activeId}); modal-close-on-update).
- Full Batch B sweep re-run: 16 mutants, all KILLED, baseline restored clean.
- Full admin suite with `--maxWorkers=2`: `Test Files 69 passed (69)`, `Tests 886 passed (886)`.
- eslint clean; production build twice byte-identical on `dist/js/main.js`; `ui-inventory --check` ok (124 modules); `ui-gate` ok; `factory-gate.sh all` green (exit 0).
