326 lines
23 KiB
Markdown
326 lines
23 KiB
Markdown
---
|
|
phase: 02-ui-performance
|
|
plan: 07
|
|
type: execute
|
|
wave: 4
|
|
depends_on: ["02-04"]
|
|
files_modified:
|
|
- .planning/phases/02-ui-performance/02-AIUI-D14.md
|
|
- neode-ui/src/views/Chat.vue
|
|
- neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts
|
|
autonomous: false
|
|
requirements: [PERF-02]
|
|
|
|
user_setup:
|
|
- service: aiui
|
|
why: "D-14's two UX defaults are implemented in AIUI, whose source is a sibling repository not present in this checkout. The neode-ui side can only pass the flags; something must read them."
|
|
dashboard_config:
|
|
- task: "Confirm where the AIUI source checkout lives and make it reachable from the machine executing this phase (project notes record the ThinkPad at .116 as the primary build server; neode-ui's dev:mock script and scripts/setup-aiui-server.sh both expect it at ../../AIUI relative to neode-ui)"
|
|
location: "Developer's own machine / build server"
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Switching away from the Chat tab and back leaves the AIUI panel loaded — the iframe is not re-created and does not reload"
|
|
- "The iframe src is stable for the lifetime of the Chat view instance: no runtime-varying value (viewport width, connection state, timestamp) is part of the URL"
|
|
- "The AIUI chat opens in its expanded state rather than requiring the user to expand it (D-14a)"
|
|
- "On a mobile viewport, AIUI opens on its chat view rather than on its context view (D-14b)"
|
|
- "The mechanism carrying both defaults across the neode-ui to AIUI boundary is recorded in writing, including which side implements which half"
|
|
- "The existing postMessage origin validation still rejects messages from any origin other than the AIUI URL's own"
|
|
- "The connected state established by AIUI's ready message is not reset when the Chat tab is deactivated, since that message is not re-sent on re-entry"
|
|
- statement: "Neither D-14 default regresses AIUI's desktop layout or its non-embedded standalone mode"
|
|
verification: backstop
|
|
prohibitions:
|
|
- "MUST NOT achieve perceived speed by removing behavior or hiding state — no suppressing the refresh indicator, no dropping a fetch the surface needs, no disabling a feature to win the metric"
|
|
- "MUST NOT widen what the embedded AIUI iframe is granted or trusted as a side effect of passing presentation flags — this phase changes how AIUI opens, never what it may reach"
|
|
artifacts:
|
|
- path: ".planning/phases/02-ui-performance/02-AIUI-D14.md"
|
|
provides: "The recorded AIUI source location, its embed-parameter contract, and which side implements each D-14 default"
|
|
- path: "neode-ui/src/views/Chat.vue"
|
|
provides: "Stable embed URL carrying the D-14 presentation flags"
|
|
- path: "neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts"
|
|
provides: "Assertions that the embed URL is stable and carries both flags, and that origin validation is unchanged"
|
|
key_links:
|
|
- from: "neode-ui/src/views/Chat.vue"
|
|
to: "the AIUI application"
|
|
via: "embed flags appended to the iframe URL query string built by the aiuiUrl computed"
|
|
pattern: "aiuiUrl"
|
|
- from: "neode-ui/src/views/Chat.vue"
|
|
to: "vue onActivated / onDeactivated"
|
|
via: "the message listener and ContextBroker follow activation, established in plan 02-04"
|
|
pattern: "onDeactivated"
|
|
---
|
|
|
|
<objective>
|
|
Finish the Chat tab: make the embedded AIUI panel survive tab switches without reloading,
|
|
and land the two AIUI UX defaults CONTEXT.md folded into this phase (D-14).
|
|
|
|
Purpose: PERF-02 and D-14. Chat's per-switch cost is unlike every other tab's — it is not
|
|
an RPC fan-out, it is a full re-creation of an embedded application. Plan 02-04 made the
|
|
Chat view instance survive; this plan makes sure nothing in the URL construction quietly
|
|
undoes that. Adding a query parameter that varies at runtime would change the iframe's
|
|
`src`, force a reload on every re-render, and hand back the entire win — which is exactly
|
|
the risk D-14's two flags introduce, since one of them is about mobile.
|
|
|
|
D-14 is a locked decision and is delivered here in full. It has one genuine external
|
|
dependency: AIUI's source is a sibling repository that RESEARCH.md verified is not present
|
|
in this checkout, so the receiving half of the contract cannot be read from `archy` alone.
|
|
Task 1 resolves that before any code is written, and Task 2 will not start until it has.
|
|
|
|
Output: a recorded neode-ui-to-AIUI embed contract, a stable embed URL carrying both
|
|
defaults, and a Chat tab that keeps its loaded panel across tab switches.
|
|
</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/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/02-ui-performance/02-CONTEXT.md
|
|
@.planning/phases/02-ui-performance/02-RESEARCH.md
|
|
@.planning/phases/02-ui-performance/02-PATTERNS.md
|
|
@.planning/phases/02-ui-performance/02-04-SUMMARY.md
|
|
@.planning/codebase/CONVENTIONS.md
|
|
@CLAUDE.md
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Locate AIUI and record the embed contract</name>
|
|
<files>.planning/phases/02-ui-performance/02-AIUI-D14.md</files>
|
|
<read_first>
|
|
- `neode-ui/src/views/Chat.vue` lines 61-125 — the `aiuiUrl` computed at line ~74 builds the query string today (`embedded=true`, `hideClose=true`, and in demo mode `mockArchy=1&seed=1`), and `onAiuiMessage` at line ~92 validates the message origin against that URL before accepting a `ready` message.
|
|
- `neode-ui/package.json` — the `dev:mock` script expects AIUI at `../../AIUI` relative to `neode-ui` and degrades to a placeholder when it is absent.
|
|
- `neode-ui/scripts/setup-aiui-server.sh` — the other place the sibling-repo path is encoded.
|
|
- `apps/aiui/manifest.yml` — describes a prebuilt container image (`localhost/archipelago-aiui:latest`) with no source in this checkout.
|
|
- `neode-ui/src/services/contextBroker.ts` — the existing postMessage channel between neode-ui and AIUI; if AIUI honours a message-based control rather than query parameters, this is where that would ride.
|
|
- `.planning/phases/02-ui-performance/02-RESEARCH.md` open question 1 — the full statement of what is known and unknown about AIUI's location and parameter support.
|
|
</read_first>
|
|
<action>
|
|
Settle where AIUI's source is and what it accepts, and write the answer down before
|
|
any code is written.
|
|
|
|
Search for the checkout: check `../../AIUI` relative to `neode-ui` (that is,
|
|
`<parent-of-archy>/AIUI`), then search the filesystem more broadly for a directory
|
|
containing AIUI's own `package.json`. Project notes record the ThinkPad at `.116` as
|
|
the primary build server, so if the source is not on this machine it may be there —
|
|
check whether it is reachable before concluding it is unavailable.
|
|
|
|
If the source is found, grep it for how it reads embed configuration: search for the
|
|
existing parameters `embedded`, `hideClose`, `mockArchy` and `seed` to find the
|
|
parameter-parsing site, then determine whether anything already controls (a) the
|
|
chat's expanded versus collapsed initial state and (b) the initial view on a mobile
|
|
viewport (chat versus context). Record the exact parameter names, accepted values,
|
|
and the file and line where each is read.
|
|
|
|
If the source is not found, record that plainly with the paths searched. Do not
|
|
invent a parameter name and ship it — a flag nothing reads is a change that looks
|
|
done and does nothing.
|
|
|
|
Also inspect the running container path: `apps/aiui/manifest.yml` points at a prebuilt
|
|
image, so an AIUI-side change requires a rebuild and republish of that image. Record
|
|
what shipping an AIUI-side change would actually involve, because D-15 restricts this
|
|
phase to the dev pair with no OTA.
|
|
|
|
Write `.planning/phases/02-ui-performance/02-AIUI-D14.md` recording: the source
|
|
location (or the searched paths and the conclusion); the embed-parameter contract as
|
|
it exists today; for each of D-14's two defaults, whether it is already supported,
|
|
needs a new AIUI-side parameter, or needs a postMessage control; which side implements
|
|
each half; and what deploying the AIUI half requires. Commit it.
|
|
|
|
This document is the contract Task 2 builds against and is the artifact a future agent
|
|
reads instead of re-running this search.
|
|
</action>
|
|
<verify>
|
|
<automated>D=/home/archipelago/Projects/archy/.planning/phases/02-ui-performance/02-AIUI-D14.md; test -f "$D" || exit 1; for s in '## Source Location' '## Embed Parameter Contract' '## D-14a' '## D-14b' '## Deployment Impact'; do grep -qF "$s" "$D" || { echo "missing: $s"; exit 1; }; done; echo OK</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` exists and contains all five required headings
|
|
- `## Source Location` states either an absolute path to the AIUI checkout or the list of paths searched and the conclusion that it is unreachable
|
|
- `## Embed Parameter Contract` lists every embed parameter AIUI reads today with the file and line where each is parsed, or states that the parsing site could not be inspected and why
|
|
- `## D-14a` and `## D-14b` each state one of: already supported by parameter X, needs a new AIUI-side parameter, or needs a postMessage control — and name which side implements it
|
|
- `## Deployment Impact` states what shipping the AIUI half requires, given that `apps/aiui/manifest.yml` points at a prebuilt image and D-15 limits this phase to the dev pair with no OTA
|
|
- No parameter name appears in `neode-ui/src/views/Chat.vue` at the end of this task: `git diff --name-only HEAD -- neode-ui/src/views/Chat.vue | wc -l` prints 0
|
|
- The document is committed
|
|
</acceptance_criteria>
|
|
<done>Where AIUI lives, what it accepts, which side implements each D-14 default, and what deploying it costs are all written down and committed — or the search is documented as exhausted so a human can point at the checkout.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Stable embed URL carrying both D-14 defaults</name>
|
|
<precondition>`.planning/phases/02-ui-performance/02-AIUI-D14.md` exists, is committed, and its `## Source Location` section names a reachable AIUI checkout rather than recording the search as exhausted (Task 1 outcome)</precondition>
|
|
<files>neode-ui/src/views/Chat.vue, neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts</files>
|
|
<read_first>
|
|
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` — the contract written by Task 1; it names the parameters and which side implements each default
|
|
- `neode-ui/src/views/Chat.vue` lines 61-125 — the `aiuiUrl` computed, the `onAiuiMessage` origin check, and the `onActivated` / `onDeactivated` handling added by plan 02-04
|
|
- `.planning/phases/02-ui-performance/02-04-SUMMARY.md` — the bucket table for `Chat.vue`, so this task does not undo the placements made there
|
|
- `neode-ui/src/services/contextBroker.ts` — if Task 1 concluded a postMessage control is needed instead of a query parameter, this is the channel it rides
|
|
- `neode-ui/src/composables/useDemoIntro.ts` — `IS_DEMO` is already a build-time-ish input to `aiuiUrl`; confirm whether it can change at runtime before treating it as stable
|
|
</read_first>
|
|
<behavior>
|
|
- `aiuiUrl` returns the same string across re-renders of a mounted Chat view, including after a deactivate and reactivate cycle
|
|
- `aiuiUrl` does not change when the viewport is resized across the mobile breakpoint
|
|
- The returned URL carries the D-14a expanded-state flag and the D-14b mobile-initial-view flag exactly as named in `02-AIUI-D14.md`
|
|
- The existing `embedded` and `hideClose` parameters and the demo-mode parameters are unchanged
|
|
- `onAiuiMessage` still rejects a message whose origin differs from the embed URL's origin
|
|
- `aiuiConnected` remains true across a deactivate and reactivate cycle, since AIUI's ready message is not re-sent
|
|
</behavior>
|
|
<action>
|
|
Implement the neode-ui half of D-14 using exactly the parameter names and values
|
|
recorded in `02-AIUI-D14.md`. Append them in the existing `aiuiUrl` computed,
|
|
following the string-concatenation convention already there, for both the
|
|
`VITE_AIUI_URL` branch and the production/demo branch.
|
|
|
|
The load-bearing constraint is URL stability. The Chat view's instance now survives
|
|
tab switches, and the iframe only keeps its loaded state while its `src` stays
|
|
byte-identical. Any input to `aiuiUrl` that can change at runtime — a reactive
|
|
viewport width, a connection flag, a timestamp, a random value — would change the
|
|
`src`, force a full AIUI reload on the next render, and give back the entire benefit
|
|
of keeping the tab alive. So D-14b's mobile default must not be expressed as a
|
|
reactive viewport read in this computed. Pass a mobile-initial-view flag whose value
|
|
is fixed for the view instance and let AIUI decide from its own viewport, or resolve
|
|
the viewport once at setup time into a non-reactive constant. Record which of the two
|
|
you chose and why in the SUMMARY.
|
|
|
|
Do not change the origin-validation logic in `onAiuiMessage`. These are presentation
|
|
flags; nothing here widens what the embedded application may reach, and the origin
|
|
check is what keeps that true.
|
|
|
|
Do not reset `aiuiConnected` on deactivate. AIUI sends its `ready` message once after
|
|
load; a reset would leave the panel showing a disconnected state forever after the
|
|
first tab switch. Plan 02-04 already flagged this — confirm it holds.
|
|
|
|
If `02-AIUI-D14.md` records that a default is implemented on the AIUI side, make that
|
|
change in the AIUI checkout too, keeping it as small as the default itself: change the
|
|
initial state, do not restructure AIUI's layout. Commit it in that repository and
|
|
record the commit reference in this plan's SUMMARY. Deploying it follows D-15 — dev
|
|
pair only, no OTA — and plan 02-08 owns the deploy.
|
|
|
|
Create `neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts` covering the six behaviors
|
|
above. The stability assertions are the important ones: read `aiuiUrl` twice across a
|
|
simulated resize and across a deactivate/reactivate cycle and assert string equality.
|
|
</action>
|
|
<verify>
|
|
<automated>cd neode-ui && npm run test -- src/views/__tests__/chatAiuiEmbed.test.ts && npm run test && npm run type-check && npm run build</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `npm run test -- src/views/__tests__/chatAiuiEmbed.test.ts` exits 0 with all six behaviors covered
|
|
- A test asserts `aiuiUrl` is string-equal before and after a simulated viewport resize across the mobile breakpoint
|
|
- A test asserts `aiuiUrl` is string-equal before and after a deactivate/reactivate cycle
|
|
- A test asserts the returned URL contains both D-14 flag names exactly as recorded in `02-AIUI-D14.md`, plus the pre-existing `embedded=true` and `hideClose=true`
|
|
- A test asserts a message from a foreign origin does not set `aiuiConnected`
|
|
- A test asserts `aiuiConnected` survives a deactivate/reactivate cycle
|
|
- `npm run test` exits 0, `npm run type-check` exits 0, `npm run build` exits 0
|
|
- The built bundle carries the new flags: `grep -rl "hideClose" web/dist/neode-ui/assets | head -1` prints a file, and the same file also matches the D-14 flag names
|
|
- If an AIUI-side change was required, the SUMMARY records its repository path and commit reference
|
|
</acceptance_criteria>
|
|
<done>The AIUI panel opens expanded, opens on chat on mobile, and its embed URL is provably stable across resize and reactivation so the iframe never reloads on a tab switch.</done>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 3: Confirm the AIUI panel persists and both D-14 defaults hold on desktop and mobile</name>
|
|
<what-built>
|
|
The Chat tab's AIUI panel now survives tab switches without reloading, and the two
|
|
D-14 defaults are in place: the chat opens expanded, and on a mobile viewport AIUI
|
|
opens on its chat view rather than its context view. The embed URL is proven stable
|
|
across viewport resize and across deactivate/reactivate, which is what keeps the
|
|
iframe from reloading.
|
|
</what-built>
|
|
<how-to-verify>
|
|
1. From the repo root run `./scripts/dev-start.sh` and open the :8100 dev preview
|
|
pointed at archi-dev (password `password123`).
|
|
2. Open the Chat tab and wait for the AIUI panel to finish loading.
|
|
3. Expected: the chat is already expanded — you should not have to expand it yourself.
|
|
4. Switch to another main tab, then back to Chat. Expected: the panel is still loaded
|
|
exactly as you left it, including anything you typed. No loading spinner, no
|
|
flash, no scroll back to the top of the conversation.
|
|
5. Resize the browser window across the mobile breakpoint while on the Chat tab.
|
|
Expected: the panel does not reload.
|
|
6. In a mobile viewport (device toolbar, or on a phone against the same preview), open
|
|
the Chat tab fresh. Expected: it opens on the CHAT view, not on the context view.
|
|
7. Switch away and back on mobile. Expected: still on chat, still loaded, still
|
|
expanded.
|
|
8. Confirm the AIUI panel still functions — send a message and get a response — so the
|
|
presentation flags did not disturb the connection.
|
|
</how-to-verify>
|
|
<resume-signal>Type "approved", or describe what you saw: which step, desktop or mobile, what happened instead.</resume-signal>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| neode-ui → embedded AIUI iframe | Configuration crosses out of the trusted app into an embedded application via the URL query string |
|
|
| embedded AIUI iframe → neode-ui | AIUI posts messages back into the host window; only same-origin-as-the-embed-URL messages may be honoured |
|
|
| AIUI container image → fleet nodes | An AIUI-side change ships as a rebuilt prebuilt image, not as a neode-ui asset |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-02-05 | Spoofing | `onAiuiMessage` origin validation in `Chat.vue` | medium | mitigate | Task 2 forbids touching the origin check and requires a test asserting a foreign-origin message does not set `aiuiConnected`; the flags added are presentation-only |
|
|
| T-02-18 | Elevation of Privilege | Embed flags widening what the iframe is trusted with | high | mitigate | This plan's second prohibition scopes the change to how AIUI opens, never what it may reach. No permission, token, credential or capability parameter is added; only initial-view and expanded-state flags recorded in `02-AIUI-D14.md` |
|
|
| T-02-19 | Information Disclosure | Sensitive values leaking into an iframe URL, which appears in referrer headers and browser history | high | mitigate | The two flags are boolean-shaped presentation values. Task 1's contract document is the review point: if a proposed parameter carries anything identity- or session-bearing, it must not ship in the query string |
|
|
| T-02-20 | Tampering | An AIUI-side change reaching the fleet outside the release train | medium | mitigate | D-15 restricts this phase to the dev pair with no OTA; Task 1 records what shipping the AIUI half requires and plan 02-08 owns the deploy under that constraint |
|
|
| T-02-SC | Tampering | npm/pip/cargo installs | high | mitigate | No package-manager installs are in scope on the neode-ui side. If the AIUI checkout requires an install to build, that install runs in AIUI's own repository under its own lockfile; do not add a dependency to `neode-ui/package.json` in this plan |
|
|
</threat_model>
|
|
|
|
<artifacts_this_phase_produces>
|
|
## Artifacts this phase produces
|
|
|
|
Created or changed by this plan — new API, not drift:
|
|
|
|
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` — the recorded AIUI source location and embed-parameter contract
|
|
- `neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts`
|
|
- Two new embed query parameters on the AIUI iframe URL, named in `02-AIUI-D14.md` (D-14a expanded state, D-14b mobile initial view)
|
|
- Possibly a corresponding change in the AIUI sibling repository, referenced by commit in the SUMMARY
|
|
|
|
Created elsewhere in Phase 02: `shouldKeepAlive()`, `KEEP_ALIVE_PATHS`, `KEEP_ALIVE_MAX`,
|
|
`DashboardRouterView.vue`, `RefreshIndicator.vue`, `resources.clearAll()`,
|
|
`useCachedResource.test.ts`, `keepAliveTabs.test.ts`, `keepAliveLifecycle.test.ts`,
|
|
`meshTabCache.test.ts`, `serverTabCache.test.ts`, `homeTabCache.test.ts`,
|
|
`secondaryScreenCache.test.ts`, `resourcesClear.test.ts`,
|
|
`e2e/perf/{surfaces,measure,surface-perf.spec}.ts`,
|
|
`.planning/phases/02-ui-performance/{02-FINDINGS.md,02-PERF-BASELINE.json,02-PERF-AFTER.json}`.
|
|
</artifacts_this_phase_produces>
|
|
|
|
<assumptions_and_flagged_items>
|
|
## Assumptions & Flagged Items
|
|
|
|
- **PERF-02 edge-probe row (spec-less fallback):** returned `unclassified` / `unresolved`. FLAGGED, not auto-backstopped and not dropped; surfaced here for human review. Resolved in substance by this plan's `must_haves.truths`, with the AIUI-layout-regression truth carried as a `verification: backstop` marker because it depends on an application whose source is outside this repository.
|
|
- **FA-E (RESEARCH open question 1, unresolved at plan time):** AIUI's source is not present in this checkout — `neode-ui`'s `dev:mock` script and `scripts/setup-aiui-server.sh` both expect it at `../../AIUI`, and RESEARCH.md verified no such directory exists on the machine that ran the research. Project notes record the ThinkPad at `.116` as the primary build server, so it may live there. This is a genuine missing-information constraint, not a difficulty judgment. Task 1 resolves it and Task 2 carries a `<precondition>` that halts if it could not be resolved — D-14 is a locked decision and is not deferred or reduced, it is blocked on a fact only the developer can supply.
|
|
- **Open:** whether either D-14 default is already supported by an existing AIUI parameter is unknown. `Chat.vue` already passes `embedded`, `hideClose`, `mockArchy` and `seed`, so a parameter mechanism exists; whether it covers expanded-state and mobile-initial-view is what Task 1 determines.
|
|
- **Open:** whether D-14b is better expressed as a fixed flag AIUI interprets against its own viewport, or as a viewport resolved once at setup, is decided in Task 2 and recorded. Both satisfy the URL-stability constraint; a reactive viewport read does not.
|
|
</assumptions_and_flagged_items>
|
|
|
|
<verification>
|
|
- `cd neode-ui && npm run test` exits 0
|
|
- `cd neode-ui && npm run type-check` exits 0
|
|
- `cd neode-ui && npm run build` exits 0 and the D-14 flags appear in `web/dist/neode-ui/assets`
|
|
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` is committed with all five headings
|
|
- The human-verify checkpoint is approved on both desktop and mobile viewports
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- The AIUI panel survives tab switches with no reload, proven by a string-equality test on the embed URL and confirmed by eye
|
|
- The chat opens expanded and, on mobile, opens on the chat view
|
|
- The embed URL contains no runtime-varying value
|
|
- Origin validation is unchanged and the connected state survives deactivation
|
|
- The AIUI-side contract is written down, so no future agent repeats the search
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/02-ui-performance/02-07-SUMMARY.md` when done. It MUST record:
|
|
the AIUI source location; the exact parameter names and values shipped; which side
|
|
implements each D-14 default and, if AIUI-side, the repository path and commit reference;
|
|
how D-14b was expressed without introducing a runtime-varying URL input; and what
|
|
deploying the AIUI half requires under D-15.
|
|
</output>
|