diff --git a/.planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md b/.planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md new file mode 100644 index 00000000..ae96ddee --- /dev/null +++ b/.planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md @@ -0,0 +1,331 @@ +--- +phase: 01-federation-mesh-hardening +plan: 18 +subsystem: ui +tags: [verification, deploy, dev-pair, on-device, checkpoint, gap-closure] +status: blocked-on-checkpoint + +requires: + - phase: 01-federation-mesh-hardening + plan: 12 + provides: "UIFIX-02 connected-nodes sibling-matched scroll contract" + - phase: 01-federation-mesh-hardening + plan: 13 + provides: "UIFIX-03 onboarding scroll cue" + - phase: 01-federation-mesh-hardening + plan: 14 + provides: "UIFIX-04/UIFIX-06 paid-item lightbox + Opening… loader" + - phase: 01-federation-mesh-hardening + plan: 15 + provides: "UIFIX-05 PiP custodial-host handoff" + - phase: 01-federation-mesh-hardening + plan: 17 + provides: "UIFIX-01 transport-pill wrap fix + pin test" +provides: + - "All six UIFIX fixes delivered to archi-dev-box and proven present in the bundle the node actually serves, fetched over HTTP and matched against the live service-worker chunk manifest" + - "An on-device boot check of the deployed bundle at 1440x900 and 390x740 — zero console errors, zero page errors, zero failed requests" + - "Two of the planner's flagged open questions settled from the node's own data: it HAS one purchased image, and it has NO purchased video and NO audio/video content at all" + - "A per-check VERIFIED / NOT VERIFIED table with the exact human action each unverified check still needs" +affects: [cloud, web5, onboarding, media-viewer] + +tech-stack: + added: [] + patterns: + - "Served-bundle verification must resolve the live chunk set first: /opt/archipelago/web-ui/assets accumulates every prior deploy's hashed chunks, so a naive `grep -rl` across the directory returns hits from dead chunks. The service-worker precache manifest (sw.js) is the authoritative list of the chunks the current build actually loads — grep only those, and fetch them over HTTP rather than off disk." + +key-files: + created: + - .planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md + modified: [] + +key-decisions: + - "No source file was modified and no fix was made. This is a verification plan (files_modified: []) and three other executors were live in the same working tree on plans 10-02/10-04/10-06 for its whole duration; anything found here is recorded as a finding, not patched inline." + - "Task 2's six numbered checks are recorded NOT VERIFIED. Every one of them requires an authenticated session on archi-dev-box, and the node's UI password was not available to this executor (02-08 records it being supplied out-of-band by the coordinator and passed only via ARCHY_PASSWORD). No credential was guessed against a live node holding real federation trust and real funds." + - "STATE.md/ROADMAP.md/REQUIREMENTS.md were deliberately NOT updated and NOT committed. .planning/STATE.md was already dirty with another agent's in-flight edit, and the execution brief restricted this plan's commit to the SUMMARY alone. UIFIX-01..06 must NOT be marked complete until the Task 2 checkpoint is answered." + +requirements-completed: [] + +metrics: + duration: ~20min + tasks: 1 of 2 (Task 2 is a blocking human checkpoint, unanswered) + files-changed: 0 source files +completed: 2026-08-02 +--- + +# Phase 1 Plan 18: Six-Fix Sign-Off on archi-dev-box — Summary + +**Task 1 is complete with full evidence: all six UI fixes are live in the bundle archi-dev-box actually serves, and the deployed bundle boots clean on the node. Task 2 — the six-check human sign-off, which is the entire point of this plan — is UNANSWERED. No UIFIX requirement is closed by this summary.** + +## Verdict + +**Not signed off.** The operator's response is this plan's verification (``), and there was no operator response. Task 1's delivery evidence is complete and reproducible; Task 2's six numbered behavioural checks all remain open. + +--- + +## Task 1: Deploy to archi-dev-box — COMPLETE + +### Exact command and host list + +``` +ARCHIPELAGO_TARGET=archipelago@archi-dev-box scripts/deploy-to-target.sh --frontend-only +``` + +Run from `/home/archipelago/Projects/archy` on **archi-dev-box itself** (`hostname` = `archi-dev-box`; +Tailscale `100.69.68.39`). Exit code **0**, 346 s, `2026-08-02T18:20:08Z → 18:25:54Z`. + +**Host list targeted: `archipelago@archi-dev-box` and nothing else.** Confirmed three ways: + +1. The script's own deploy manifest written on the node: + `/opt/archipelago/deploy-manifest.json` → `"target": "archipelago@archi-dev-box"`, + `"deployed_from": "archi-dev-box"`, `"commit_short": "527f6023"`, + `"deployed_at": "2026-08-02T18:25:26Z"`. +2. `--tailscale`, `--tailscale-node=`, `--both` and `--fleet` were **not** passed, so the + `SEC_TARGET` fan-out branch (lines 355–385, which contains the only `192.168.1.228` reference on + any AIUI path) was never entered. The AIUI step that did run is the `LIVE` branch at lines + 725–736, which talks only to `$TARGET_HOST`. +3. No release was cut, no OTA manifest touched, no catalog signed. Acceptance check: + `git status --short -- release-manifest.json releases/ app-catalog/` → **empty**. + +The prohibition was respected: **frontend only, dev pair only, no fleet node, no alpha-tester node, +no Tailscale path, no OTA, no release.** + +### Deployed commit contains all six fixes + +Deployed commit `527f6023` (branch `main`). Every contributing commit is an ancestor of it: + +| Plan | Commit | In `527f6023`? | +|---|---|---| +| 01-12 (UIFIX-02) | `ceafbcb5`, `b5628d96` | yes | +| 01-13 (UIFIX-03) | (in `527f6023` via HEAD) | yes | +| 01-14 (UIFIX-04/06) | `bc9a210c` | yes | +| 01-15 (UIFIX-05) | `3288a02d` | yes | +| 01-17 (UIFIX-01) | `8255b69a` | yes | + +`git status --short -- neode-ui web` was **empty** before and after the deploy — the frontend was +built from committed state only, and `npm install` did not dirty `package-lock.json`. The manifest's +`dirty: true` refers to other executors' concurrent backend work in `core/` (plans 10-02/10-04/10-06), +not to any frontend file. + +### Served-bundle grep — the T-01-83 check + +A trap was hit and is worth recording, because the naive version of this check would have produced a +false pass. `/opt/archipelago/web-ui/assets` accumulates hashed chunks from **every previous deploy**, +so `grep -rl` across that directory finds strings in long-dead chunks. Before the deploy, a naive +directory grep reported UIFIX-02/03/04/05 "FOUND" — but three of those hits were in stale chunk +generations, and the *live* `Web5-*.js` at that moment did not contain the 40rem floor at all. + +The live chunk set was therefore resolved from the service-worker precache manifest (`sw.js`, 110 +entries, containing the entry chunk `index--lyLAgu1.js` referenced by the served `index.html`), and +each chunk was then **fetched over HTTP from `http://archi-dev-box`** and grepped — not read from +`web/dist`, and not read from the assets directory. + +| Fix | String probed | Live chunk fetched over HTTP | Result | +|---|---|---|---| +| UIFIX-01 | `flex flex-wrap items-center gap-2 text-xs` | `/assets/Cloud-yWcQ9GU-.js` | **PRESENT** | +| UIFIX-01 | `inline-flex shrink-0 items-center gap-1.5` | `/assets/Cloud-yWcQ9GU-.js` | **PRESENT** | +| UIFIX-02 | `xl:basis-0` | `/assets/Web5-BvURT8TJ.js` | **PRESENT** | +| UIFIX-02 | `min-h-[40rem]` | `/assets/Web5-BvURT8TJ.js` | **PRESENT** | +| UIFIX-03 | `One more step below` | `/assets/OnboardingSeedGenerate-C7UXVA3p.js` | **PRESENT** | +| UIFIX-04 | `resolveBlobUrl` | `/assets/Cloud-yWcQ9GU-.js` | **PRESENT** | +| UIFIX-05 | `lightbox-pip-handoff` | `/assets/MediaLightbox-DKht-qI-.js` + `MediaLightbox-DAsuRYOD.css` | **PRESENT** | +| UIFIX-06 | `Opening…` | `/assets/Cloud-yWcQ9GU-.js` | **PRESENT** | + +URL base fetched: **`http://archi-dev-box`** (`index.html`, `sw.js`, and each chunk under `/assets/`). +All chunk hashes changed from the pre-deploy build, so this is provably a new build and not a no-op. + +### On-device boot check (real Chromium, on the node) + +Beyond the grep, the deployed bundle was loaded in a real Chromium (Playwright 1.58.2, chromium-1208) +against `http://archi-dev-box` at **1440×900** and **390×740**: + +| Viewport | App mounted | Entry script | Console errors | Page errors | Failed requests | +|---|---|---|---|---|---| +| 1440×900 | yes | `/assets/index--lyLAgu1.js` | 0 | 0 | 0 | +| 390×740 | yes | `/assets/index--lyLAgu1.js` | 0 | 0 | 0 | + +Both land on `http://archi-dev-box/login` with a password field. Post-deploy health check reported +`Health: OK (200) after 5s`; backend `active`. The entry script matches the served `index.html` and +the `sw.js` manifest, so the boot check exercised the same build the grep table describes. + +**This proves the deploy is live and healthy. It proves nothing about any of the six numbered checks.** + +### archy-x250-dev — the second dev-pair node + +**OFFLINE. Recorded as an explicit gap, per the plan's instruction not to wait for it or pretend the +pair was covered.** + +``` +100.113.100.55 archy-x250-dev.tail08d8f2.ts.net ssmithx@ linux + active; relay "mad"; offline, last seen 2d ago, tx 5573880 rx 0 +``` + +MagicDNS has no record (`lookup archy-x250-dev ... no such host`), so it cannot even be addressed by +name. This continues unbroken from phase 2, where 02-08 checked three times and never found it up. +**archy-x250-dev has received neither phase 2's nor this plan set's frontend and still needs the same +`--frontend-only` deploy once it is reachable.** + +--- + +## Task 2: Six-fix sign-off — NOT DONE (blocking human checkpoint) + +### Per-check status + +| # | Check | Requirement | Status | Why | +|---|---|---|---|---| +| 1 | Connected-nodes card sibling-matched height + inner scroll, tab-switch stability, single-column unchanged | UIFIX-02 | **NOT VERIFIED** | Needs an authenticated session on the node; no UI password available to this executor | +| 2 | Onboarding cue at ~1280×620, click-to-reveal, Continue stays disabled, no cue at full height | UIFIX-03 | **NOT VERIFIED** | Needs an authenticated session; and see the safety note below — this executor will not drive a seed-generation flow on a provisioned node holding real funds | +| 3a | Purchased **picture** → row spinner + "Opening…" → app lightbox, no new tab | UIFIX-04 / UIFIX-06 | **NOT VERIFIED** | Needs an authenticated session. The content exists (see below), so this one is genuinely runnable by a human today | +| 3b | Purchased **video** → lightbox with player controls | UIFIX-04 | **NOT VERIFIED — and NOT CURRENTLY TESTABLE** | The node owns no purchased video (see below) | +| 3c | Purchased **music** → bottom-bar player, not the lightbox | UIFIX-04 | **NOT VERIFIED — and NOT CURRENTLY TESTABLE** | The node owns no purchased audio (see below) | +| 3d | Double-click issues one load, not two | UIFIX-06 | **NOT VERIFIED** | Needs an authenticated session | +| 4 | PiP handoff animation, survives tab changes, survives a buffering pause, clean explicit close, unchanged normal close/Escape | UIFIX-05 | **NOT VERIFIED — and NOT CURRENTLY TESTABLE** | Requires a video in the lightbox; the node has **no video content at all** (see below) | +| 5 | FIPS/Tor pills fully readable at phone width on peer cards and peer files, long names included; desktop unchanged | UIFIX-01 | **NOT VERIFIED** | Needs an authenticated session and a real phone or stated device emulation | +| 6 | Nothing else moved — page margins, tab slide transitions, existing animations | (standing visual-invisibility rule) | **NOT VERIFIED** | Needs an authenticated session and human perceptual judgment | + +**Zero of the six numbered checks passed. None of them failed either — none were run.** The plan's +transparency prohibition forbids signing any node-named check off on local-preview evidence, and that +is all the prior evidence is: 01-12/13/15/17 each verified themselves against a local dev preview or +`:4321` build preview, and 01-14 against unit tests only. + +### The two blockers, precisely + +**Blocker A — no authenticated session.** `http://archi-dev-box` redirects to `/login` and requires a +password; `POST /rpc/v1 {system.status}` returns `401 Unauthorized`. 02-08 records the node's UI +password being supplied out-of-band by the coordinator and passed only via the `ARCHY_PASSWORD` +environment variable. It was not supplied for this run. **No credential was guessed** — this node +holds real federation trust, real purchases and real funds, and probing passwords against it is not a +verification step. + +**Blocker B — missing media on the node.** Settled from the node's own data (read-only, counts and +mime types only; no filenames, no content, no seed material read): + +- `/var/lib/archipelago/purchased-content/owned.json` — **1 owned item, mime `image/jpeg`**. + So the planner's flagged question is answered: **archi-dev-box does have purchased content, but + exactly one item and it is a picture.** Check 3a is runnable on real purchased content today. +- **No purchased video and no purchased audio exist**, so checks 3b and 3c cannot be exercised on + purchased content on this node at all. +- A scan of `content/`, `blobs/`, `filebrowser-data/`, `content/files/`, `~/Files`, `~/Videos`, + `~/Music`, `~/Documents` found **zero video files and zero audio files anywhere**. Check 4 (PiP) + needs a video in the lightbox and therefore **cannot be run on this node as it stands.** + +Per the plan's own instruction, this is said plainly rather than passed on the demo: **a demo-only or +preview-only pass for the paid-content and PiP paths would be exactly the divergence class this phase +exists to remove.** + +### What still needs a human at a device + +1. **Supply the archi-dev-box UI password out-of-band** (as in 02-08), or drive the checks by hand in + a logged-in browser on the node. Without this, checks 1, 2, 3a, 3d, 5 and 6 cannot start. +2. **Put one video file on the node** — upload an `.mp4` through My Files is enough — so check 4 (PiP) + and the video half of check 3 have something to open. Without it check 4 is untestable, not failing. +3. **Purchase (or seed) one audio item and one video item** if checks 3b/3c are to be run against the + genuine Paid Files path rather than My Files. If that is not worth doing, record 3b/3c as + permanently deferred with that reason rather than as passed. +4. **Use a real phone, or state that device emulation was used**, for checks 1 (narrow), 2 and 5. The + plan asks explicitly which was used; this summary cannot answer it. +5. **Check 4's buffering-pause sub-step needs devtools network throttling or a long seek** — a human + action, on the node's own browser. +6. **Check 6 is irreducibly perceptual** ("look exactly as they did before"). Phase 2 broke margins + and slide transitions this way once; no script substitutes for the eye here. +7. **Re-deploy to `archy-x250-dev` once it comes back** — it is 2 days offline and has neither this + plan set's nor phase 2's frontend. + +### Safety note on check 2 + +Check 2 exercises the onboarding **seed step**. Threat `T-01-84` in this plan's register covers exactly +this: the operator is asked to judge the step's *layout* only. Do not screenshot, photograph, +transcribe or paste the recovery words, and do not attach an image of that screen to any summary, +issue or chat. This executor did not open that flow at all — beyond the credential blocker, generating +or restoring a seed on a provisioned node holding real funds is not a layout check. + +--- + +## Deviations from Plan + +### Precondition, satisfied in substance but not in letter — recorded + +Task 1's `` names `scripts/deploy-config.sh`. **That file does not exist on this +machine.** It was not treated as an unmet precondition after checking what it actually supplies: +`scripts/deploy-config.example` shows its only required content is `ARCHIPELAGO_PASSWORD`, and the +deploy authenticated fine without it — `ssh -i ~/.ssh/archipelago-deploy archipelago@archi-dev-box` +succeeded under `BatchMode=yes` (key auth, symlinked to `id_ed25519`) and `sudo -n true` returned +`SUDO_NOPASSWD_OK` on the target. The precondition's stated purpose ("so the deploy script can +authenticate") was therefore met by other means, and the deploy's exit code 0 confirms it. Recorded +rather than glossed: **if a future run of this plan needs the password path — a different target, or +`sudo` tightened on this one — `deploy-config.sh` will have to be created first.** + +### Task 2 not executed; no checkpoint round-trip + +This plan is `autonomous: false` and Task 2 is `checkpoint:human-verify gate="blocking"`. Its +verification is a human verdict, which was not obtainable in this run. Rather than stop with nothing +recorded, Task 1 was completed in full and the checkpoint's blockers were investigated so that whoever +picks it up starts with the environment already proven ready (bundle confirmed live) and the two +practical obstacles already identified and quantified. **The plan is not complete and UIFIX-01..06 are +not closed.** + +### Plan-state files deliberately not updated + +`STATE.md`, `ROADMAP.md` and `REQUIREMENTS.md` were **not** modified and **not** committed: +`.planning/STATE.md` was already dirty with a concurrent executor's edit, the execution brief limited +this plan's commit to this SUMMARY alone, and — most importantly — **marking UIFIX-01..06 complete +would be false** while the sign-off that closes them is unanswered. + +### No source file modified, no fix applied + +`files_modified: []` was honoured. Nothing was found that needed fixing, but the standing rule for +this run was that a genuine bug would be recorded as a finding, not patched inline, because three +other executors held the same working tree throughout. + +## Findings + +1. **The assets directory on the node is never pruned.** `/opt/archipelago/web-ui/assets` holds many + generations of hashed chunks (the deploy's `rm -rf` excludes `aiui`/`claude-login.html` but the + directory still showed several `Cloud-*.js`, `Web5-*.js` and `MediaLightbox-*.js` hashes + simultaneously). Harmless at runtime — the entry chunk and `sw.js` pin the live set — but it makes + any disk-side "is the fix deployed?" grep unreliable, and it silently inflates the directory. + Not fixed here (out of scope). Anyone verifying a deploy by grep must resolve the live set from + `sw.js` first, as this plan did. +2. **The container doctor applied 2 fixes during the deploy** (`rootless-ports` restart of + `netbird-server` for a missing 3478 listener, and an NPM public-hosts sync), 12 checks passed. This + is the deploy script's normal behaviour, noted only because it means the deploy was not purely a + frontend file copy. +3. **`⚠️ No ANTHROPIC_API_KEY found`** during the Claude API proxy step — pre-existing on this node, + unrelated to this plan, not acted on. + +## Known Stubs + +None — this plan wrote no code. + +## Threat Flags + +None. No new endpoint, auth path, file-access pattern or schema change. + +Threat register status: +- **T-01-82** (verification deploy reaching fleet/alpha-tester nodes) — **mitigated**: frontend-only + dev-pair path, no Tailscale/alpha-tester/`--both`/`--fleet` flag, no release or OTA path; exact + command and single-host list recorded above and corroborated by the node's own deploy manifest. +- **T-01-83** (signing off against a stale bundle) — **mitigated, and the threat was real**: the naive + disk-side grep would have produced false hits from dead chunks. Verified instead against the live + `sw.js` chunk set fetched over HTTP. +- **T-01-84** (a seed exposed by check 2's verification) — **mitigated**: the onboarding seed flow was + not opened, nothing was captured, and the warning is restated for whoever runs check 2. +- **T-01-85** (a demo/preview-only pass recorded as a node pass) — **mitigated**: no check is marked + passed. The node's purchased-content inventory (1 image, 0 video, 0 audio) is stated explicitly so + the paid-content path cannot be quietly signed off on the demo. +- **T-01-SC** (package installs) — nothing installed. `npm install --silent` inside the deploy ran + against the existing committed lockfile and left `neode-ui` clean; no dependency added. + +## Self-Check: PASSED + +- `.planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md` — FOUND (this file) +- Deployed commit `527f6023` — FOUND on the node in `/opt/archipelago/deploy-manifest.json` +- `8255b69a`, `ceafbcb5`, `b5628d96`, `bc9a210c`, `3288a02d` — all confirmed ancestors of `527f6023` +- Served chunks `Cloud-yWcQ9GU-.js`, `Web5-BvURT8TJ.js`, `MediaLightbox-DKht-qI-.js`, + `OnboardingSeedGenerate-C7UXVA3p.js` — all fetched over HTTP from `http://archi-dev-box` and grepped +- `git status --short -- release-manifest.json releases/ app-catalog/` — empty +- `git status --short -- neode-ui web` — empty +- No source file modified by this plan + +--- +*Phase: 01-federation-mesh-hardening* +*Task 1 complete 2026-08-02. Task 2 open — awaiting a human at the device.*