docs(02-07): record live-testing follow-up round (overlay, theme, key fallback, CLI path)
This commit is contained in:
parent
faf4a75ddf
commit
5b37426fab
@ -238,3 +238,172 @@ None required for this plan's own scope — the AIUI-side commit is now upstream
|
||||
---
|
||||
*Phase: 02-ui-performance*
|
||||
*Completed: 2026-07-30*
|
||||
|
||||
## Addendum: Live-Testing Follow-Up Round (2026-07-30, post-approval)
|
||||
|
||||
After Task 3's checkpoint was approved, the user live-tested the embedded Chat/AIUI panel
|
||||
against the restored `:8100` mock session (iframe pointed at the local AIUI dev server,
|
||||
`http://100.69.68.39:5173`, running `feat/d14-embed-defaults`) and reported four issues.
|
||||
Each was root-caused rather than patched over. This addendum records the fixes; no new plan
|
||||
was created per the coordinator's direction.
|
||||
|
||||
### 1. Loading overlay never dismissed, blocking the interface
|
||||
|
||||
**Root cause (AIUI, `services/archyBridge.ts`):** `archyBridge.init()` used
|
||||
`window.location.origin` — this **iframe's own** origin — as the postMessage target origin
|
||||
for messages sent **to** the parent, and as the validation origin for messages received
|
||||
**from** the parent. Both are wrong: they should be the **parent's** origin. This worked by
|
||||
coincidence only when AIUI is served same-origin as its host (production's `/aiui/` proxy)
|
||||
and silently broke the entire bridge — including the initial `'ready'` message — the moment
|
||||
AIUI runs on a different origin than its embedding page (any dev setup with a separate AIUI
|
||||
dev server, exactly this test).
|
||||
|
||||
**Fix:** `archyBridge.ts` now derives the parent's real origin from `document.referrer`
|
||||
(the standard, cross-origin-safe way an iframed document learns its embedding parent's URL),
|
||||
falling back to `window.location.origin` only if `document.referrer` is unavailable.
|
||||
|
||||
**Defense in depth (archy, `Chat.vue`):** even with the root cause fixed, the loading overlay
|
||||
must never be able to wedge the UI regardless of AIUI/backend state. Two changes: the overlay
|
||||
now has `pointer-events: none` (it has no interactive content, so it should never have blocked
|
||||
clicks reaching the iframe underneath), and a bounded 8s timeout unconditionally dismisses it
|
||||
if `'ready'` never arrives — the timeout does not fabricate a successful connection; the
|
||||
connected indicator still reflects reality. Covered by two new tests in
|
||||
`chatAiuiEmbed.test.ts` (fires at exactly 8s, does not fire prematurely).
|
||||
|
||||
### 2. Background rendered white, then flat black (not the branded look)
|
||||
|
||||
**Root causes (AIUI):** three compounding issues, found via source inspection:
|
||||
- `useTheme.ts`'s `initTheme()` decides light/dark from `localStorage` or the OS's
|
||||
`prefers-color-scheme`, with zero awareness of being embedded — an embedding browser/OS
|
||||
with no dark preference (common in headless/automated or freshly-provisioned contexts)
|
||||
landed on `'light'`.
|
||||
- `useArchy.ts`'s `onThemeUpdate` callback applied Archy's reported accent color but silently
|
||||
**ignored** the `mode` field Archy always sends as `'dark'` — the theme-sync loop was
|
||||
incomplete.
|
||||
- `main.css`'s `body` had **no explicit `background-color` at all** — so `ChatPage.vue`'s
|
||||
embedded `background: transparent` fell through to the browser's white UA default, not to
|
||||
anything dark.
|
||||
- **First-pass fix** (dark bg only): forced `setTheme('dark')` in `App.vue`'s `onMounted`
|
||||
when embedded (before any handshake completes — must not depend on the postMessage round
|
||||
trip), wired `useArchy.ts`'s theme callback to apply `theme.mode`, and gave `body` an
|
||||
explicit `#0a0a0a` (`html.light body` → `#faf9f6`) background. This fixed "white" but
|
||||
produced a flat black canvas.
|
||||
- **Second-pass fix** (live user follow-up: "black is fine while loading, but the real
|
||||
background still never shows"): `ChatPage.vue`'s embedded branch was deliberately opting
|
||||
out of the same background-image treatment (`bg-intro-3.jpg` cover image) the standalone
|
||||
dark-mode app uses, substituting flat `transparent` instead. Reordered the style/overlay
|
||||
conditionals so `isDark` takes priority over `isEmbedded` — the embed now renders the
|
||||
**exact same** branded background image + readability overlay as standalone, with the
|
||||
`#0a0a0a` body color serving only as the natural progressive-load fallback before the image
|
||||
paints, matching the user's explicit ask.
|
||||
|
||||
### 3. New feature: auto-open Settings when there's no working AI credential
|
||||
|
||||
**Design constraint:** must not misfire for the documented production path, where a
|
||||
node-side proxy (not a personal key) is expected to just work — see the API key research
|
||||
below.
|
||||
|
||||
**Implementation (AIUI, `useAI.ts` + `ChatWindow.vue`):** a new one-shot `needsApiKey` signal,
|
||||
set only on a **narrow** set of failure signatures (401/403, "api key"/"unauthorized" text, or
|
||||
a proxy-unreachable failure) from a `sendMessage`/`regenerateLastResponse`/`editAndResend`
|
||||
attempt using the `claude`/`openrouter` providers (never `mock`) — deliberately excluding
|
||||
generic/transient errors (rate limits, momentary network blips) so Settings doesn't pop up for
|
||||
a problem Settings can't fix. `ChatWindow.vue` watches the signal and opens the existing
|
||||
`SettingsModal`, resetting the signal immediately after (a pulse, not sticky state, so a later
|
||||
retry that fails the same way can re-trigger it).
|
||||
|
||||
### 4. Chat CLI fallback crashed with `ENOENT`
|
||||
|
||||
**Root cause (AIUI, `server/claude-proxy.ts`):** the local dev proxy's CLI fallback (used when
|
||||
no `ANTHROPIC_API_KEY`/`ANTHROPIC_TOKEN` is configured) spawned a **hardcoded**
|
||||
`~/.local/bin/claude` path — broke with `spawn ENOENT` on this machine, where the `claude` CLI
|
||||
actually lives under the active `nvm` Node install's `bin/` directory. The user unblocked
|
||||
themselves with a symlink; that symlink is version-pinned and brittle, so a proper fix was
|
||||
still needed.
|
||||
|
||||
**Fix:** `resolveClaudeBin()` now tries, in order: an optional `CLAUDE_BIN` env override →
|
||||
`command -v claude` (a real `PATH` lookup, the same way a user would resolve it themselves) →
|
||||
the historical hardcoded path (for anyone relying on it) → the bare command name (letting
|
||||
`spawn()` itself attempt a `PATH` search at process-start time as a last resort). The
|
||||
`ENOENT` error handler now names three concrete fixes (install the CLI, put it on `PATH`, or
|
||||
set `ANTHROPIC_API_KEY`/`ANTHROPIC_TOKEN`) instead of a bare `Spawn error: ...` message.
|
||||
|
||||
**Verified live:** restarted the local `claude-api-proxy` (port 3141) after the fix; confirmed
|
||||
a full send → spawn → response round trip both directly against the proxy and through Vite's
|
||||
`/api/claude` proxy path (the exact path the embedded iframe uses) — response: `"pong"` to a
|
||||
scripted prompt, streamed via SSE as expected.
|
||||
|
||||
### API key provisioning research (user request: "load our key on my nodes, never in the repo/ISO")
|
||||
|
||||
Documented how AIUI resolves an AI provider credential today, and audited archi-dev-box
|
||||
without ever printing a key value:
|
||||
|
||||
- **Client-side (browser):** `useSettingsStore().settings.claudeApiKey`, plain-text in
|
||||
`localStorage['aiui-settings']` — **flagging, not fixing:** this is a pre-existing violation
|
||||
of AIUI's own `CLAUDE.md` invariant ("API keys ... never in localStorage"), out of this
|
||||
round's scope. A separate encrypted IndexedDB vault also exists but is unreachable in the
|
||||
embedded-in-Archy context specifically, since Archy's embed deliberately skips the
|
||||
passphrase prompt that vault depends on (`App.vue`: `!archy.isEmbedded.value` guard).
|
||||
- **Server-side proxy — already node-local on archi-dev-box, confirmed present, nothing to
|
||||
provision:** `systemctl cat claude-api-proxy.service` shows a systemd unit
|
||||
(`/etc/systemd/system/claude-api-proxy.service`) running `/opt/archipelago/claude-api-proxy.py`,
|
||||
loading its credential via `EnvironmentFile=/var/lib/archipelago/secrets/claude-api-proxy.env`
|
||||
— confirmed present, mode `0600`, owned `archipelago:archipelago` (file existence/permissions
|
||||
checked via SSH; **no key value was ever printed, logged, or committed**). Listens on
|
||||
`127.0.0.1:3142`; nginx's `/aiui/api/claude/` location proxies to it. **This already
|
||||
satisfies "node-local, never in the repo/ISO" for archi-dev-box's production-style `/aiui/`
|
||||
path — no new engineering needed there.**
|
||||
- **Finding:** this live configuration has evolved past what `scripts/setup-aiui-server.sh` (in
|
||||
this `archy` repo, cited in `02-AIUI-D14.md`'s Deployment Impact section) documents — that
|
||||
script describes patching nginx to proxy directly to `api.anthropic.com` with a
|
||||
header-injected key; the actual deployed config instead proxies to this separate
|
||||
`claude-api-proxy.service`. Flagging the doc/reality drift, not fixing the script (out of
|
||||
scope for this round).
|
||||
- **Repo-local dev workflow (`pnpm dev`):** `packages/app/server/claude-proxy.ts` reads
|
||||
`ANTHROPIC_API_KEY`/`ANTHROPIC_TOKEN` from a git-ignored `.env.local` (confirmed excluded by
|
||||
AIUI's `.gitignore`; loader checks both the monorepo root and `packages/app/`) — already
|
||||
satisfies "never in the repo" by construction; falls back to the local `claude` CLI when
|
||||
absent (fixed above).
|
||||
- **What's actually missing is provisioning, not a new mechanism** — both known deployment
|
||||
shapes already have a working, repo/ISO-clean, node-local secret path:
|
||||
- **archi-dev-box:** already provisioned; nothing for 02-08 to do for the Claude-key path
|
||||
specifically. 02-08's actual remaining job is unchanged from the main summary above
|
||||
(rebuild the AIUI image from `development`, redeploy) and does not touch this secret at
|
||||
all (it lives at the nginx/systemd layer, independent of the AIUI container image).
|
||||
- **Framework PT** (the user's other personal machine): needs a one-time, manual,
|
||||
machine-local step only the user can do — create a git-ignored `.env.local` (repo root or
|
||||
`packages/app/`) with their own `ANTHROPIC_API_KEY`, or run `claude setup-token` for the
|
||||
OAuth/Max option. Not something I can perform without access to that machine or their
|
||||
credential.
|
||||
- Building a new orchestrator-level `generated_secrets` manifest entry for AIUI (the
|
||||
`container::secrets`/`core/` pattern `CLAUDE.md` documents for other apps) was
|
||||
**deliberately not pursued** — it would duplicate a mechanism that already works, and
|
||||
touching the orchestrator is out of this phase's own D-12 constraint ("NOTHING touching
|
||||
the orchestrator"). If a future phase wants to formalize archi-dev-box's manual
|
||||
`claude-api-proxy.service` setup into the standard manifest/secrets pattern, that's a
|
||||
distinct, larger piece of work belonging to its own plan.
|
||||
|
||||
### Commits (this addendum)
|
||||
|
||||
- **archy:** `faf4a75d` — `fix(02-07): loading overlay can never wedge the Chat/AIUI UI permanently`
|
||||
(`neode-ui/src/views/Chat.vue`, `neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts`)
|
||||
- **AIUI** (`/home/archipelago/Projects/AIUI`, branch `development`, pushed upstream): `6e8b96d`
|
||||
— `fix(app): embed round-trip fixes — origin, dark bg, key fallback, CLI path`
|
||||
(`services/archyBridge.ts`, `App.vue`, `composables/useArchy.ts`, `styles/main.css`,
|
||||
`pages/ChatPage.vue`, `composables/useAI.ts`, `components/chat/ChatWindow.vue`,
|
||||
`server/claude-proxy.ts`)
|
||||
|
||||
### Verification
|
||||
|
||||
- archy: `npm run test` — 95 files / 774 tests pass (774 = the prior 772 + 2 new timeout
|
||||
tests); `npm run type-check` clean; `npm run build` clean.
|
||||
- AIUI: `vue-tsc --noEmit` clean; `vitest run` — 332/335 pass (3 pre-existing, unrelated
|
||||
failures — song-extraction count mismatches and a web-search system-prompt assertion —
|
||||
confirmed present identically before this round's changes, not introduced by them);
|
||||
production build (`vite build`) clean, with `chatExpanded`/`mobileChat` and the CLI-fallback
|
||||
logic present in the built assets.
|
||||
- Live: full send → spawn → response round trip confirmed via curl against both the proxy
|
||||
directly and the `/api/claude` path the embedded iframe actually uses.
|
||||
- Visual confirmation of the overlay/background/settings-modal fixes in an actual browser is
|
||||
left to the user (this environment has no browser to drive) — the underlying root causes
|
||||
were fixed with verified reasoning and, where checkable via HTTP, confirmed live.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user