# REPORT — controller v0.197.0: R-203, both halves (2026-08-04) Deployed to demo-hp guest 9201. No hub change. Nothing deleted, wiped or moved; the R-201 drill was not resumed. ## What shipped **Part 1 — one resolver, five callers.** `appbackup.NamespaceRootFor` / `IsEnrolledDrive` are now the single expression of the drive-kind rule; `backup.Manager.namespaceRoot` and `stacks.Manager.inGuest` delegate (they were two copies that already differed over `filepath.Clean`). Routed through it: `stacks/deploy.go` `withPathVars` (the live defect), `appexport/fabplan.go` + `export.go` via a new `GetStackNamespaceRoot` provider method, `web/handlers.go`'s FileBrowser mounts (the fifth site, which the spec did not name — latent today), and `stacks/delete.go`'s `ExportDataMounts` (its own commit). `ComputeFabBuckets` now receives the namespace root, matching `ComputeCaptureSet`. **Part 2 — the verdict.** `last_status` gains `incomplete`; the capture gaps are recorded structurally so the verdict has something to act on; the operator hears it through the existing per-run digest. ## Proven live on demo-hp ``` bind before: /mnt/sys_drive/userdata/media/books bind after: /mnt/sys_drive/felhom-data/userdata/media/books capture log: 0 mandatory path(s) -> 1 mandatory path(s) $ restic ls -l latest --tag calibre-web -rw-r--r-- 1000 1000 181 2026-08-04 12:53:06 .../media/books/DRILL-SENTINEL.txt ``` The file, by name and size, inside the snapshot — not a green status. ## Tests `go build && go vet && go test ./...` rc=0; `controller_gates.py --fast` OK. Table-driven over BOTH drive kinds, because this defect survived by being invisible on the kind that already worked. **One red-proof PASSED and the test was wrong, not the code:** the first Scenario-C test reached only `offboxCaptureSet` while the mutation lives in `runOffboxInternal`. Replaced with a run-level test driving `RunOffboxBackup`; it now fails under both an unreachable gap-recording and an unconditional `ok`. Scenario D's proof also could not fail by construction (the tier filter already excludes optional paths) — the two-part mutation that does is recorded. ## Corrections to the spec - **Five call sites, not four.** The fifth is the FileBrowser mount builder. - **`ExportDataMounts` is export-only despite living in `delete.go`.** Its only caller is the `.fab` adapter, and the delete path's own `ProtectedHDDPaths` guard is layout-agnostic by construction, so the destructive-path risk the spec flagged does not exist. ## Observations `resolveAbs` resolves `RootHDD` and `RootUserdata` against one root — fine on enrolled drives, and now consistent between export and backup, but whether `${HDD_PATH}` should mean the namespace root on the system drive is a separate decision affecting every deployed app. The deploy API is first-deployment-only (409 after), so an existing app picks up the corrected path via start/redeploy. The CSRF form field is `_csrf`, not `csrf_token` — second session running that this cost time.