Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/app/CONTEXT.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,18 @@ other surface opens beside or in front of the Chat; the Chat is never replaced b
another surface, only overlaid. There is no view in which the Chat is absent.
_Avoid_: Main view, editor, session pane (as the whole hub)

**Brain**:
The data-true living background of the **Chat** — a map of the current session's thought that
fills the Chat behind every component and is never interactive. One per window, present wherever
the Chat is (Landing included) and nowhere else.
_Avoid_: Background, wallpaper, atmosphere, brain strip, canvas

**Glass**:
The frosted, translucent surface every chat component floats on above the **Brain** — tinted
just enough that text stays legible over the moving graph. (How the blur and tint are derived
is an ADR-0002 concern, not a glossary one.)
_Avoid_: Card background, frosted panel, blur layer, tier

**Rail**:
The one narrow vertical strip of icons along the window's leading edge. Collapsed to
icons by default; each icon reveals its text label on hover. The Rail is the only
Expand DownExpand Up@@ -68,6 +80,9 @@ only surfaces its state), setup wall
## Relationships

- The **Landing** is a **Chat**; the window never opens on anything else.
- The **Brain** is the background of the **Chat** and only the Chat; every chat component
floats on **Glass** above it, while the **Rail**, titlebar, and **Panels** stay solid and
frame the living pane.
- Every **Rail surface** opens as a **Panel** beside the **Chat**; the Chat is never
replaced, only overlaid, and at most one Panel is open at a time.
- The **Rail** carries navigation surfaces above and the **Account zone** below; both
Expand DownExpand Up@@ -100,3 +115,6 @@ reader meeting them in older code or notes should map them forward:
a possible future, not part of this redesign. See ADR 0001.
- **Home cards** "Meet Amico" and "Jump back in" → folded into the first-run Landing and
the Chats surface respectively; they are no longer standalone surfaces.
- **Inline brain strip** (the Brain as a single row inside the message timeline) → absorbed
into the **Brain** background. One render loop, promoted from a timeline row to the room;
the timeline keeps only its text shimmer. See ADR 0002.
125 changes: 125 additions & 0 deletions packages/app/docs/adr/0002-brain-as-chat-background.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
# 0002 — The Brain becomes the Chat's living background; components float on legibility-floored glass

- Status: Proposed
- Date: 2026-07-24
- Deciders: Kate Bonner (Head of Product)
- Tracking: harmoniqs/opencode#56

## Context

The amico **Brain** — the data-true, live map of a session's thought — currently renders
as an inline strip *inside* the message timeline (a native canvas engine; the `/brain.html`
iframe was already retired). It is one row among many, easy to scroll past, and it competes
with the messages rather than framing them.

An earlier redesign (`kate/chat-brain-atmosphere`, six commits, never pushed or merged)
explored promoting the Brain to a chat-wide background behind *tiered per-bubble glass*
("atmosphere"), reaching a resolved design and a Phase-5 build. Kate reopened the design
fresh rather than continuing that branch. The open question: does the Brain stay an inline
timeline element, or become the **permanent background of the Chat** with every component
floating on frosted glass — and if so, how is legibility *guaranteed* when text sits on a
translucent surface over a moving graph? "Movement comes through" (translucency) and
"highly legible" (contrast) are in direct tension, and the earlier build hit exactly one
legibility failure: muted grey text not clearing WCAG-AA on its most translucent tier.

This decision governs the **Amicode app UI** (the opencode fork). It sits under ADR 0001
(chat-first shell): the Chat is the one persistent hub, and this ADR settles what the Chat
*looks like*.

## Decision

**The Brain becomes the permanent, full-bleed background of the Chat** — Landing,
conversation, and composer all sit on it. There is **one Brain**: the inline timeline strip
is absorbed into the background (one render loop, promoted from a timeline row to the room).
The **Rail, titlebar, and Panels stay solid**, framing the living pane. The Brain is a fixed,
viewport-anchored backdrop; cards scroll over a stationary Brain.

**Every chat component floats as a frosted-glass card, and legibility is guaranteed by
construction — not by eye:**

- **Blur** — a *high, constant* gaussian blur on every card. It provides perceptual calm and
kills the high-frequency motion detail that hurts reading. It is cheap (no per-frame logic)
and it is *not* what makes text legible.
- **Tint** — contrast comes entirely from a tint whose **opacity is pinned to clear the WCAG-AA
floor (with a small safety margin, not exactly at the floor) over a fixed reference frame** —
a synthetic Brain at peak bloom in which the sampled backdrop is the brightest palette value
**the Brain actually paints in that theme** (peak `#fff676` on dark; peak `#8f8000` on light —
the light-mode Brain never paints `#fff676`, so validating against it there would derive a
near-transparent tint that silently fails over the real light frame). Blur smooths the backdrop but does not darken it; a card over a bright-yellow
cluster is still on bright yellow until the tint pulls the composite toward the ink. The tint
set is **keyed to the chat's own theme** — the chat runs a theme independent of the app shell
(default oc-2) — so a chat-theme change re-keys the Glass and re-validates contrast. **Two
tints:** *standard* (prose, bubbles, composer) holds body text ≥4.5:1; *dense* (code, diffs,
run-plots) is more opaque and holds code/diff text ≥4.5:1 and graphical marks — syntax colors,
diff fills, plot lines — ≥3:1 (WCAG 1.4.11), so colored content stays crisp.

**The Brain is data-true with an adaptive heartbeat.** Nodes light up as amico reads files,
runs tools, and touches the vault; the graph grows with the conversation, **densifying in place
within the fixed, viewport-anchored frame** — bounded by a node cap / recency window so a long
session neither scrolls the Brain nor grows its per-frame cost without limit. Motion follows
activity: a lively tempo when amico is working, ~8fps breathing at rest, and a **hard pause**
when the window is hidden or `prefers-reduced-motion` is set. At rest it shows a **sparse
seed** — a near-empty Landing whose graph grows *only* from the current session's real
thought, with **no cross-session persistence** (a fresh session opens nearly empty). One Brain
per window shows the **active** session; switching the titlebar session tab swaps the Brain to
that session's graph.

**Pre-agreed perf fallback (non-negotiable mid-build):** the frosted look is protected. At
rest, full blur and full motion always apply. A frame-time monitor trips the ease when p95
frame time stays over budget for a sustained window (with hysteresis before restoring, to
avoid oscillation): the Brain's *motion* eases (calm/slow) and returns to full tempo at rest.
If easing motion to a full stop still misses budget, the Brain **hard-pauses to a static
blurred field** — the terminal valve — but **blur fidelity never degrades.**

**Build by reuse.** A new, well-named branch off `local/amicode` reuses the de-risked native
plumbing — the native Brain engine, the glass-token generator, and the mount wiring from the
earlier branch (design-neutral, already hardened by an adversarial port-fidelity review) — and
builds the *design* layer fresh. No iframe. Both light and dark themes are first-class from day
one.

See `packages/app/CONTEXT.md` for the new vocabulary (**Brain**, **Glass**).

## Consequences

- The **inline brain strip** is retired as a timeline row; the timeline keeps only its text
shimmer (the existing working indicator — the cycling gerund + live elapsed shown while a turn
works). One Brain, one render loop.
- **No node-level interactivity is lost.** The strip's only affordance was a click-to-expand/
collapse toggle (it was a collapsible row); the Brain engine has no node hit-testing. As an
always-present background the expand toggle is obviated, not removed from a capable surface.
- When a solid **Panel** covers the Chat, the Brain need not animate behind the occluded area —
a cheap perf saving, since Panels are opaque.
- The *standard* tint's opacity is **bounded below** by the contrast floor — it cannot be made
ultra-transparent, or muted grey text fails AA over the reference frame (the exact
failure the earlier build hit). Secondary/muted text therefore rides the *dense* tint or a
locally-dimmed zone.
- The Chat's cost profile changes: high-radius backdrop-blur recomputed per frame over an
animating canvas, across many cards, while scrolling, is the expensive combination. It must
pass a **laptop-webview 60fps gate** (the real target — headless Chromium is only a proxy);
the motion-ease fallback is the release valve.
- The **Rail, titlebar, and Panels are deliberately excluded.** Nav chrome stays rock-solid,
and dense Panel lists (Chats history, Run gallery) don't fight a moving background.
- The Landing (a fresh, empty Chat per ADR 0001) now opens on the sparse-seed Brain rather
than a blank surface — the window's first impression is the living background, calm and
nearly empty, not a widget wall.

## Alternatives considered

- **Keep the inline brain strip** (a timeline row). Rejected: it is one element among many;
the redesign's intent is the Brain as the room, not a row.
- **One calm reading lane / unified glass sheet** (Brain only in the margins). Rejected in
favor of per-component floating cards — motion lives in the gaps between cards, which is
where "movement comes through" *without* the cards themselves having to be see-through to it.
- **Dynamic contrast glass** (each card samples its backdrop per frame and adapts its tint).
Rejected: per-frame readback is the biggest perf risk and AA is hard to prove against every
frame; the static AA-floor tint is deterministic and cheap.
- **Quiet zone behind cards** (dim/slow the Brain under each card's footprint via per-card
masking). Rejected: real engine complexity for translucency the gaps already provide.
- **Breathing skeleton atlas at rest** (the full latent graph, dim, breathing). Rejected in
favor of the sparse seed — calmer, more literal, and it lets the floating cards dominate.
- **Protect motion and ease blur under load**, or **degrade only while scrolling**. Rejected:
the frosted look is the aesthetic being protected; motion is the acceptable give under load.
- **Rebuild the engine clean**, or **evolve the old `kate/chat-brain-atmosphere` branch**.
Rejected: reuse the de-risked plumbing (including its already-fixed port defects), but build
the design fresh on a new branch rather than inherit the old design structure or re-pay for
the engine port.
Loading