Files
ESP32_Serial_Swiss_Army_Knife/docs/phase8d20_implementation.md
T

164 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 8D.20 — confirmed HTTPS lifecycle and reboot
Date: 2026-09-13. **The authorized HTTPS stop/restart/reboot API, ACK handoff, dispatcher integration and Settings UI are implemented and host-tested/build-verified. Independent review complete with no confirmed actionable defects; target validation remains pending.** Existing Network controls are reused, not duplicated. No unrelated remaining 8D.19 domain or 8D.21 identity work is included.
Independent reviewer verified installed-IDF callback/request ordering, nonreused IDs and lost-work reservation, original-login/deadline checks, canonical lifecycle ABA and failure handling, dispatcher/HTTPD lock separation, reboot shutdown path and UI duplicate gates. Reviewer independently reran lifecycle41, cookie lifecycle8+shared, dispatcher, UI153+renderer/HTML/CSP and diff checks PASS. Parent final `pio run` confirmation PASS6.82s,100,508 B RAM/1,821,505 B flash; diff check PASS. These are host/source/build checks, not real TLS/UDP/reset or runtime-reserve validation.
## End-to-end integration contract (supersedes prerequisite-only status below)
The continuation session found uncommitted integration source/tests already present beyond the prerequisite described by project memory. It preserved that work, inspected the actual implementation, ran all focused/broad checks, extended send-return/request-lifetime/relogin tests, clarified post-restart session expiry in the UI and completed these records. The previous prerequisite evidence is retained below as history; it is not the final feature cost.
### API and authorization
`web_lifecycle_settings.{c,h}` owns three optional exact routes: current-admin bodyless GET `/api/settings/lifecycle`, GET `/api/settings/lifecycle-operation`, and JSON POST `/api/settings/lifecycle-operation`. Existing cookie policy enforces the original live login/current principal, admin role, bounded framing, no query/body on reads, and Origin/CSRF on mutations. No CLI string, password, ticket, private key, identity mutation or new authentication bypass is exposed.
- Snapshot: `{generation,running,transitioning,controllable}`, 128-byte output buffer, zero-wait canonical owner copy. Busy/unavailable returns 503.
- POST: exactly `{action,generation}` with action `stop`, `restart` or `reboot`, unsigned nonzero/nonexhausted generation; no duplicate/extra fields, escapes, coercion or numeric overflow. At most 256 body bytes and four receives. Body/request scratch is wiped. Parser errors are 400; occupied/exhausted admission is 503.
- Accepted response: 202 with `{id,action,state:"pending"}`. This acknowledges **pending admission**, not execution, completion or peer receipt. Result buffer is 96 bytes; reads expose only the invoking session's slot, otherwise `{id:0,action:"none",state:"idle"}`. States are `idle`, `pending`, `ok`, `failed`, `cancelled`; executing is projected as pending. Result IDs are not secrets or authorization tokens.
- One static operation/result slot and one firmware-lifetime non-reused uint32 ID sequence. Exhaustion rejects until reboot; successful HTTPS restart does not reset IDs. Pending/executing work cannot be overwritten. Completed results are replaceable, not durable history or an idempotency/replay facility.
### ACK ownership, sequencing and failure bounds
1. HTTPD authenticates/parses and reserves the slot with the copied session/principal, expected HTTPS generation, two-second ACK deadline and 30-second total dequeue-admission deadline.
2. The synchronous response send must return successfully before one `httpd_queue_work()` submission. The callback argument contains only the ID cast through `uintptr_t`: no request, response, fd, socket DB or reusable operation pointer. The saved server handle is comparison-only: its matching callback clears it, or successful HTTPD destruction retires the lost reservation.
3. HTTPD invokes `ack_handoff` after the synchronous handler has returned. It checks/consumes the exact ACK ID, rejects expired work and submits only the operation ID with zero wait on the existing four-entry administration queue. It does no lifecycle work, socket IO, authentication or waiting. The dispatcher may be scheduled before this short callback returns; the callback holds no lock across queue submission and never waits for that dispatcher.
4. The existing dispatcher reserves the executing slot, copies/wipes shared principal material, validates the **original** session/principal/current admin, checks the 30-second deadline again after validation, then invokes the generation-conditional owner API outside all slot/console locks. A newer login for the same account never substitutes. Ordinary HTTP socket loss does not itself revoke the login or cancel admitted work.
5. Stop/restart uses canonical owner orchestration and its continuously held transition reservation. Once admitted, restart deliberately invalidates the old login and still completes its reserved start without revalidating that invalidated login. Owner failure is `failed`, not `cancelled`: even `ESP_ERR_INVALID_STATE` can follow auth invalidation/detach side effects. `cancelled` is reserved for rejection before owner admission.
Send failure never queues a callback. HTTPD queue failure or dispatcher queue failure cancels before admission without retry. An accepted-but-lost HTTPD callback keeps **one** ACK reservation, even after its operation deadline expires: subsequent POSTs fail busy rather than accumulating delayed callbacks. A late callback clears only its own reservation and cannot execute expired work. Successful canonical HTTPD destruction retires the reservation before a replacement server starts; failed destruction retains it. Same-handle/request-storage reuse and duplicate/old IDs are inert. Canonical UART0/reachable SSH `web stop` then `web start` is the recovery for a lost reservation; this closes every web client. A delayed callback that eventually arrives can also release its reservation.
Deadlines are checked lazily on callback, dequeue and API activity; there is no new firmware timer and no hard wall-clock cancellation/erasure promise. The two-second ACK phase includes synchronous response sending; a send returning at/after the deadline cannot admit work. Socket timeout, scheduling delay, successful send, aborted fetch or missing result proves neither peer receipt nor cancellation of an already-admitted action. HTTPD shutdown/start and reset are not bounded by these admission deadlines.
### SDK and reboot audit
Installed ESP-IDF 5.5.0 `httpd_main.c` was inspected: `httpd_queue_work` sends a control message, `httpd_process_ctrl_msg` calls it on HTTPD, and request/session processing is synchronous on that same owner. No inline callback from queue submission. Existing `CONFIG_HTTPD_QUEUE_WORK_BLOCKING` is disabled in defaults/active/generated configuration; the new module fails compilation if enabled. No private HTTPD access was added outside the established adapter. Real UDP loss, TLS timing and task scheduling remain target tests, not established by dependency doubles.
`web_server_reboot_current()` checks zero-wait canonical mutex/state/generation, reserves transition and advances generation, releases the mutex, then calls **the same `esp_restart()` API** used by `system_console` UART0, SSH owner, browser-admin owner and local reboot. No duplicate reboot runner or CLI dispatch. Source audit found no application shutdown-handler registration and no typed operation console-session pending/self-cleanup reservation. The dispatcher typed branch executes outside the console lock before ordinary console-session execution handling. ESP-IDF reset invokes its registered shutdown handlers then the no-OS reset; the typed path does not synchronously stop HTTPD first. Unexpected reset return reports failure and deliberately retains the transition reservation to prevent duplicate admission. Target reset/SDK shutdown behavior is unmeasured.
### UI and recovery contract
`web_ui.c` adds admin-only Settings → **HTTPS / Reboot**, using existing Settings navigation, same-session validation, bounded API reader and native explicit confirmations. Opening/refreshing/selecting does not mutate or change either terminal/broker lease. Service-wide confirmations include clients admitted after the snapshot and before execution, all web logins/both browser terminal routes, and unchanged identity/device working configuration for HTTPS-only actions. Reboot warns that all clients, SSH, USB and UART operation are interrupted and unsaved RAM-only working configuration can be lost. No saved configuration/identity reset is performed.
Requests have a single-flight 15-second whole-request deadline including session validation, headers and body, with controller/generation/navigation fences. Pending/unknown outcomes block duplicate submission across navigation/pagehide. Results are checked explicitly, not automatically polled. A lost ACK or replaced/mismatched ID/action remains uncertain; Refresh cannot clear that uncertainty or replay the operation. Known matched terminal results still require a fresh snapshot before another explicit confirmation. There is no automatic mutation retry, persistence, restored mutation or fresh-login result adoption. HTTPS restart expires the login; reload and explicitly sign in after recovery. Existing session-expiry handling closes both sockets and fences stale responses/401s.
The view links directly to the **existing Network** controller. Its backend and mutation/check logic are unchanged; existing bounded confirms, generation/current-login checks, uncertainty handling and no-replay tests pass. Network help/confirmation wording now accurately distinguishes restoring STA/AP reachability/UART0 administration from native USB's network-independent **UART1 serial**, not Wi-Fi or web administration. SSH recovery requires that SSH still runs and is reachable; Wi-Fi loss can remove it. USB is not promised uninterrupted access through whole-device reboot.
### Validation performed in the continuation
All commands passed with finite tool timeouts; no timeout or failed build occurred:
- `python3 tests/web_admin_transport/server_lifecycle.py`: **41 groups**, 34 prior + seven integration groups. Extracted real owner and ACK/execute functions are joined for canonical stop/reserved restart/reboot, ABA during validation, every stop failure, lost reservation through failed/successful destruction, same-server reuse, expiry/queue/revocation, reset admission/defensive return and all six optional route allocation positions plus unregister failure/restart.
- `python3 tests/web_cookie_auth/run.py --lifecycle`: **eight lifecycle groups plus shared auth**. Real handlers/parser/store, dependency doubles for HTTPD queue/owner/dispatcher; strict policy/parser/bounds, send/queue failures, ACK and dequeue/post-validation deadlines, original-login revocation races, duplicates, lost/late callback/ID exhaustion, admitted revocation and uncertain owner failure. Added send-return-at-deadline rejection and overwritten request/connection/scratch storage before callback/dispatch, demonstrating no retained request pointer.
- `python3 tests/admin_console_boundary/run.py`: real typed dispatcher ID routing, not-ready/full queue, unchanged four-entry queue and no command runner; existing owner/self-detach/deferred regressions.
- `python3 tests/web_ui_session/run.py`: **153 browser groups** (143 prior + ten lifecycle), seven-view renderer/HTML/CSP. Includes bounded late headers/body/session/ACK/401, navigation/pagehide, strict schemas, no mutation on view/Network link, confirmation/recovery wording, duplicate/unknown gating, both terminal drains, explicit accepted restart followed by expired login and fresh-document no-replay.
- `python3 tests/admin_console_boundary/lifecycle.py`, `accounts.py`; `tests/admin_ssh_policy/run.py`.
- `python3 tests/web_cookie_auth/run.py` and each existing variant `--ssh`, `--network`, `--accounts`, `--serial-settings`, `--broker`, `--display`, `--admin`, `--settings`.
- `python3 tests/web_admin_transport/run.py --tickets`, `tests/web_admin_tickets/run.py`, `tests/web_httpd_idle/run.py`, `tests/web_serial_performance/run.py`.
- `python3 tests/web_session_store/run.py` and `--serial`; `tests/ssh_management/run.py`, `tests/web_network_settings/run.py`, `tests/session_broker_diagnostics/run.py`, `tests/web_auth_parse/run.py` (294 cases), `tests/web_diagnostics/run.py`, `tests/web_security/run.py`, `tests/web_login_ui/run.py`.
- `git --no-pager diff --check`, source self-review and bounded `pio run`. Final resource confirmation follows below.
These are host/renderer tests with controlled boundary failures, not real HTTPD scheduling/TLS/on-wire delivery, physical reboot, browser geometry, hardware/full-mix throughput or independent review. No upload/erase/commit/device action, identity/SDK/dependency/generated-asset change or full M3 sign-off.
### Integrated resource evidence
Reference before 8D.20: **100,404 B RAM / 1,808,441 B flash** (previous agent's measured baseline). Prerequisite build: **100,404 / 1,808,589 B**. Continuation first `pio run` passed in **21.86 s**, **100,508 / 1,821,441 B** before the final explicit post-restart-login wording. This is **+104/+13,000 B** versus pre-phase baseline, **+104/+12,852 B** versus prerequisite. Final wording/build measurement is recorded separately below; the original baseline was not recreated by reverting uncommitted work.
Target `nm -S` for the new module: operation slot **88 B**, lock **8 B**, ID sequence/ACK ID/server comparison handle **4 B each** = **108 B mutable object storage** (linked RAM delta includes alignment/placement, not simply the object sum). Read-only action/state pointer arrays **12/24 B**. No module heap/PSRAM allocation or internal fallback. Handler locals include one operation copy, authenticated session view,256-byte body,40-byte content type and96-byte response; snapshot response128 B; dispatcher has one operation copy. Compiler stack frames/runtime high-water remain unmeasured.
Handlers increase **36→39** (three optional routes and three table pointers plus HTTPD-owned descriptor/URI allocations). Six HTTPS sockets, two serial slots, one browser-admin slot, four cookie sessions, existing ticket capacities, four-entry dispatcher/item capacity, task count/priorities/stacks and firmware timers remain unchanged. At most one lifecycle HTTPD control work item and one operation ID on the existing dispatcher; lost HTTPD work stays reserved rather than growing queued work. Optional route allocation failure cannot take down other domains. CPU160 defaults/active/generated and nonblocking HTTPD queue configuration verified; no CPU/config/throughput tuning. Static size is not approval of runtime heap/largest-block/HTTPD or dispatcher stack reserves.
### Final confirmation
After the explicit post-restart session-expiry UI wording, `pio run` passed in **22.85 s** (120,000 ms timeout): **100,508 B linked RAM / 1,821,505 B flash**. Final total delta versus pre-phase100,404/1,808,441 is **+104 B RAM / +13,064 B flash**; versus prerequisite100,404/1,808,589 it is **+104/+12,916 B**. The final UI153, cookie lifecycle8+shared, owner/pipeline41 and dispatcher suites were rerun and passed after the wording change. Broad regressions above passed before that wording-only change. No build timed out. Independent parent review is the next handoff, not represented by these self-checks.
## Historical prerequisite record
The following sections preserve the previous internal-slice evidence. Statements that integration was unimplemented describe that earlier checkpoint only.
## Scope and split decision
Read agent code-map, relevant architecture/design decisions, current-state, exact phase8d_plan 8D.20 row and work-unit/resource rules. Inspected `main.c`, canonical system reboot, administration dispatcher, browser-admin owner lifecycle, existing SSH typed backend/UI and existing Network confirmations. Initial worktree was clean.
The explicit user request permits proceeding with 8D.20 despite 8D.19 being only the completed/reviewed SSH slice. No unrelated other-service 8D.19 prerequisite is invented. Existing Network already owns Wi-Fi controls and must not be duplicated.
The concrete missing HTTPS prerequisite was generation-checked public lifecycle admission and a restart reservation spanning stop through start. Existing `web_server_stop()` followed by `web_server_start()` has a lifecycle-admission gap and no expected-generation argument. A snapshot comparison followed by unconditional stop would allow stale work to affect a replacement server.
Before editing, split under the plan's 600800-authored-line scope threshold: owner adaptation, bounded HTTPD ACK/dispatcher handoff, authenticated typed routes, UI session/uncertainty handling and their owner/HTTP/UI fault suites were estimated to exceed one bounded chunk. Selected an internal prerequisite, which the plan permits, rather than exposing partially protected routes or omitting acceptance tests. This is **not delivery of the requested full API/UI feature**. The remaining complete integration is listed below.
Changed production files: `src/web_server.{c,h}` only. Extended existing `tests/web_admin_transport/server_lifecycle.py`; related phase/agent documents updated. No new task, runner, queue, timer, route, dispatcher item, stack size, socket/session/ticket capacity, NVS schema, configuration, dependency, SDK or asset change. No reboot/identity/certificate/key/factory-reset operation added. Existing 8D.18/UI and SSH19, signed-off CPU160 combined binary WebSocket send, Network, UART0 recovery and USB ownership are preserved.
## Public owner contract
- `web_server_get_management_snapshot()` is a zero-wait, secret-free projection `{generation, running, transitioning, controllable}` using the existing server mutex. Failure clears the destination; null is invalid argument, uninitialized owner is invalid state, mutex contention is timeout. It performs no HTTPD work, socket query, task scan, authentication or persistence.
- `web_server_stop_current(expected_generation)` and `web_server_restart_current(expected_generation)` acquire the existing mutex with zero wait, then atomically compare generation and lifecycle state before canonical stop admission. Zero generation is invalid argument. Stale/saturated generation, absent server, transition or retained lifecycle failure is invalid state without lifecycle side effects.
- These APIs are **off-HTTPD only**, just like ordinary start/stop. A future caller must authorize the original login/current admin, complete its bounded ACK handoff and invoke from the existing dispatcher. Neither lifecycle API authenticates, sends an ACK, waits a bounded time for HTTPD, or cancels admitted work after revocation.
- Restart uses the same canonical stop implementation. On successful stop it keeps `s_transitioning` reserved while clearing the old handle, then invokes the private reserved start path. Competing canonical/conditional lifecycle callers cannot acquire the stopped-to-start gap. The server mutex is released around authentication, transport detach, TLS start/stop, registration and external callbacks; it is not held while waiting for HTTPD.
- Any idle/admin/serial hard-detach or SSL-stop failure skips start, releases the transition reservation, retains existing ownership/error semantics and makes conditional controls unavailable. Canonical `web_server_stop()` remains available for recovery. Existing serial-detach timeout semantics are unchanged: this timeout permits SSL destruction, then restart after successful destruction.
- Start-side failure releases the reservation. If cleanup fails, the partial server remains owned and canonical stop must clean it before another start. No identity/configuration rollback or automatic retry occurs.
- Repeated `web_server_init()` no longer clears a retained `ESP_ERR_INVALID_STATE` lifecycle error. Only first initialization resets its initial sentinel, preventing an unsuccessful recovery start/init from making failed cleanup appear controllable.
### Generation and completion limits
One firmware-lifetime uint32 generation begins at 1. Every admitted canonical or conditional start/stop advances it, including an admission that later fails. Rejected calls do not advance it; counter clear and repeated init do not reset it. A successful restart normally advances twice. Same HTTPD-handle reuse and canonical stop/start ABA cannot restore an old confirmation.
Generation saturates at UINT32_MAX. Conditional admission then fails closed, while ordinary start/stop recovery remains available. A restart already admitted at UINT32_MAX1 can complete its reserved start at saturation; no new conditional action is admitted with the saturated token. Reboot will reset firmware state and invalidate all old browser sessions; no typed reboot admission is part of this slice.
`controllable` means a running, nontransitioning server with a successful last lifecycle result and a nonexhausted generation. It does not establish network reachability, response receipt, browser-session validity, or a hard shutdown deadline. A lifecycle failure may already have invalidated all web logins. Once stop is admitted, later logout/revocation/timeout must not be reported as cancellation of that stop or its reserved restart.
## Validation actually performed
All commands below passed, each terminal group with a finite timeout. No build timed out.
- `python3 tests/web_admin_transport/server_lifecycle.py`: **34 groups**, 27 existing plus seven new owner groups. Compiles extracted production lifecycle functions, public header and real URI initializers against bounded dependency doubles.
- `python3 tests/admin_console_boundary/run.py`, `lifecycle.py`, `accounts.py`; `python3 tests/admin_ssh_policy/run.py`.
- `python3 tests/web_admin_transport/run.py --tickets`; `python3 tests/web_admin_tickets/run.py`.
- `python3 tests/web_httpd_idle/run.py`; `python3 tests/web_serial_performance/run.py` (existing combined-send/SDK-boundary regressions).
- `python3 tests/web_session_store/run.py`, and `--serial`.
- `python3 tests/web_ui_session/run.py`: existing 143 browser groups plus renderer/HTML/CSP checks. No UI edits or new 8D.20 UI test claims.
- `python3 tests/web_cookie_auth/run.py` and each existing variant: `--ssh`, `--network`, `--accounts`, `--serial-settings`, `--broker`, `--display`, `--admin`, `--settings`.
- `python3 tests/ssh_management/run.py`, `tests/web_network_settings/run.py`, `tests/session_broker_diagnostics/run.py`, `tests/web_auth_parse/run.py`, `tests/web_diagnostics/run.py`, `tests/web_security/run.py`, `tests/web_login_ui/run.py`.
- `git --no-pager diff --check`; production diff self-review. Final lifecycle34/diff rerun and bounded build confirmation also passed. This is not independent parent review.
New owner cases cover preinit/null/zero arguments; zero-wait mutex contention and cleared snapshots; same-handle canonical ABA; counter clear; successful stop; reserved restart with deterministic competing canonical and conditional calls at the released-mutex gap; each stop failure stage; repeated init preserving cleanup failure; six restart start-side failures including failed partial-server cleanup; saturation without wrap; transition rejection; and inherited serial timeout behavior. All external lifecycle doubles assert that the server mutex is not held where required. Failure recovery uses actual canonical orchestration, not replacement management functions.
Limits: no real TLS/HTTPD scheduler, device sockets, physical reboot, browser geometry, queue/ACK pipeline, original-login typed admission, UI stale-result or typed reboot tests were added or claimed. Existing idle/admin/store tests exercise their own callback/lifecycle fences, **not an unimplemented 8D.20 ACK handoff**. No secrets are added to production projections/logs or new test output. No independent reviewer tool is available in this session; parent review is pending, not simulated by self-review.
## Build and resource evidence
`pio run` timeout: 120,000 ms.
| Measurement | Time | Linked RAM | Flash |
|---|---:|---:|---:|
| Actual pre-edit baseline | 21.10 s | 100,404 B | 1,808,441 B |
| Post-production-change build | 35.05 s | 100,404 B | 1,808,589 B |
| Final confirmation | 20.02 s | 100,404 B | 1,808,589 B |
| Delta | — | +0 B | +148 B |
Target object `nm -S` reports `s_generation` 4 B, management snapshot function 165 B, conditional wrappers 26 B each. The public management entry points have no production callers yet and are linker-discarded; the linked delta is **not** the eventual end-to-end feature cost. The additional generation exists in the firmware, with placement yielding no net linked-RAM increase. No heap/PSRAM allocation is introduced. The new management projection has four scalar fields; no request/response buffer exists yet. Existing 36 handlers, six HTTPS sockets, two web serial slots, one browser-admin socket, four cookie sessions and four-entry dispatcher remain unchanged.
CPU160 was checked in `sdkconfig.defaults`, active sdkconfig and generated sdkconfig.h. PlatformIO's generic board banner says 240 MHz; it does not override the verified generated configuration. No CPU tuning occurred. Runtime HTTPD/dispatcher stack floors, heap minima/largest blocks and reserve approval remain unmeasured; static size is not runtime reserve evidence.
## Historical remaining-integration checklist — now implemented above
1. Add one narrow bounded HTTPS/reboot operation domain on the existing dispatcher, with optional current-admin cookie/Origin/CSRF routes and a secret-free bounded snapshot/result. Do not introduce a generic runner or increase queue/task budgets.
2. Specify and test ACK handoff before stop/restart/reboot: HTTPD must never wait for its own shutdown. Owner completion must be bounded/generation-qualified without request/raw-fd lifetime capture. Handle response send failure, queue failure, accepted-but-lost/delayed callback, expired/revoked original login, shutdown and callback ABA. A late callback must not revive a retired action or authorize a replacement login/server. A bounded ACK phase is not a bounded lifecycle completion or peer-receipt guarantee.
3. Revalidate original login, principal/current admin and dequeue deadline immediately before action admission. Use the new conditional owner APIs for HTTPS stop/restart, and establish narrow canonical reboot admission rather than executing a CLI string. Do not revalidate a deliberately invalidated login between the already-admitted stop and its reserved start. No certificate/key/identity/reset action.
4. Add coherent Settings API/UI with explicit service-wide confirmations, warning that all affected clients—including clients admitted after the snapshot—disconnect. HTTPS stop needs UART0 or still-running reachable admin SSH `web start`; native USB remains UART1 serial, not an administrative web-start route. Reboot affects the entire device, including SSH, USB and UART operation during restart, loses unsaved RAM-only working configuration, and does not imply uninterrupted recovery links.
5. Reuse existing Network controls instead of duplicating Wi-Fi. Audit their confirmation/recovery wording and uncertainty behavior within this phase: Wi-Fi disruption can also remove SSH reachability. USB preserves network-independent UART1 access for network-only operations, not administration or uninterrupted reboot.
6. Bound pending/result and browser request lifetimes. Disable duplicate submission during pending/unknown outcomes; never automatically retry or restore a mutation. Original-login/result-ID/session fences must prevent old responses from being adopted after reconnect. A missing ACK, timeout, revocation, connection loss or result replacement is not evidence that admitted work was cancelled. Recovery reads and explicit reconnect/relogin must not replay an operation.
7. Add real production-path owner/dispatcher/HTTP/UI tests for the above, rerun regressions/build, measure incremental linked/object resources, and request independent parent review. No 8D.21 or unrelated remaining 8D.19 work.
## Pending target and review checklist
- [ ] Independent parent review of this prerequisite and later integrated ACK/API/UI work.
- [ ] Canonical UART0 and SSH web stop/start/recovery after these internal changes.
- [ ] Target typed HTTPS stop/restart/reboot flows (integration now implemented and host-tested).
- [ ] Slow/lost ACK, delayed callback, busy dispatcher, expired/revoked login and failed stop target fault cases where practical; no stale execution/replay.
- [ ] HTTPS-stop recovery through UART0/reachable SSH; explicit fresh web login after restart, no old operation restore.
- [ ] Reboot/all-client/USB interruption and unsaved-working-state warnings verified against target behavior.
- [ ] Existing Network Wi-Fi loss/recovery without duplicated controls or false USB-admin claims.
- [ ] Existing UI18/SSH19/Network/auth/logout and full supported client mix at signed-off CPU160/combined WS send; broker byte/drop accounting.
- [ ] Settled boot/full-mix/recovery memory minima, largest blocks and HTTPD/dispatcher stack margins; no invented reserves.
No device operation, upload, erase, commit, target sign-off, full 8D.20 completion or M3 claim.