Files
waggle-os/docs/ux-refactor/p1b-plan-review-record.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

47 lines
7.4 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.
# P1b Plan — Adversarial Review Record (2026-06-11)
Two workflow rounds over `p1b-auth-gate-plan.md` v1 (6 lenses; round 1's contract lane completed, 5 lanes re-ran in round 2 after a session-limit failure). **24 confirmed / 0 refuted / 16 LOW.** Every confirmed finding was independently verified by a skeptic agent against the live tree before acceptance. All are folded into plan v2; this file records them so they are not re-found, plus the LOW-grade confirmations of plan claims.
## Confirmed (folded into plan v2)
| # | Sev | Lens | Finding → v2 disposition |
|---|-----|------|--------------------------|
| 1 | CRITICAL | contract | `installMcp` is a typed JSON getter whose error body carries the ApprovalModal security envelope (`{requiresApproval, blocked, severity}` on 403/422); throw would kill the flow and the phase4b tests (method-level mocks) are blind → body-envelope migration class + adapter-level pins as hard Stage-A exit criterion |
| 2 | CRITICAL | sse-ws-auth | All five EventSource SSE channels 401-dead in EVERY default run since D1 (bearer-gated /api GETs; EventSource can't send headers; no `?token=` on those routes; only `/ws` has the pattern and `connectWebSocket` is dead code) → plan §1.4, founder ruling requested, P1b scope unchanged |
| 3 | HIGH | deadlock | Gate-never-released hang: `fetchWithTimeout` bounds headers only; connect's `res.json()` reads unbounded → connect watchdog (~15s end-to-end deadline) |
| 4 | HIGH | deadlock | Gate disarms after failed early connect (default Tauri cold-sidecar path) re-opening the ungated burst with no recovery (NetworkError never triggers the 401 leg; focus never fires on an already-focused window) → settled-failure re-arm + ServiceProvider backoff + `waggle:connect-settled` bus consumed by all Stage-B surfaces |
| 5 | HIGH | deadlock | SSE half of the boot race unfixed and v1's "if and only if they attach the token" conditional matched zero methods (noop-guards = session-long dead subscriptions) → §1.4 honest de-scope; subscribe* untouched; acceptance check 1 scoped to fetch layer |
| 6 | HIGH | test-blast | `adapter.eraseData.test.ts`/`adapter.startTrial.test.ts` pin method-level `Erase failed (400): <detail>` messages with **message-first** detail; v1's error-first precedence inverted it → message-first pin + legacy-prefix wrappers in both methods |
| 7 | HIGH | sse-ws-auth | Chat `sendMessage` silent-empty-bubble on stale/missing token; plan's (b)+(c) verified the correct fix; `useChat` catch defects confirmed → no design change; Stage B useChat rework retained |
| 8 | HIGH | tier-ui | Definitive gate-vs-nag table (nav = pure capability gate, do NOT wire tierResolved; TrialExpiredModal + StatusBar badges already safe; UpgradeModal event-driven safe) → §1.2(d) classification |
| 9 | HIGH | tier-ui | useBilling failure renders FREE-as-fact + upgrade CTAs in Settings→Billing with zero error indication; post-Stage-A every failure funnels there → mandatory reshape |
| 10 | HIGH | tier-ui | SECOND tier-as-fact surface missed by v1: SettingsApp General-tab `'{tier} plan'` badge fed by `getSettings` (`:54/157/239-241`) → single-source from resolved billing state |
| 11 | HIGH | d3-fidelity | LoginBriefing got no revalidation wiring while v1's own UI copy promised "retrying" (plus-clause violation on a ruling-named surface) → wired to `useRevalidateOnError`, acceptance asserts recovery |
| 12 | MEDIUM | deadlock | setServerUrl mid-flight: stale connect continuations clobber baseUrl/localStorage/token (fallback can persist DEFAULT_SERVER over the user's new URL) → epoch guard |
| 13 | MEDIUM | deadlock | Straggler 401s chain sequential refreshes past single-flight; refresh failure semantics unpinned (silent token-less retry) → token-versioned retry + throwing `refreshSessionToken` |
| 14 | MEDIUM | deadlock | healthProbe not single-flighted; races useOfflineStatus's exempt /health probes on shared `this.baseUrl` mutation → single-flight + local-var fallback + epoch commit |
| 15 | MEDIUM | deadlock | `uploadFile`/`ingestFile` bypass the chokepoint entirely (raw `fetchWithTimeout`, hand-attached token, unconditional `res.json()`) → routed through the shared core |
| 16 | MEDIUM | test-blast | "~30 adapter test files" wrong by ~4× (actual 7; 2 not behavior-identical) → corrected, eraseData/startTrial carved out |
| 17 | MEDIUM | test-blast | Stage C's expected-fallout class is empty (no silent-empty-on-!ok pins exist); real risk was a mechanical rewrite erasing the startTrial message-surfacing guard → Stage C expectation rewritten |
| 18 | MEDIUM | test-blast | Call-count tripwire: kickoff must stay OUT of adapter module scope/constructor (5 files index `mock.calls[0]`) → zero-network-at-import/construction pin |
| 19 | MEDIUM→LOW | test-blast | 9 component test files render real ServiceProvider over hand-rolled mocks → ServiceProvider existing-members-only constraint |
| 20 | MEDIUM | sse-ws-auth | SSE has zero restart/error recovery even where it could work (`es.close()` in onerror; once-only subscribes) → part of the §1.4 "SSE auth + reconnect" single follow-up |
| 21 | MEDIUM | tier-ui | refreshTier failure path also clobbers trialInfo (StatusBar trial countdown transiently wiped); tierResolved semantics for unrecognized-tier unpinned → both pinned in (d) |
| 22 | MEDIUM→LOW | tier-ui | Nav `getDockForTier` confirmed pure capability gate → do-not-touch advisory + comment |
| 23 | MEDIUM | d3-fidelity | P7 deferral defense factually wrong for its own exemplar (MCPHubApp already connect-gated; gate adds nothing) and plus-clause narrowing was self-granted → two cheap wirings in §1.2(h) + decision-log note §3 |
| 24 | HIGH | contract (r1) | (v1 round) `installMcp` misattribution — superseded by #1's fuller statement; plus `addCustomMcp`/`updateMcpPermissions`/`revokeMcp`/`startMcp`/`stopMcp`/`testMcp` body-envelope class, `useChat` consumer correction, `AdapterHttpError.message` pin, BackupApp-only among direct sites, uninstall false-success fix |
## LOW (verified clean / advisory — no plan change beyond what v2 already carries)
- ensureReady 3-state ambiguity + sync memo assignment + settled-success memo retention → pinned in v2 §1.1(a).
- "Pass-through stays armed by accident of the import graph" → `boot-connect.ts` first-import + zero-network pin close it structurally.
- Setup files clean; no test imports main.tsx/App.tsx; Stage B existing-suite fallout ≈ zero (all coverage net-new).
- No adapter-level raw/envelope pins exist anywhere (confirms blindness diagnosis) → hard exit criterion.
- `subscribeHarvestProgress` degrades silently under 401 (import works, no progress UI) — noted in §1.4.
- `connectWebSocket` dead code, builds `?token=null` pre-token → P7 ledger.
- Trial-expired modal verified already-safe; UpgradeModal verified safe; KvarkNudge doesn't exist (resolves to SettingsApp Enterprise CTAs — covered by the Billing-tab fix).
- useFeatureGate / settings-tier-filter / persona-tier / dock-nudge / onboarding-tier-filter all local-state driven — excluded; stale comment + orphaned file → P7.
- `ComplianceDashboard.tsx:274` verified as THE live-confirmed CockpitApp crash; its `refreshTemplates` valid-empty cache added to §1.2(h).
- Ruling coverage sweep: all four D3 sub-items + five live-confirmed targets covered; six named getters stay on throwing path; deviation-class note → §3.
- Scope-creep audit: nothing cut-worthy; useBilling tagged as D3-4 extension.