The run rows used a .nav-row class that was never defined, so the button
had no width or flex constraint: long run titles pushed the row past the
340px pane and forced a horizontal scrollbar with the title clipped.
Define .nav-row as a constrained selectable row (width:100%, min-width:0)
so the inner title ellipsizes in place. Align the pane and detail
typography to the 14/12 scale while here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The home page was 13 cards deep with empty roll-ups (Today, In progress,
Team duplicated other pages) and a stark full-black goal card that fought
the rest of the surface. Now:
- Three intent cards lead — Quarter goal (light, integrated), Monthly
budget, and Waiting-on-you with a real "all caught up" empty state.
- A four-up metric strip (workload, run activity, success rate, token
usage) replaces the redundant budget chart. Each tile lifts on hover
and its header shows a live readout of the hovered datum.
- Activity (agent runs, recent messages) closes the page.
No data wiring or approval logic changed; HomePage drops 500 → 344 lines.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flat 13-item list read as clutter and reused icons (Team/Meetings,
Work/Sprints, Inbox/Approvals all shared a glyph). Now:
- Primary group leads with the core loop: Home, Work, Team, Messages,
Runs, Files, Approvals.
- A divider demotes the occasional surfaces: Inbox, Documents,
Knowledge, Meetings, Sprints, Social — still one click away, nothing
orphaned (search can't route to several of them).
- Distinct calendar/target/inbox/folder icons end the collisions.
- Every nav glyph is the 18px icon bound in a 24px box; the collapsed
rail centers icons instead of hiding them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Establish one token system across the shell and primitives:
- Typography: vendor Geist (variable woff2, self-hosted, CSP-clean);
body 14/16px, title 18px, captions 12px; drop oversized headings.
- Color: primary #181818, secondary #6A6A6A over a pure-neutral ramp.
- Spacing: strict 4px grid via --sp-* tokens.
- Radius: 8 → 40 scale (--r-sm..--r-2xl, --r-full).
- Icons: 18px glyph, 1.5 stroke, 24px bound by default.
Sweeps tokens through styles.css, components.css, and overlays.css so
buttons, inputs, cards, pills, menus, the sidebar, topbar, and page
titles all read from the same scale. No page logic touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The rollback pack canonicalizes the source path via realpathSync.native
(correct: it resolves symlinks before comparing). On macOS tmpdir() is
/var → /private/var, so the test's raw legacyPath never matched. Compare
against the canonical path instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every interactive control now acknowledges input: buttons and icon
buttons press down (scale on :active) and ease their hover states, nav
items, menu rows, segmented controls, cards, table rows, and inputs
transition over shared motion tokens (120/200/280ms, ease-out curves).
Dialogs and popovers enter with a short rise + fade over a fading
backdrop; switching routes and onboarding steps plays a quiet view
entrance keyed on the route/step. State pills cross-fade their tones
as runs progress. All motion collapses under prefers-reduced-motion.
PRODUCT.md captures the register and design principles guiding this.
Engine-side writes - hires, delegated tasks, chat messages, run state -
only reached the renderer after a run finished: the queue worker sent
one snapshot-changed at the end, and nothing fired at claim time, so
the window sat frozen for the minutes a CEO run takes unless the user
reloaded by hand. The execution service now takes a notifyDataChanged
callback, the agent loop fires it after every tool-bearing iteration,
and the worker announces the claim itself, so the UI follows the
company working in real time.
Interrupted runs now return to the queue instead of failing - an app
shutdown is not a model error and must not spend the task's bounded
retry budget. Startup work moves behind the single-instance lock so a
losing second instance can never run recovery against the live
instance's database. Background provider requests that died before
receiving an external response id now fail at boot (the background
worker can only resume accepted ones). Agent updates resolve the
department through the same canonical lookup as hires, replacing the
free-text write and its invented "General" default.
A reasoning-heavy model can burn the whole max_tokens budget on
thinking blocks; the parsed turn then has neither text nor tool calls
and the loop treated it as a successful completion, marking a run done
when nothing happened. Such a first turn now fails the run so the
failure is visible and the heartbeat retries; later empty-text turns
after real tool work still complete normally.
The CEO's first run hired five reports into departments "general" and
"engineering"; the hiring-policy lookup was exact-match against the
seeded names (Executive, Engineering, ...), so every hire failed and
the kickoff produced nothing. The tool schema now enumerates the
canonical departments, the policy lookup matches case-insensitively
and stores the canonical name on the agent, and an unknown department
error lists the valid choices so the model can self-correct.
The abort timer was cleared as soon as fetch resolved with headers, so
a server that stalled the response body hung the request forever - and
one hung run froze the autonomous queue behind the worker re-entrancy
guard. JSON requests now carry a header deadline plus a longer body
deadline on one abort controller, and event streams fail when no chunk
arrives within an idle window, cancelling the reader. Timeouts surface
as ProviderHttpError so the run records a clear failure and the
heartbeat retries.
A task_run still marked running at startup has no executor - the
single-instance app that owned it is gone - yet nothing failed it, the
queue worker only claims queued rows, and the heartbeat excludes tasks
with a running run, so one interrupted shutdown wedged the company
forever. Boot now fails orphaned running runs and their in-flight
foreground provider requests (auditably), which hands the task back to
the heartbeat for an automatic retry. Background provider requests are
untouched; the background worker resumes those by external id.
Adversarial review of the kickoff feature surfaced two gaps. First, the
kickoff task was inserted with raw SQL, so a workspace-bound kickoff
carried requires_filesystem without the execution approval every other
task receives - manual runs failed and review-mode orgs had no approval
trail. Kickoffs (in-app and onboarding) now go through createTask and
its policy gates. Second, a goal created before any agent existed
stayed inert forever; creating an agent now operationalizes every
active goal that has no task yet. Goal owners must be active agents:
an archived owner falls back to the top-level agent and the owner
dropdown only offers active agents.
Every source file is now under 500 lines. Task prompt construction
moves from task-execution-service into task-prompts; goal assignee
resolution moves from organization-service into goal-kickoff; the
shared command types become a domain-structured shared/commands
directory with unchanged import paths; the provider adapter contract
types move to provider-adapter-contract, re-exported through
provider-adapters so callers keep a single public surface.
Remove dead imports and unused values across pages; replace the
MentionTextarea index-reset effect with an event-time reset; compute
budget chart segment offsets in a pure helper instead of mutating a
render-scope variable; drop the manual memoization the compiler could
not preserve in BillingPanel.
Settings shrinks from 14 tabs to the five that serve the product:
Workspace, Billing, Providers, Skills, Migration. The deleted panels
(Members/IAM, Models, Policies, Operations, SDLC, Compliance, Profile,
Notifications, Security) were read-only governance tables and raw
audit-log dumps with no old-app counterpart. The Automation page goes
too: its rules stored empty conditions and no engine ever executed
them. Sidebar reordered to match the old app hierarchy (Home, Inbox,
Work, Approvals first). Backend services and schema are untouched.
A goal created from the Work page only wrote a goals row; nothing ever
read it, so the company never started working on it. Creating a goal now
produces an "Operationalize goal" kickoff task for the chosen owner (or
the top-level agent when unowned) and re-enables that agent's heartbeat,
so the autonomous queue worker starts the run - the same activation
contract the previous runtime had via goal_activated wakes and CEO
bootstrap. Onboarding shares the same kickoff module instead of its own
copy.
Inbox page has three tabs: Inbox (filterable by approvals, failures,
blocked, messages — shows chronological items with agent attribution),
Standup (per-agent daily summary with tasks/runs counts), and
Performance (company metrics with success rate, throughput, and
per-agent breakdown table).
HiringPipeline component shows agents across stages: Pending Approval,
Approved, Active, and Rejected. Each stage column shows agent cards
with role, department, and approval timing. Stage assignment is derived
from the agent's lifecycle and associated hire approval state.
AgentWorkloadChart (bar), BudgetUtilizationChart (stacked bar),
RunActivityChart (7-day bar), SuccessRateChart (ring gauge), and
CostTrendChart (7-day line). Pure SVG, no external charting library.
Charts render from snapshot data in the DashboardCharts module.
Two-pane RunsPage: left pane shows searchable, filterable run queue
(by status: queued/running/completed/failed). Right pane shows full
run detail: agent, provider, trigger, timing, duration, execution
context, error messages for failures, and provider request response
text as a scrollable log viewer.
Migration 041 adds parent_task_id to tasks for subtask hierarchy.
TaskDetailDialog now has three tabs: Overview (description, assignee,
project, goal, workspace, runs with error messages), Subtasks (child
tasks with status), and Activity (audit events and approval history).
Inline edit overlay covers title, description, assignee, risk, priority,
and parent task. Task delete is available from the dialog footer.
Agents can now create versioned company documents (PRD, spec, memo,
report, runbook, proposal, policy, guide) via the create_document tool.
Documents are persisted in the documents table and visible in the
Documents page.
Three new pages in the sidebar navigation: Documents (CRUD with type
filtering, inline editing, status lifecycle), Knowledge Base (card grid
with category/importance filters, create/delete), and Automation (rule
table with trigger/action/priority/enable toggle, create/edit/delete).
Each page connects to the corresponding backend service from the
previous commit.
UpdateAgentInput now includes department and budgetCents. The edit dialog
on the Team page shows department and monthly budget alongside the
existing role, title, seniority, provider, and model fields.
The workspace settings panel now includes an Add button that opens a
dialog with name, local path (with directory picker), and optional
project binding. Supports creating secondary workspaces beyond the
initial bootstrap workspace.
Full-stack: IPC channels, command types, OrganizationService methods,
IPC handlers, preload bridge, desktop-api types, and UI dialogs on the
Work page. Users can now create goals and projects directly instead of
relying on the onboarding bootstrap.
The homepage now shows two activity cards: recent agent runs (with
state, trigger, and timing) and recent chat messages (with sender,
channel, and message preview). Gives the operator real-time visibility
into what agents are doing.
Shared MentionTextarea component provides @mention autocomplete in both
the channel composer and the thread reply composer. Type @ to see agents
and employees, arrow-navigate, Enter to select.
Type @ in the message composer to see a dropdown of agents and employees.
Arrow keys navigate, Enter/Tab selects, Escape dismisses. The selected
handle is inserted at the cursor position.
When a task_run is triggered by a chat @mention, the agent now receives
the full thread transcript (up to 20 messages) in its prompt so it
understands what it is replying to and can use the correct thread_id.
Agents were deaf to @mentions — only /agent slash commands triggered a
task_run. Now ChatService.sendMessage auto-wakes every mentioned agent
(skipping self-mentions and /agent which already handles it) by creating
a queued task_run through ChatAgentWakeService.wakeFromMention.