AIUI is now cloned at /home/archipelago/Projects/AIUI
(git.tx1138.com/lfg2025/AIUI, development branch — 17 commits ahead
of main with zero unique main commits). Replaces the exhausted-search
conclusion with the real embed-parameter contract read from source
and records the D-14a/D-14b implementation: chatCollapsed's initial
state now honors a new ?chatExpanded param (chat.ts), and ChatPage.vue
re-asserts mobileTab='chat' on mount when ?mobileChat is present,
guarding against carry-over from AIUI's own module-singleton content-
panel state on an internal remount. Both are static, unconditional
query params — no runtime-varying URL input.
AIUI-side commit lives on feat/d14-embed-defaults (900c0b9) in the
local clone only — anonymous push to origin returned 403 Forbidden.
Needs a maintainer with push rights to land it before any deployed
AIUI build can honor these two parameters.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
AIUI D-14 Embed Contract
Written: 2026-07-30 (Phase 02 Plan 07, Task 1) Amended: 2026-07-30 (Phase 02 Plan 07, Task 2 resume) — the orchestrator supplied the missing fact this document's original Task 1 pass could not obtain from within this environment: AIUI's source repository location. This amendment replaces the "search exhausted" conclusion below with the real contract read directly from source, and records the D-14a/D-14b implementation actually shipped.
Source Location
AIUI's source is now located and was cloned for this plan:
- Repository:
https://git.tx1138.com/lfg2025/AIUI(anonymousgit ls-remotesucceeds; clone succeeded read-only, push is403 Forbiddenanonymously — see Deployment Impact). - Cloned to:
/home/archipelago/Projects/AIUI— the exact sibling pathneode-ui/package.json'sdev:mockscript andscripts/setup-aiui-server.shboth already expected (<parent-of-archy>/AIUI), so no changes to either script were needed once the clone existed. - Branch used:
development, not the repository's defaultmain. Verified viagit log main..origin/developmentvsgit log origin/development..main:developmentis 17 commits ahead ofmain(including a run offix(app):security-hardening commits — origin validation, rate limiting, CSP, SSRF protection, dev-server auth — and the feature/content work this session's grep relies on) with zero commits unique tomain.mainis simply stale here;developmentis the actual working branch. (AIUI's ownCLAUDE.mddocuments the intended name asdev, but the real remote branch is nameddevelopment— a doc/reality mismatch noted for AIUI's own maintainers, out of scope to fix from this repository.) - ThinkPad build server: confirmed reachable this session at
100.69.68.39(Tailscale), per the orchestrator-supplied fact. Not needed once the git clone succeeded — recorded here for completeness since Task 1's original search treated it as the fallback path. - Hosting note (context only, not acted on): the user noted AIUI hosting may later be ported to "our main vps2." No action taken on this in this plan; recorded for whoever picks up AIUI's deployment next.
Original Task 1 search (superseded, kept for history)
The initial pass searched the sibling path (<parent-of-archy>/AIUI — not present at that
time), a broader filesystem sweep (found only prebuilt dist/ output under
/opt/archipelago/web-ui*/aiui, this repo's apps/aiui/demo/aiui, and installer-ISO
copies — never source), and the ThinkPad at 192.168.1.116 (ping/ssh both failed at that
time — host unreachable from this environment on that address/path). That search was
genuinely exhausted with the facts available at the time; the blocker was resolved
externally (see Amended note above), not by anything different this pass did on the same
inputs.
Embed Parameter Contract
neode-ui/src/views/Chat.vue's aiuiUrl computed (lines ~74-82) sends, unchanged by this
plan:
embedded=true— always present.hideClose=true— always present.&mockArchy=1&seed=1— only whenIS_DEMO(build-time env var, stable for the life of the bundle).
Read from AIUI source (development branch, commit 9176324 before this plan's changes):
embedded—packages/app/src/main.ts:34-35. Read once, before router init, vianew URLSearchParams(window.location.search).has('embedded')(presence-only check) and stored onwindow.__AIUI_EMBEDDED__so it survives SPA navigation. Matches the deployed build's minified behavior exactly (independently confirmed by the diligence pass on the built bundle before source access — see history above).mockArchy—packages/app/src/composables/useArchy.ts:83-96. Read viaimport.meta.env.VITE_MOCK_ARCHY === 'true' || new URLSearchParams(...).has('mockArchy'), but the mock-data branch is only enteredif (useMock && !embedded)— i.e. mutually exclusive withembeddedin this codebase today. This confirms the inconsistency flagged during the original diligence pass:Chat.vue's demo mode sendsembedded=trueandmockArchy=1together, but AIUI only honorsmockArchywhen NOT embedded. Pre-existing, unrelated to D-14, not fixed here (out of this plan's file scope — flagged for whoever owns the demo-mode AIUI experience next).hideClose— confirmed absent from AIUI's source entirely (grep -rn "hideClose" packages/returns nothing). AIUI does not read or honor this parameter at all today. Not part of D-14; not investigated further.seed— confirmed absent from AIUI's source entirely (noURLSearchParamsread anywhere references it).Chat.vue's&seed=1in demo mode currently does nothing on the AIUI side. Pre-existing, unrelated to D-14, not fixed here.
D-14a
Default: the AIUI chat should start EXPANDED rather than requiring the user to expand it.
- Root cause, found in source:
packages/app/src/stores/chat.ts:132(before this plan's change):const chatCollapsed = ref(localStorage.getItem('aiui-chat-collapsed') !== 'false')— defaults to collapsed (true) on any fresh/unsetlocalStorage, i.e. exactly the behavior CONTEXT.md describes ("requiring the user to expand it").chatCollapsedgatesChatWindow.vue'sPromptIndex(collapsed quick-pick view) vs. the full virtualized message list (expanded view) — confirmed atChatWindow.vue:35/ChatWindow.vue:249. Once a user manually expands, the choice is written back tolocalStorage(chat.ts:196-198) and persists — so this bug only bites on a genuinely freshlocalStorage, matching the reported "starts collapsed" default. - Status: implemented, on the AIUI side, gated by a new query parameter —
?chatExpanded. Not something the deployed build already read (see original diligence pass); a source change was required and made. - Which side implements it: AIUI reads the flag; neode-ui sends it. Implementation:
chatCollapsed's initial ref now checksnew URLSearchParams(window.location.search).has('chatExpanded')first, defaulting tofalse(expanded) when present, before falling back to the existinglocalStoragecheck. Never written back tolocalStorage— it is a one-time initial-state override for this session only, so a later manual collapse/expand while embedded still persists exactly as before, and the standalone (non-embedded) app's own default is completely untouched (it never sends?chatExpanded, so itslocalStorage-based behavior is unchanged). - Why a new parameter rather than reusing the existing
embeddedflag:embeddedis already overloaded (transparent background, mock-Archy gating, passphrase-prompt skip); tyingchatCollapsed's default to it directly would mean every embedded session forces expanded with no way to decouple the two concerns later. A dedicated flag keeps the contract self-documenting and matches this plan's acceptance criteria (the flag name must be discoverable in the built neode-ui bundle).
D-14b
Default: on a mobile viewport, AIUI should open on its CHAT view rather than its context view.
- Root cause, found in source:
packages/app/src/pages/ChatPage.vue:469already initializesmobileTabto'chat'(const mobileTab = ref<'chat' | 'content' | 'context'>('chat')) — so a truly fresh mount already defaults correctly. The reported bug is a carry-over, not a wrong default:panelOpen,selectedFilm/selectedBook/etc., andhasDetailOpen(computed from those) are module-scope singleton refs inuseContentPanel.ts(ref()calls at module top level, not inside the composable function), shared across every mount ofChatPage.vuewithin the same AIUI SPA session. If a user previously opened a detail item (makinghasDetailOpentrue, whichChatPage.vue's ownwatch(hasDetailOpen, ...)at line ~507 flipsmobileTabto'context'for), andChatPage.vuelater remounts within the same iframe session (e.g. an internal AIUI navigation to/guideand back) without those refs resetting,hasDetailOpenis stilltrueon the new mount and the "opens on chat" default never gets a chance to hold — it's immediately superseded by state carried over from before. Note this is now less likely to surface at all going forward, since Task 2's URL-stability fix (see D-14 URL stability, below) keeps the iframe itself from reloading on an Archipelago tab switch — but it can still happen from AIUI's own internal navigation, independent of Archipelago. - Status: implemented, on the AIUI side, gated by a new query parameter —
?mobileChat. - Which side implements it: AIUI reads the flag; neode-ui sends it. Implementation: a new
onMountedhook inChatPage.vuere-assertsmobileTab.value = 'chat'when?mobileChatis present andisMobile.valueis true, at mount time only. This does not touch thepanelOpen/hasDetailOpenwatchers that drive normal tab-switching in response to actual user taps after the initial paint — those are unchanged, so a user can still navigate to the content/context tabs exactly as before; only the initial tab on a (re)mount is affected. - Why AIUI decides from its own viewport rather than neode-ui passing a computed
boolean: per Task 2's URL-stability constraint,
aiuiUrlcannot embed a reactive viewport read (see below).?mobileChatis sent unconditionally and is unconditionally interpreted by AIUI against its ownisMobilecomputed (windowWidth.value < 1024, read from the iframe's ownwindow.innerWidth) — this is the "let AIUI decide from its own viewport" option named in the plan's Task 2 action, chosen over resolving neode-ui's outer viewport once at setup time, since AIUI's iframe has its own layout breakpoint and is the side that actually owns the mobile/desktop layout switch.
D-14 URL Stability (how neode-ui sends both flags)
Both ?chatExpanded and ?mobileChat are appended unconditionally in Chat.vue's aiuiUrl
computed — static strings, not derived from any runtime-varying input (no viewport read, no
connection state, no timestamp). This preserves the property Task 2 exists to protect: the
iframe src stays byte-identical across re-renders, viewport resizes, and
deactivate/reactivate cycles, so the AIUI panel never reloads on an Archipelago tab switch.
Deployment Impact
apps/aiui/manifest.yml still declares a prebuilt container image
(localhost/archipelago-aiui:latest, pull_policy: always) — this plan's AIUI-side change
does not alter that; shipping it still requires:
- Merging the AIUI-side commit. Changes are committed locally in the cloned checkout at
/home/archipelago/Projects/AIUIon branchfeat/d14-embed-defaults(based ondevelopment), commit900c0b9. Anonymous push tooriginfailed with403 Forbidden(git push origin feat/d14-embed-defaults) — this account has read-only access togit.tx1138.com/lfg2025/AIUI. The branch and commit exist only in this local checkout until someone with write access pushes it (or opens a PR from a fork/mirror they do have write access to) and merges it intodevelopment. - Rebuilding AIUI's production bundle from the merged source:
cd AIUI/packages/app && VITE_BASE_PATH=/aiui/ npx vite build(perscripts/setup-aiui-server.sh's documented prerequisite). - Rebuilding the
localhost/archipelago-aiui:latestcontainer image from that build output (a local/on-node image tag, not a registry pull). - Redeploying/restarting the
aiuiapp container on the target node(s) sopull_policy: alwayspicks up the new local image.
Per D-15, deployment is dev pair only this phase, no OTA — plan 02-08 owns the deploy
step under that constraint, once the AIUI-side commit is actually mergeable (i.e., once a
maintainer with push rights lands feat/d14-embed-defaults on development and it's
rebuilt/redeployed). Until that push/merge happens, the deployed AIUI build on any node
(including archi-dev-box) will NOT carry these two parameters — neode-ui's side is fully
forward-compatible with that (both flags are inert query-string additions the current
deployed AIUI build simply ignores, per the confirmed absence of hideClose-style unknown
params breaking anything), but the two visual defaults (expanded start, mobile-chat-first
start) will not be observable against any currently-deployed AIUI build until the AIUI
commit ships.