Files
archy/.planning/phases/01-federation-mesh-hardening/01-18-PLAN.md
T

228 lines
15 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: 01-federation-mesh-hardening
plan: 18
type: execute
wave: 9
depends_on: ["01-12", "01-13", "01-14", "01-15", "01-17"]
files_modified: []
autonomous: false
requirements: [UIFIX-01, UIFIX-02, UIFIX-03, UIFIX-04, UIFIX-05, UIFIX-06]
gap_closure: true
must_haves:
truths:
- "All six UI fixes are exercised on real node hardware, not only on the local preview, because two of them are about how the app behaves on a device rather than in a viewport"
- "The connected-nodes list scrolls at a sibling-matched height on the node's own screen and at a phone width"
- "The onboarding tickbox is discoverable on a genuinely short viewport on the node"
- "A purchased picture opens in the app's lightbox on the node, with the wait visible"
- "Picture-in-picture closes the lightbox with a handoff and survives a real tab change and a real buffering pause on the node"
- "The FIPS/Tor pills are readable at phone width on the node"
- "Every surface that was not supposed to change is confirmed unchanged on the node — the standing visual-invisibility rule is verified, not assumed"
prohibitions:
- statement: "This phase's frontend MUST NOT be deployed beyond the dev pair — no OTA, no release, no fleet node, no alpha-tester path; a verification step is never a reason to widen a deploy"
category: safety
- statement: "Sign-off MUST NOT be given on local-preview evidence alone for any check that names the node — the local preview and a real device disagree exactly where these fixes matter, which is why phase 2's on-device pass found four issues the preview did not"
category: transparency
artifacts: []
key_links: []
---
<objective>
Put all six UI fixes in front of a human, on the node, once.
Purpose: each of plans 01-12 through 01-17 verifies itself with tests and a local-preview observation,
which is the right granularity for an autonomous plan but is not sufficient evidence for a
user-reported blocker. Two of these fixes — picture-in-picture surviving a tab change, and the pills
at phone width — are about device behaviour that a desktop preview cannot reproduce. Rather than
interrupting five plans with five checkpoints, they are gathered here so the operator is asked once,
after the code is on archi-dev-box. This mirrors how plan 01-10 consolidates the federation and
Lightning sign-offs.
Output: a recorded sign-off, or a numbered issue list that becomes the input to a gap-closure pass.
</objective>
<execution_context>
@$HOME/.claude/gsd-core/workflows/execute-plan.md
@$HOME/.claude/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/STATE.md
@.planning/phases/01-federation-mesh-hardening/01-12-SUMMARY.md
@.planning/phases/01-federation-mesh-hardening/01-13-SUMMARY.md
@.planning/phases/01-federation-mesh-hardening/01-14-SUMMARY.md
@.planning/phases/01-federation-mesh-hardening/01-15-SUMMARY.md
@.planning/phases/01-federation-mesh-hardening/01-17-SUMMARY.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Put the six fixes on archi-dev-box, frontend only, dev pair only</name>
<files>none — this task builds, deploys and verifies delivery; it modifies no file in the repository</files>
<precondition>archi-dev-box resolves and answers over HTTP from this machine, and `scripts/deploy-config.sh` exists (it is gitignored; `scripts/deploy-config.example` documents it) so the deploy script can authenticate</precondition>
<read_first>
- `scripts/deploy-to-target.sh` lines 1-30 — the usage block. `--frontend-only` skips the Rust
build and container rebuilds; `--live` targets the default host; `--both` fans out to additional
hosts; `--tailscale` reaches the alpha-tester nodes and must not be used here.
- `.planning/phases/02-ui-performance/02-08-SUMMARY.md` — the exact command phase 2 used for the
same kind of dev-pair frontend deploy, and its record of archy-x250-dev being offline. Reuse the
command shape rather than inventing one.
- The five plan SUMMARYs listed in `<context>` — specifically each one's recorded local-preview
observations, so you know what the node is expected to reproduce.
</read_first>
<action>
Build the frontend and deploy it to archi-dev-box with the dev-pair frontend-only path. Record the
exact command in the SUMMARY.
Do not use the Tailscale or alpha-tester paths, do not cut a release, do not touch the OTA manifest,
and do not deploy to any fleet node. Note that this plan set's FED-07 work is backend and is
verified separately by plan 01-16 — this deploy is frontend only.
Check whether archy-x250-dev is reachable. It has been offline since phase 2. If it is still
offline, record that plainly as a gap rather than waiting for it or pretending the pair was
covered; single-node verification on archi-dev-box with the second-node gap recorded honestly is
the expected pattern for this phase.
Then confirm the node is actually serving this build before handing over to the checkpoint — fetch
the served bundle from archi-dev-box and grep it for strings this plan set introduced (the
onboarding cue copy, the picture-in-picture handoff class, and the paid-item viewer). Grep the
served asset, not the local `web/dist` copy: per CLAUDE.md the frontend build can silently no-op,
and a checkpoint run against a stale bundle is worse than no checkpoint.
</action>
<verify>
<automated>curl -fsS --max-time 20 "${ARCHY_DEV_URL:?set ARCHY_DEV_URL to archi-dev-box's UI base URL}/" -o /dev/null &amp;&amp; echo served</automated>
</verify>
<acceptance_criteria>
- The SUMMARY records the exact deploy command and the host list it targeted, and that list contains no fleet, alpha-tester or Tailscale host.
- The SUMMARY records the served-bundle grep result for all three introduced strings, naming the URL fetched.
- The SUMMARY records archy-x250-dev's reachability, and if unreachable records it as an explicit gap.
- No release artifact, OTA manifest or catalog was modified — `git status --short -- release-manifest.json releases/ app-catalog/` is empty.
</acceptance_criteria>
<done>archi-dev-box is serving a bundle that provably contains all six fixes, and the second-node gap is recorded.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 2: Six-fix sign-off on archi-dev-box</name>
<what-built>
Six user-reported UI issues, now on archi-dev-box:
- **Connected nodes (UIFIX-02).** On the Web5 tab, the connected-nodes card no longer grows to fit
every node. Its height now comes from the card beside it in the row and the list scrolls inside
that height, with a floor so a short neighbour cannot squash it. Below the two-column breakpoint
nothing changed.
- **Onboarding tickbox (UIFIX-03).** On a screen too short to show the whole seed step, a soft
gradient and a small glass pill reading "One more step below" now appear at the bottom edge of
the scrolling area. Clicking it scrolls the confirmation tickbox into view, and it disappears
once the tickbox is visible. On a tall screen it never appears at all.
- **Paid Files pictures (UIFIX-04).** Purchased pictures and videos now open in the app's own
lightbox instead of a browser tab. Purchased music still goes to the bottom-bar player, and
purchased documents still open the way they did.
- **Loader states (UIFIX-06).** Opening a purchased file now shows a spinner and an "Opening…"
label on its row for as long as the fetch takes, and a failure now shows an error instead of
appearing to do nothing. Every other surface that was flagged as slow was audited and its verdict
recorded.
- **Picture-in-picture (UIFIX-05).** Entering picture-in-picture now closes the lightbox with a
handoff animation, and the video keeps playing. The session survives switching main tabs and
survives buffering pauses; only explicitly stopping it ends it.
- **FIPS/Tor pills (UIFIX-01).** The pills are now pinned by a test so no future cleanup can remove
them, and every site that could not be read at phone width was fixed.
</what-built>
<how-to-verify>
Run all of this against archi-dev-box's own UI, not the local preview. Use a real phone or the
browser's device emulation for the narrow checks, and say which you used.
1. **Connected nodes scroll (UIFIX-02).** Open the Web5 tab on a wide window. Expected: the
connected-nodes card and the card to its right are the same height, and if there are more nodes
than fit, the list scrolls inside the card — the row does not get taller. Switch between the
trusted, observers and requests tabs: expected the card's height does not change. Then narrow the
window to a single column: expected exactly the layout you had before this change.
2. **Onboarding cue (UIFIX-03).** Open the onboarding seed step at a short viewport (a small laptop
height, or device emulation at roughly 1280×620). Expected: a soft fade and a small pill reading
"One more step below" at the bottom of the scrolling area; clicking it brings the tickbox into
view and the cue disappears. Confirm the cue does not tick the box for you and the Continue
button stays disabled until you tick it yourself. Then open the same step at full height:
expected no cue at all and a step that looks exactly as it did before.
3. **Paid Files in the lightbox (UIFIX-04) and the loader (UIFIX-06).** Go to Cloud → Paid Files
and click a purchased picture. Expected: the row shows a spinner and "Opening…" while it loads,
then the picture opens in the app's lightbox — no new browser tab. Click a purchased video:
expected the same, in the lightbox with player controls. Click a purchased music track: expected
the bottom-bar player, not the lightbox. If you can, click one twice quickly: expected one load,
not two.
4. **Picture-in-picture (UIFIX-05).** Open a video in the lightbox and click the
picture-in-picture button. Expected: the lightbox animates away in a way that reads as the video
moving into the small window rather than the lightbox being dismissed, and the video keeps
playing. Now switch between main tabs a few times: expected playback continues. Now cause a
buffering pause — throttle the network in devtools, or seek far ahead: expected it recovers and
the small window stays. Now close the small window explicitly: expected playback stops and
nothing is left behind. Finally open the lightbox again and close it with the close button and
with Escape: expected exactly the close animation it had before.
5. **FIPS/Tor pills at phone width (UIFIX-01).** At a phone width, go to Cloud and look at the peer
cards, then open a peer's files. Expected: wherever a FIPS or Tor pill appears on desktop it
appears here too, fully readable, not clipped and not overlapping anything, including when a peer
name or filename is long. Compare the same screens at desktop width: expected unchanged.
6. **Nothing else moved.** Move through the main tabs and the Cloud sub-tabs. Expected: the page
margins, the slide transitions between tabs, and every existing animation look exactly as they
did before this plan set. Phase 2 broke margins and slide transitions this way once, so this is a
real check, not a formality.
If anything fails, say which numbered step and what you saw — that becomes the gap list rather than
a re-run of the whole plan set.
</how-to-verify>
<resume-signal>Type "approved" to sign off UIFIX-01 through UIFIX-06, or describe the issues by step number.</resume-signal>
</task>
</tasks>
## Planner Assumptions (flagged, unresolved)
- **archy-x250-dev is assumed to still be offline.** Phase 2 checked three times and found it gone.
Task 1 re-checks rather than assuming, and records the gap either way; nothing in this plan blocks on
it.
- **Whether archi-dev-box has purchased content to test step 3 with** is unknown to the planner. If it
has none, say so in the sign-off rather than marking step 3 passed on the demo — a demo-only pass for
a paid-content path is exactly the divergence class this phase exists to remove.
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| operator judgement → sign-off | A human verdict gates whether these six blockers are considered closed |
| deploy host → node | A frontend bundle crosses this boundary onto a live node |
| live node → operator observation | Verification runs against a real node holding real federation trust, real purchases and real funds |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-01-82 | Elevation of Privilege | a verification deploy reaching fleet or alpha-tester nodes | high | mitigate | Task 1 requires the frontend-only dev-pair path, forbids the Tailscale and alpha-tester flags and any release or OTA path, and requires the exact command and host list to be recorded for audit |
| T-01-83 | Repudiation | signing off against a stale bundle the node never actually received | high | mitigate | Task 1 requires grepping the bundle served by the node — not the local build output — for three strings this plan set introduced, before the checkpoint runs |
| T-01-84 | Information Disclosure | a screenshot or recording of the verification exposing a recovery seed from step 2 | high | mitigate | Step 2 exercises the onboarding step's layout only; the operator is not asked to capture or transcribe the words, and nothing in this plan asks for an image of that screen |
| T-01-85 | Repudiation | a demo-only pass on the paid-content path being recorded as a node pass | medium | mitigate | The prohibition forbids local-preview evidence for node-named checks, and the planner assumption requires saying so explicitly if the node has no purchased content |
| T-01-SC | Tampering | npm/pip/cargo installs | high | mitigate | This plan installs nothing and modifies no source file — it builds, deploys and asks. If a fix arising from the checkpoint needs a dependency, it belongs in a gap-closure plan whose research covers the Package Legitimacy Gate first |
</threat_model>
<verification>
The operator's response is the verification. An "approved" response closes UIFIX-01 through UIFIX-06;
any described issue is captured verbatim in the SUMMARY as a gap for `/gsd-plan-phase 1 --gaps`.
</verification>
<success_criteria>
- All six numbered checks were exercised on archi-dev-box, at the widths each one names.
- The operator either approved or produced a numbered issue list.
- The outcome is recorded in the SUMMARY, including which device or emulation was used for the narrow checks.
- The archy-x250-dev gap is recorded rather than glossed over.
</success_criteria>
<output>
Create `.planning/phases/01-federation-mesh-hardening/01-18-SUMMARY.md` when done, recording the
verdict, the deploy command, the served-bundle grep evidence, the device used for narrow checks, and
any issue text verbatim.
Stage by explicit path, commit, and `git push gitea-ai main`.
</output>