Skip to content

feat(ui): the agent header, restored — in-place name and avatar, funding switcher, toggles, live sparklines, 7-day spend, GitHub line, capacity bars (abilityai/trinity-enterprise#955) - #3826

Merged
vybe merged 16 commits into
devfrom
feature/ent955-header-inline-identity
Oct 20, 2026

Conversation

@trinity-ability

@trinity-ability trinity-ability commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Operator ruling on the v1.0.0 release build (2026-10-19): the agent header goes almost back to the way it was before the Settings home (#3798, ent#887) and its ent#950 amendment (#3823). Settings › General keeps its rows; the header is the quick path. The id rename stays in Settings only (it stops the agent and re-keys ~20 tables, #1664), and Pull / Push / Refresh stay on the Git tab.

Closes abilityai/trinity-enterprise#955

The header now — four rows

  1. Identity. Avatar with the owner's hover overlay (Generate an avatar when there is none; Change avatar prompt + Next emotion when there is one; opens on keyboard focus too). Name with a pencil that edits the display label in place — editor inside the h1, pre-filled with the label never the slug, Enter/blur save, Esc cancels, empty clears. Slug, description, badges, and the funding badge that doubles as the subscription switcher (owners; native select overlaid on the badge). Right: Brain door, capacity bars, Start/Stop.
  2. Autonomy and Read-only toggles, tags (chips, counted "+N", dialog editor), and the live CPU / MEM sparklines against their limits. No uptime, no gear.
  3. 7-day spend row: sparkline, today, trend vs avg, lifetime · runs. ≈ API-equiv under a subscription, — when cost is unreported.
  4. GitHub line — icon, linked repo, branch, last commit, changed / ahead / behind / in sync — only when a repository is connected. No buttons.

Removed from the header: the ent#950 context ring (AgentHeaderContext.vue deleted) and the Overview live strip (AgentLiveStrip.vue deleted; Overview shows no strip).

Capacity bars

The dashboard's CapacityMeter, alone — no word, no count, its own tooltip names the slots. Green → amber → red as the agent's parallel slots are taken, pulsing at full. Running agents only.

It lights for a 10-second task. Two causes fixed:

  • The fleet store's WS-driven workingState was fed only by the store's own socket, which the Dashboard opens on mount and closes on unmount — on the agent page nothing fed it. The app-wide /ws (utils/websocket.js) carries the same agent_activity events and now feeds it (operator roles).
  • The slot map (fetchSlotStats, 15 s tick in useAgentContextStats) is re-read on both edges of the working state. Re-reading only on the start left the bar lit up to 15 s after the run.

Measured on a 5 s turn: lit within ~1 s of the start, cleared 0.5 s after the end.

One footprint while loading

Measured on an in-app agent switch and fixed: the description line is always one line tall; the funding badge has a same-size skeleton; the spend row is always rendered (skeleton → "No spend yet" or the trend); the GitHub row is reserved up front for a github: template; row 2's tags block is flex-1 with truncating chips and the live cluster never wraps internally; and the page's loading spinner no longer stacks above a still-rendered agent on a switch (KeepAlive). Header top, header height and tab-strip top are constant across a round-trip switch.

Events — one handler per verb

The header emits the same events the General pane's rows emit: set-label, open-avatar-modal, cycle-emotion, change-subscription, toggle-read-only, plus toggle, toggle-autonomy, update-tags / add-tag / remove-tag. AgentDetail wires them to the handlers it already owned, so the two surfaces cannot drift. AgentHeaderMeta gains show-repo / show-tags so the header renders its two halves in two rows, and the last-commit sha.

Design-system self-check

  • Zero raw non-gray palette classes, zero hex. The raw-colour baseline grew for the restored rows' gray chrome and is re-frozen in its own commit with the growth named (AgentHeader.vue raw_gray 9 → 57 across the series; AgentHeaderMeta.vue first entry 19). npm run check:tokens OK.
  • Loading-gate baseline regenerated (one gate narrowed, none added).
  • Both themes verified live; keyboard: Esc/Enter on the editor, focus rings on every new button, overlay on focus-within.

Tests

  • tests/unit/agentHeaderIdentityEdit.mount.spec.js (mounted): pencil per principal, label-not-slug prefill, Enter/blur/Esc/unchanged/empty; overlay per avatar state and principal; funding select → change-subscription, viewer gets no switcher; read-only reaches the page, no gear, no uptime; live stats running vs stopped; spend row reserved/skeleton/"no spend yet"; GitHub row reserved per template; capacity bars alone (WS-before-poll lights one bar; nothing when stopped); and a shallow-mount of AgentDetail proving set-label reaches PUT /label and the modal opens from the header.
  • agentHeader950.mount.spec.js: repo line with commit sha and no Pull / Push; tags; matchMedia stubbed hoisted for the sparklines.
  • websocketWorkspaceOnly.spec.js: an operator's activity events light and clear the working state over the app-wide socket; a Workspace-only member's do not.
  • useAgentContextStats.spec.js fake store gains the slot fetch.
  • Full frontend unit suite: 386 files, 6130 tests, exit 0.

Docs

Requirements core-agent.md (Settings-home entry), feature flows agent-settings-home.md + agent-avatars.md, architecture/frontend.md, user-docs managing-agents.md.

🤖 Generated with Claude Code

trinity-ability and others added 3 commits October 19, 2026 20:05
… agent header again (Abilityai/trinity-enterprise#955)

ent#887 (#3798) moved the label edit and the avatar prompt into Settings ›
General and ent#950 (#3823) kept the header slim. The operator asked for the
quick path back: a pencil beside the name edits the display label inline
(editor inside the h1 so it inherits the heading's type and ink; pre-filled
with the label, never the slug; Enter/blur save, Esc cancels, empty clears),
and hovering — or focusing into — the avatar shows the camera (no avatar) or
change-prompt + next-emotion (avatar with an identity prompt). Owners only,
never a system agent — the same predicate the General pane uses.

The header emits the SAME three events the General pane's Identity rows emit
(`set-label`, `open-avatar-modal`, `cycle-emotion`), and AgentDetail wires them
to the handlers it already owned, so the two surfaces cannot drift. Settings ›
General keeps its rows; the id rename stays there only (it stops the agent and
re-keys ~20 tables, #1664).

No new raw palette classes: the header's gray count stays at its baseline (9),
the overlay's scrim and ink are `bg-black/40` / `text-white` on a photograph,
the pencil uses action-primary tokens, and the inline editor inherits the h1's
colour. Keyboard reaches the overlay via `group-focus-within`.

Tests: `tests/unit/agentHeaderIdentityEdit.mount.spec.js` mounts the header
(pencil visibility per principal, label-not-slug prefill, Enter/blur/Esc/
unchanged/empty, overlay per avatar state and principal) and shallow-mounts
the page (set-label reaches PUT /label and the heading follows; the modal
opens from the header; `hasAvatarPrompt` is passed). Full frontend unit suite
green (386 files, 6122 tests, ratchets included); `check:tokens` OK.

Docs: requirements core-agent (§ Settings home), feature flows
agent-settings-home + agent-avatars, architecture frontend.md, user-docs
managing-agents.

Closes Abilityai/trinity-enterprise#955

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…wallows its underline (Abilityai/trinity-enterprise#955)

Seen in the browser on the first build: the identity block is content-sized,
so the editor's `w-full` resolved against the input's intrinsic ~20ch and a
long name scrolled its first letters out on select-all; the 2px underline
also dropped the badge rows by 2px while editing. The block and the h1 take
`flex-1` only while editing (at rest nothing moves), and the input carries
`-mb-0.5`. Also drops a `font-inherit` class that is not a Tailwind utility —
preflight already gives the input the heading's font.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ck in the agent header (Abilityai/trinity-enterprise#955)

ent#887 moved the header's second and third rows (live CPU/memory, and the
7-day cost sparkline with today, trend and lifetime) to the top of Overview as
`AgentLiveStrip`. The operator wants them on the header, where they were.
The header gains a `strip` slot at the card's bottom; AgentDetail slots the
same `AgentLiveStrip` there with a new `embedded` prop (a top rule instead of
card chrome — mutually exclusive class arms, #2662) and no longer renders it on
Overview, so the strip stays one component with one data path and is shown
once. No new raw palette classes in either file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@trinity-ability trinity-ability changed the title feat(ui): edit the display name and change the avatar in place on the agent header again (abilityai/trinity-enterprise#955) feat(ui): agent header — display name and avatar editable in place, live strip and 7-day spend row back (abilityai/trinity-enterprise#955) Oct 20, 2026
trinity-ability and others added 2 commits October 19, 2026 20:46
…itcher, toggles, live sparklines, 7-day spend, GitHub line (Abilityai/trinity-enterprise#955)

Operator ruling on the release build: the header goes almost back to the way
it was before the Settings home (#3798). Four rows now:

  1  identity — the ent#955 in-place label pencil and avatar overlay stay;
     the badges regain the funding badge with the overlaid native subscription
     select (owners), which emits the same `change-subscription` the General
     pane does; right: Brain door, context ring, Start/Stop
  2  the Autonomy and Read-only toggles (`toggle-read-only` → the page's
     existing handler), the ent#950 tag chips (AgentHeaderMeta, tags only),
     and the live CPU / MEM / uptime sparklines against the configured limits
     (skeleton before the first sample, allocation when stopped) with a gear
     that opens Settings › General for memory / CPU (`open-resources`)
  3  the 7-day spend row (sparkline, today, trend vs avg, lifetime · runs)
  4  the GitHub line — repo, branch, last commit, changed / ahead / behind —
     only when the live status says a repository is connected, and WITHOUT
     Pull / Push / Refresh: the Git tab keeps the verbs (the one exclusion)

AgentHeaderMeta gains `show-repo` / `show-tags` so the header renders its two
halves in two rows, plus the last-commit sha the old git row showed.
AgentLiveStrip is deleted and Overview no longer shows a strip.

The identity spec pins the restored rows (funding select → change-subscription,
viewer gets no switcher, read-only + gear events, live stats running vs
stopped, spend row gated on a first run); the ent#950 spec pins the commit sha
and the absence of Pull / Push on the GitHub row, and stubs `matchMedia`
hoisted, which the sparklines' module-level theme read needs under jsdom.

The raw-colour baseline is re-frozen in the next commit, on its own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eader (Abilityai/trinity-enterprise#955)

Deliberate growth, named: AgentHeader.vue raw_gray 9 → 53 (the pre-#3798 rows
came back with their chrome ink: toggles divider, live-stats meta, spend row,
gear); AgentHeaderMeta.vue gets its first entry at raw_gray 19 (it was created
after the last freeze; adding the last-commit sha made it worth pinning). No
raw non-gray anywhere in either file; no hex.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@trinity-ability trinity-ability changed the title feat(ui): agent header — display name and avatar editable in place, live strip and 7-day spend row back (abilityai/trinity-enterprise#955) feat(ui): agent header back to the pre-Settings-home shape — in-place name/avatar, funding switcher, toggles, live sparklines, 7-day spend, GitHub line (abilityai/trinity-enterprise#955) Oct 20, 2026
trinity-ability and others added 10 commits October 19, 2026 20:47
…no canvas 2D context, and its async clearRect failed the run as an unhandled error (Abilityai/trinity-enterprise#955)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…bilityai/trinity-enterprise#955)

Measured on an in-app agent switch: the tab strip moved twice while the
header filled in. Now every late-arriving piece has a reserved home:
  - the slug / description line is always one line tall;
  - the funding badge shows a same-size skeleton until the auth status lands;
  - the 7-day spend row is always rendered — skeleton in flight, "No spend
    yet" after a first fetch that found no runs — at a fixed min-height;
  - the GitHub row is reserved up front for a `github:` template (skeleton
    while running and the status is in flight, a sentence while stopped) and
    otherwise appears only when the live status names a remote;
  - row 2's tag block and live-stats cluster have fixed widths from `sm` up,
    so whether the row wraps no longer depends on which data has arrived
    (the skeleton, the stopped allocation and the running cluster were three
    different widths — a 32px jump a beat after paint).

Pinned by the identity spec (reserved spend row, reserved GitHub row per
template, funding skeleton). Raw-colour baseline: header raw_gray 53 → 63 for
the three skeleton fills, named here rather than re-frozen apart since it is
the same change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…bilityai/trinity-enterprise#955)

Agent Detail is KeepAlive-cached: on a switch the previous agent stays on
screen for the ~100ms the new one takes to land, and the page's loading
spinner stacked ABOVE it, pushing the header and the tab strip down 112px
and back. The spinner now renders only when there is no agent on screen at
all; a switch is stale-while-loading, like a refresh. Measured: header top,
header height and tab-strip top are constant across a round-trip switch.
Loading-gate baseline regenerated (the gate was narrowed, not added).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… on the agent header (Abilityai/trinity-enterprise#955)

Two operator notes on the restored header. (1) Row 2 broke into two lines:
the live cluster's fixed width was a hair under its running content, so
uptime and the gear dropped under CPU / MEM, and the tags block's fixed width
pushed the cluster itself to a second line. The cluster is now `flex-nowrap`
at a min-width that fits its widest state, and the tags block is `flex-1`
with truncating chips — one line wherever toggles + cluster fit, and the wrap
point no longer moves when data lands. (2) "Is something showing when the
agent is actively working?" — now yes, the dashboard's capacity meter with a
word: a `Working` badge on a pulsing green dot plus the slot bars and
`active/max`, or a quiet `Idle`, beside the context ring for running agents.
"Working" is the WS `workingState` first (instant), else the polled activity
state; the slot count never reads below it, so the bars and the word cannot
disagree. `useAgentContextStats` now also refreshes `fetchSlotStats` on its
15 s tick, so the header and the fleet tile read one map.

Pinned by the identity spec (Idle / Working / WS-before-poll / hidden when
stopped). Raw-colour baseline: header raw_gray 63 → 65 (the `active/max`
meta text), named here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…uster (Abilityai/trinity-enterprise#955)

Operator: the CPU / MEM line carries the sparklines and their limits only.
The `open-resources` emit goes with the gear (memory / CPU are in Settings ›
General, one click away on the tab strip). Raw-colour baseline: header
raw_gray 65 → 59, named here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…op — no Idle/Working word, no slot count, no context ring (Abilityai/trinity-enterprise#955)

Operator: just the capacity indicator. The dashboard's CapacityMeter stands
alone (its own tooltip names the slots); the Working / Idle badge, the
`active/max` text and the ent#950 context ring are gone, and
`AgentHeaderContext.vue` is deleted with its unit and e2e assertions. The
page no longer passes `contextStats` / `working` to the header; the
composable keeps the slot map fresh on its tick. Raw-colour baseline: header
raw_gray 59 → 57, named here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ot fetch the tick now makes (Abilityai/trinity-enterprise#955)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tyai/trinity-enterprise#955)

The bars read the slot map, refreshed every 15 s; a task or message that
finishes between two polls never reached them. The WS activity event lands
the moment an execution starts (`stores/network.js::workingState`), so the
composable now exposes it, the header fills one bar on it immediately (the
bars never read below the WS signal), and the slot map is re-read on that
event so the real count follows without waiting for the tick.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… the working map over the app-wide socket (Abilityai/trinity-enterprise#955)

Root cause of "a 10-second task never lights the capacity bars": the fleet
store's WS-driven `workingState` updated only through the store's OWN
socket, which the Dashboard opens on mount and closes on unmount — on the
agent page nothing fed it, so the header's instant signal was dead and the
bars waited for the 15 s slot poll, which a short run slips between. The
app-wide `/ws` (App.vue, `utils/websocket.js`) carries the same
`agent_activity` events; its handler now feeds the store's
`handleActivityStatusChange` for operator roles. The dashboard "working"
chip benefits the same way when the Dashboard is not the page that opened
the socket.

Pinned in websocketWorkspaceOnly.spec.js through the real connect path:
an operator's start/terminal events light and clear the state; a
Workspace-only member's do not touch it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rinity-enterprise#955)

Measured on a 5 s turn: the start event lit the bar at once and re-read the
slot map, but the end event only cleared the WS flag — the map still said
one slot taken until the next 15 s tick, so the bar stayed lit for up to
15 s after the run. The map is now re-read on both edges; measured again:
lit within a second of the start, cleared half a second after the end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@trinity-ability trinity-ability changed the title feat(ui): agent header back to the pre-Settings-home shape — in-place name/avatar, funding switcher, toggles, live sparklines, 7-day spend, GitHub line (abilityai/trinity-enterprise#955) feat(ui): the agent header, restored — in-place name and avatar, funding switcher, toggles, live sparklines, 7-day spend, GitHub line, capacity bars (abilityai/trinity-enterprise#955) Oct 20, 2026
… each AgentHeaderMeta half carries its own test id (Abilityai/trinity-enterprise#955)

The PR-side e2e run caught two things the mounted specs cannot see:

- At 375px the live cluster's "Loading live usage…" skeleton (`w-64`)
  pushed the page 25px wide. The cluster is now `min-w-0 max-w-full` and
  all three skeletons (live usage, spend, repository) are `max-w-full`,
  so a fixed-width placeholder is bounded by its row.
- `AgentHeaderMeta` is rendered twice (tags half in row 2, repo half in
  row 4) and both carried `data-testid="agent-header-meta"`: Playwright's
  strict mode refused the locator, and the 768px fit check measured the
  badges against the TAGS card's right edge. The repo half keeps
  `agent-header-meta`; the tags half is `agent-header-meta-tags`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vybe

vybe commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

PR Validation Report (/validate-pr, Lane B — frontend only, no tests/ deletions)

PR: #3826 · Branch: feature/ent955-header-inline-identity → dev · Files: 20 (+1074/−387)

Category Status Notes
Commit messages ✅ 16 conventional feat/fix/test/chore(ui) commits, each naming ent#955
Base branch ✅ dev
PR size ✅ 20 files
Status auto-promotion ⚠️ Closes abilityai/trinity-enterprise#955 is cross-tracker — set status-in-dev on ent#955 by hand after merge
Requirements / Architecture / Flows ✅ core-agent.md, architecture/frontend.md, agent-settings-home.md, agent-avatars.md, user-docs all in the diff
Security §4.1–4.3 ✅ no keys, emails, or IPs (the "IP" hits are the GitHub icon's SVG path data)
Enterprise-docs guard ✅ no hits on the added lines
File modes / host paths ✅ none
Config / build packaging ➖ no backend change
Design-system ratchets ✅ raw_nongray 0 and hex 0 in both header files; AgentHeader.vue raw_gray 9 → 57 and AgentHeaderMeta.vue new at 19 are named in the re-freeze commit; loading-gate baseline shrank by one (AgentDetail.vue)
Structural review ⚠️ /review not run on this branch — this report does not cover races or auth boundaries (frontend-only diff, no new API surface)
Execution coverage (§5.4) ✅ see table
Merge gate 🚧 Tier 1 was green on 88997e4; re-running on c804de0 after the fix below. dev-ci success on dev tip 5b1eed8

Execution coverage (§5.4)

changed symbol / test file executed by live consumer verdict
websocket.js → networkStore.handleActivityStatusChange websocketWorkspaceOnly.spec.js drives the real useWebSocket() and asserts network.workingState lights and clears utils/websocket.js:211 (production socket) ✅
useAgentContextStats → slots, working, dual-edge refresh() useAgentContextStats.spec.js views/AgentDetail.vue:91 (:working), :720 (slots: headerSlotStats) ✅
AgentHeader events (set-label, open-avatar-modal, …) agentHeaderIdentityEdit.mount.spec.js — mounted; shallow-mounts the real AgentDetail and asserts set-label reaches PUT /api/agents/alpha/label views/AgentDetail.vue:103-111 ✅ production wiring pinned
agentHeader950.mount.spec.js mounted DOM .text() assertions, no source-text reads — ✅

Found on the PR-side e2e run (88997e4) and fixed in c804de0

  • ❌ → ✅ 375px: the live cluster's w-64 "Loading live usage…" skeleton pushed the page 25px wide. Cluster is now min-w-0 max-w-full; all three skeletons are max-w-full.
  • ❌ → ✅ 768px + strict-mode: AgentHeaderMeta renders twice and both halves carried data-testid="agent-header-meta", so Playwright refused the locator and the fit check measured the git badges against the tags card. The repo half keeps the id; the tags half is agent-header-meta-tags.
  • ⚠️ not PR-caused: agent-detail-request-dedupe.spec.js flagged /compatibility x2 on cornelius. That is CompatibilityPanel.loadInitial's static-then-AI second fetch (container_running && !ai_ran_at), untouched here; the spec normally lands on test-harness-agent, which was not yet running in that run.

Recommendation

APPROVE once Tier 1 + e2e are green on c804de0. Local run of the two header mount specs and the three ratchet guards on c804de0: 5 files, 71 tests, exit 0.

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated per /validate-pr (report above). Tier 1 + e2e + journey-smoke green on c804de0.

@vybe
vybe merged commit d1c2447 into dev Oct 20, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants