Commit graph

982 commits

Author SHA1 Message Date
Nexus Dev
14ecbf00bb feat(nexus): VoiceContext for phase 14 voice globalization
Lifts MediaStream, recording state, transcription buffer, and queue for
non-Assistant captures out of ChatInput's internal VoiceMicButton. The
provider owns the POST /api/transcribe fetch (v1.6 pipeline, unchanged),
exposes idle/listening/speaking state to the top-strip GlobalMicButton,
and queues transcripts captured away from /assistant for PersonalAssistant
to drain on mount.

Per spec sections 4.2 (mic states), 5.5 (voice from non-Assistant modes),
and 10.3 (voice as global affordance). Tests use manual createRoot + act
with a mocked getUserMedia injector to stay deterministic in jsdom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:22:48 +00:00
Nexus Dev
4623c8aea0 feat(nexus): add mobile tab bar and sheet variants (phase 15)
Replace legacy MobileBottomNav with the new 4-destination MobileTabBar
that mirrors the desktop IconRail (Assistant, Studio, Projects,
Settings). Adds volt active states with a 2px bar above the icon,
safe-area bottom padding, and scroll-hide wiring via the existing
mobileNavVisible handler in Layout.

Adapts Phase 9 HistorySheet and MemorySheet to a full-screen variant
below 768px via useMediaQuery, and makes Phase 11 BuilderTabStrip
horizontally scrollable with scroll-snap and edge fade on mobile.
Light TopStrip polish (tighter padding, truncating breadcrumb)
completes the mobile frame.

PromoteTransition mobile variant is deferred — Phase 12 had not yet
landed when Phase 15 started; the controller can add the mobile
branch when merging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:21:28 +00:00
Nexus Dev
f41690ff30 feat(nexus): add LocalAI and CloudProviders settings cards (phase 13)
LocalAISection surfaces the Hermes adapter tier, Whisper/Piper voice
availability, and the global voice toggle wired through nexus-settings.

CloudProvidersSection lists Anthropic and OpenAI API key slots; keys
are set via the existing /api-keys/store endpoint and rendered as a
password-type Input so values are masked on display and never logged.
Presence is derived from the workspace secret vault.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:20:14 +00:00
Nexus Dev
b277527510 feat(nexus): add promote-transition css-first animation overlay
Phase 12 — renders the 700ms compress-and-rise transition from spec
§5.6 using CSS keyframes inlined via a scoped <style> tag. Chat ribbon
compresses to 30vh with an inset shadow (0–200ms), brainstormer panel
slides up from the bottom (200–500ms), SOURCE CONVERSATION label fades
in (500–700ms). Honors prefers-reduced-motion. No motion library.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:19:32 +00:00
Nexus Dev
70698b9e58 feat(nexus): add brainstormer panel for promote-to-project flow
Phase 12 — the form that rises into the bottom 70% during the promote
animation. Collects goal (required), acceptance criteria (one per
line), default gate checklist, and engineer template picker. ESC key
cancels when not mid-create; goal field autofocuses on mount.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:19:18 +00:00
Nexus Dev
d2d41886fa feat(nexus): add promote-to-project state machine hook
Phase 12 — idle/prompting/creating/done/error state machine that drives
the 700ms compress-and-rise animation and calls projectsApi.create with
a brainstormer payload (goal, acceptance criteria, gates, engineer
template). Also exports buildCreatePayload helper for testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:19:01 +00:00
Nexus Dev
8953fb13ab feat(nexus): add WorkspaceSection settings card (phase 13)
First of eight section cards for the consolidated Settings page. Folds
in theme toggle, re-run onboarding trigger, keyboard shortcuts, log
censoring, feedback sharing preference, and the two experimental
toggles (isolated workspaces, dev-server auto-restart) that previously
lived at /instance/settings/experimental.

Preserves all existing form validation, save handlers, and error
toasting from InstanceGeneralSettings and InstanceExperimentalSettings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:17:40 +00:00
Nexus Dev
7aa98d385f feat(nexus): add SettingsSection shell primitive (phase 13)
Shared section card shell for the Nexus Settings page per spec §8.2:
1px charcoal border, 8px radius, transparent fill, uppercase silver
title with hairline rule. Used by all 8 section cards that replace the
nested Paperclip instance-settings tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:16:32 +00:00
Nexus Dev
428f033690 refactor(nexus): wave 2.5 follow-ups (presentations + proj derivatives)
Three coordinated changes after reviewing the wave 2 subagent reports:

1. Restore Presentations as the 9th Studio workshop.

Phase 10's subagent dropped Presentations from the workshop grid
because the spec's eight-workshop list didn't include it. But
.planning/PROJECT.md explicitly lists "Presentations & video
generation via Remotion" as an Active v1.7 requirement and the
existing PresentationPanel.tsx is already a real working Remotion
generator. Dropping it was silent feature regression.

  - workshops.ts: add "presentations" slug + Presentation Lucide
    icon, placed between social and convert in canonical order
  - classifyIntent.ts: add pitch-deck / slide-deck / demo-video /
    keynote intent routing (before social so "pitch deck" wins)
  - StudioWorkshopDetail.tsx: import PresentationPanel and add a
    "presentation" case in WorkshopBody
  - workshops.test.ts: expected canonical order updated to 9 slugs
  - classifyIntent.test.ts: 4 new parameterized rows for presentations
  - WorkshopCard.test.tsx: index 7 is now PRESENTATIONS, 8 is CONVERT
  - WorkshopGrid.test.tsx: expected card count 9, canonical order

2. ProjectCard hero-stat derivatives instead of em-dash city.

The shared Project record has none of the fields the spec §7.1 card
depends on: milestoneProgress, nextGate, costBurned, per-project
agent count, phase/milestone array. Wave 2 shipped every card with
"—%" and blank hero numbers — visually underwhelming for a layout
whose whole point is the 72px volt performance stat.

Compute best-effort proxies on the client from data that exists:

  - progress: closed_issues / total_issues × 100, from a single
    issuesApi.list(companyId) query grouped by projectId
  - nextGateName: first pending approval whose payload.projectId
    matches, from a single approvalsApi.list(companyId, "pending")
    query
  - lastActivity: max(project.updatedAt, newest issue.updatedAt in
    the project), rendered as "8m ago"-style diff

Each proxy is annotated with // TODO(phase-11.5) for replacement
when real backend aggregates land. phase, costBurnedCents, and
per-project agent count remain hard gaps — rendered as null which
the card surfaces as em-dashes. These three are explicitly queued
for Phase 11.5.

No backend changes; everything derives from existing endpoints.
Two new useQuery calls in Projects.tsx (issues + pending approvals)
both fire per-company, not per-project, so they stay cheap for the
~dozens-of-projects scale the list targets.

3. Spec updated 8 → 9 workshops everywhere it referred to the count.

docs/specs/2026-04-11-nexus-layout-overhaul.md:
  - §2 IA table: 8 → 9 workshops
  - §6 ASCII header: Eight → Nine
  - §6.3 section title: Eight workshops → Nine workshops
  - §11 decisions log #16: amendment note explaining the 8→9 bump
  - §13 phase 10 description: 8-card → 9-card, with Presentations
    explicitly called out
  - "Folds into Studio as the 8th workshop" → "Folds into Studio
    as a workshop (the legacy /convert route is preserved for
    backwards compat)"

Verification: 75/75 studio tests passing; 52/52 projects tests
passing; tsc clean on studio/ + projects/ + Projects.tsx +
ProjectDetail.tsx + StudioWorkshopDetail.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:04:36 +00:00
Nexus Dev
1b7e3d44fe refactor(nexus): wire wave 2 routing and icon rail gate indicator
Controller integration pass after the three wave 2 subagents (phases
9, 10, 11) completed their phase implementations. Three changes in
one commit because they're a single coordinated post-dispatch step:

1. App.tsx routing
   - Adds 5 new per-project builder tab routes for phase 11:
       projects/:projectId/agents
       projects/:projectId/gates
       projects/:projectId/costs
       projects/:projectId/activity
       projects/:projectId/org
     plus their unprefixed UnprefixedBoardRedirect variants so
     direct nav and deep links resolve through the same fallback
     chain as /overview and /issues.
   - Adds content-studio/:workshopSlug as a sibling route for
     phase 10's workshop detail view. Without this, clicking a
     workshop card hit the * fallback NotFoundPage because the
     existing content-studio route was an exact match and the
     ContentStudio-internal pathname workaround couldn't fire.
   - Does NOT rename the legacy /convert route. ConvertPage still
     renders directly at /convert for backwards compat; Studio's
     Convert workshop reuses the ConvertPanel body inside its own
     detail shell.

2. IconRail volt-dot indicator
   - Imports useCompany from CompanyContext and useGateIndicator
     from the new phase 11 hook.
   - When selectedCompanyId resolves to a company with at least
     one pending approval (displayed as "gates" per phase 11's
     display rename), renders a 6px volt dot overlay in the
     top-right of the Assistant destination icon and updates
     the link's aria-label to "Assistant (pending gates)".
   - This is the single global notification surface specified by
     spec section 10.4 - no badge counts, no inbox icons, no toasts.

3. IconRail.test.tsx
   - Mocks useGateIndicator at module scope so tests don't need
     a QueryClientProvider for the rail's useQuery-backed data.
   - Replaces the plain function mock with a vi.fn() spy so
     per-suite overrides can flip hasPendingGates without dynamic
     imports.
   - Adds a sibling describe block that verifies the volt dot
     renders and the aria-label updates when hasPendingGates is true.
   - 7 original tests pass; 2 new tests cover dot-absent and
     dot-present cases. 9 tests total.

Verification: 211/211 tests passing across 22 files in the combined
frame + assistant + studio + projects suites; tsc clean on every
wave 1 and wave 2 file plus App.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:32:56 +00:00
Nexus Dev
1e85565765 refactor(nexus): add BuilderTabStrip to ProjectDetail (phase 11)
Integrates the Phase 11 Builder tab strip into ProjectDetail without
disturbing any existing Paperclip sub-route behavior. The approach:

1. When location.pathname resolves to a Builder tab (overview /
   issues / agents / gates / costs / activity / org), render the
   BuilderTabStrip + the matching new tab component (OverviewTab /
   ProjectIssuesList / AgentsTab / etc.) and hide the legacy
   PageTabBar + legacy overview/list handlers.
2. When the pathname resolves to a legacy tab (configuration /
   budget / workspaces / plugin:*), the existing Tabs/PageTabBar and
   dispatch render exactly as before — backwards compat preserved.

This is Option 1 from the Phase 11 plan: union the old and new tab
logic, silent pass-through for configuration/budget, no surface in
the new strip.

OverviewTab currently receives null for every data slice
(progress/milestones/originChat/activity24h) because the shared
Project type + projectsApi don't carry those fields yet; the tab
renders explicit em-dash / "No milestones defined" placeholders.
See the Phase 11 report for the backend gap list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:24:40 +00:00
Nexus Dev
e43d5ec220 refactor(nexus): drop unused useNavigate import from PersonalAssistant (phase 9)
Phase 9's Promote action no longer navigates away from the Assistant
route (spec §5.7 says the user stays in Assistant after promotion), so
useNavigate() and its ugly data-nav placeholder span are dead code.
Drop them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:23:42 +00:00
Nexus Dev
c3689f11b1 refactor(nexus): rewrite Projects list with hero-stat cards (phase 11)
Replaces the EntityRow table with the spec §7.1 hero-stat card grid:
full-width cards on <1024px, 2-up on lg+, 16px gap. Each card uses
the new ProjectCard component (72px Inter Black volt hero, status
dot, progress bar, sub-line, footer).

Adds the spec's empty state — full-bleed 96px Inter Black volt
"NO PROJECTS YET" with a forest-green "⊕ START YOUR FIRST PROJECT"
CTA. The regular top-right "⊕ NEW PROJECT" CTA is also forest-green.
Both CTAs wire to the existing openNewProject dialog.

Data gaps: the card passes progress=null, phase=null, nextGateName=
null, costBurnedCents=null because the shared Project type doesn't
carry those fields yet. ProjectCard renders em-dash placeholders
gracefully. Status is mapped from the existing ProjectStatus enum
(paused → waiting, active/running → working, else idle).

Also fixes an unrelated test fixture that used the stale "active"
ProjectStatus literal which tsc rejects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:23:14 +00:00
Nexus Dev
06c6317c25 refactor(nexus): rewire PersonalAssistant to use new frame pieces (phase 9)
Composes the Phase 9 assistant primitives at /assistant: full-bleed chat
canvas with a 760px centered column, AssistantHomeGreeting for the no-
conversation state, AssistantInputBar + ActionStrip anchored at the
bottom, and HistorySheet / MemorySheet slide-overs. Drops the 160px
inner conversation-list column and the custom MessageBubble, delegating
thread rendering to the shared ChatMessageList and streaming to the
shared useStreamingChat hook. Adds a ?prompt= query-param fallback for
Studio → Assistant hand-offs, preserves the existing assistantHandoff
call as the Promote action, and syncs the selected conversation id with
the shared ChatPanelContext so HistorySheet stays in lockstep.

Also fixes typecheck fallout from the rewrite: switches toast tones to
the documented info|success|warn|error set, narrows the stale-project
filter to use archivedAt (ProjectStatus never had "archived"), and
tightens the MemorySheet test render helper to JSX.Element.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:22:55 +00:00
Nexus Dev
d10c5b991e feat(nexus): add thin wrappers for per-project tabs (phase 11)
Wires the 6 Builder tab content components:

• IssuesTab — thin wrapper around IssuesList (which already accepts a
  projectId prop, so this is a full wiring).
• GatesTab / AgentsTab / CostsTab / ActivityTab / OrgTab — render a
  shared TabPlaceholder marking the Phase 11 data gap. None of the
  underlying list components accept a projectId filter today, and the
  Phase 11 plan forbids modifying them unilaterally. The placeholders
  are explicit (never fabricate per-project data) and carry the
  follow-up description the controller will turn into a ticket.

GatesTab is named "Gates" everywhere visible (display-only rename per
spec §7.2.4 / plan §5); approvalsApi / Approval type / /api/approvals
endpoints are all untouched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:21:33 +00:00
Nexus Dev
cd6c172d48 refactor(nexus): rewire content studio to workshop grid 2026-04-11 12:21:29 +00:00
Nexus Dev
f20fd0ec8d feat(nexus): add OverviewTab with hero stat + milestones (phase 11)
Implements the default Builder tab per spec §7.2.1:
• 72px Inter Black volt hero percentage + "N AGENTS ACTIVE" counter
• Milestone checklist card with [✓]/[○]/[ ] bullets and pale yellow
  next-gate marker + "← NEXT GATE" label
• Optional origin chat card (hidden when project has no origin)
• 24h activity rollup card (commits, issues closed, gates awaiting,
  burned)

All data inputs are typed with nullable fields so callers can pass
null for any missing slice and the tab renders explicit em-dash or
"No milestones defined" placeholders without fabricating values.
The Project type doesn't currently carry milestones, origin chat, or
24h rollups — see Phase 11 report for the backend gap list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:19:53 +00:00
Nexus Dev
a0cb132e9d feat(nexus): add MemorySheet slide-over (phase 9)
Right-side 340px slide-over that lists the facts stored in the existing
assistantMemoryApi and lets the user append new facts or clear memory.
Handles loading, error, and empty states, closes on backdrop click or
ESC, and degrades to a workspace prompt when no company is selected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:19:36 +00:00
Nexus Dev
2c3f4ff623 feat(nexus): add BuilderTabStrip component (phase 11)
Introduces the 7-tab Builder strip rendered under TopStrip on Project
Detail: OVERVIEW · ISSUES · AGENTS · GATES · COSTS · ACTIVITY · ORG.
Inter 600 14px uppercase, 0.1em tracking, silver default, volt text
+ 2px volt underline on active. ORG is hidden for single-agent
projects (spec §7.2.7).

Also exports resolveBuilderTab() and useActiveBuilderTab() for the
ProjectDetail integration. The link targets for agents/gates/costs/
activity/org route back to ProjectDetail via path matching until the
controller adds the new App.tsx routes post-Wave (see report).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:18:35 +00:00
Nexus Dev
8527beca56 feat(nexus): add HistorySheet slide-over (phase 9)
Left-side 320px slide-over butted against the icon rail. Wraps the
existing ChatConversationList so conversation grouping, search, pinning
and create/rename flows are preserved as-is. Closes on backdrop click
or ESC and includes a dismiss button in the header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:18:31 +00:00
Nexus Dev
d432326e7a feat(nexus): add StudioWorkshopDetail page (phase 10)
Two-column workshop detail shell — params on the left (holds the
existing generator panel), preview placeholder and action bar
(Save/Export/Send to Assistant) on the right. Maps each workshop slug
to one of the legacy ContentStudio generator panels, with ConvertPanel
folded in for the convert workshop and a placeholder fallback for
unknown slugs. Reads ?prompt= from the URL and surfaces it as a chip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:17:58 +00:00
Nexus Dev
cd75772a6a feat(nexus): add ActionStrip for assistant actions (phase 9)
Four-button row beneath the input bar — Promote (volt outline, disabled
when the conversation is not promotable), Attach, Memory, History. Pure
presentational component; the caller owns click handlers and the
promotable predicate. Phase 12 will wire the promote animation itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:17:46 +00:00
Nexus Dev
a2dab5b4f6 feat(nexus): add ProjectCard hero-stat component (phase 11)
New Projects list card: 72px Inter Black volt hero percentage, pale
yellow/volt/forest status dot, 8px progress bar, sub-line with phase
and next-gate counter, footer with burn + last activity. Graceful
em-dash placeholders when milestone/cost/activity fields are missing
(the shared Project type has no milestoneProgress etc. yet — see
Phase 11 data-gap report).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:17:12 +00:00
Nexus Dev
3fe4b543cf feat(nexus): add AssistantInputBar composite (phase 9)
Wraps the existing ChatInput and VoiceWaveform into the spec-canonical
input surface: voice waveform above, hairline divider, and text input
below, inside a rounded 8px near-black shell. Stateless composite —
callers pass micStream/micActive and onSend through to the underlying
primitives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:16:51 +00:00
Nexus Dev
31d64bbb1d feat(nexus): add StudioPromptBar with intent routing (phase 10)
Freeform input rendered at the bottom of the Studio home. Uses the
classifyIntent helper to route to a workshop, or falls through to the
Assistant for unclassified prompts. The parent page wires the two
callbacks to navigate().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:16:12 +00:00
Nexus Dev
37d3919c98 feat(nexus): add AssistantHomeGreeting component (phase 9)
Renders a conversational home-state message as an assistant-turn bubble
when no chat is active, summarising active agents, pending gates,
recent completions, and stale projects via markdown bullets. Replaces
the old Dashboard grid for the Assistant landing route. Exposes a pure
`buildGreetingMarkdown` helper driving the unit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:15:50 +00:00
Nexus Dev
6010a105fe feat(nexus): add useGateIndicator hook (phase 11)
Introduces a minimal pending-gate indicator hook for Phase 11. Reads
the existing approvals endpoint and exposes {hasPendingGates, count,
loading, error} for the IconRail Assistant dot overlay. The rename
from "Approvals" to "Gates" is display-only — approvalsApi and the
underlying /api/approvals endpoint are untouched.

Ships the hook + tests only; the IconRail wiring is deferred to the
controller post-Wave 2 (IconRail is a Phase 8 file).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:15:47 +00:00
Nexus Dev
d2dcb1c813 feat(nexus): add WorkshopCard and WorkshopGrid components (phase 10)
Presentational primitives for the Studio home. WorkshopCard renders a
single workshop with title/subtitle/volt icon and fires onSelect on
click. WorkshopGrid lays them out responsively (1/2/3 columns). No
routing concerns here — the ContentStudio page binds onSelect to
navigate().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:15:14 +00:00
Nexus Dev
397e12a8fd feat(nexus): add useAssistantHomeStatus hook (phase 9)
Aggregates active agents, pending gates, recent completions, and stale
projects from the existing agents, approvals, activity, and projects
APIs into a single shape consumed by AssistantHomeGreeting. The pure
`composeHomeStatus` helper drives the tests; the hook wires it to
react-query and degrades each slice to empty on fetch error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:14:47 +00:00
Nexus Dev
533490f1a2 feat(nexus): add WORKSHOPS data + classifyIntent helper (phase 10)
Introduce the single source-of-truth data structure for the 8 Studio
workshops (diagrams, icons, themes, wallpapers, documents, brand-kits,
social, convert) and a pure keyword-based intent classifier used by
StudioPromptBar to route freeform prompts to the right workshop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:14:17 +00:00
Nexus Dev
2dbf281117 refactor(nexus): apply Task 6 code-quality fixes to Layout.tsx (phase 8)
Three cleanups from the Task 6 code-quality review:

1. Delete dead instanceSettingsTarget state and supporting code.
   The useState, the readRememberedInstanceSettingsPath helper, the
   INSTANCE_SETTINGS_MEMORY_KEY constant, and the localStorage-writing
   effect were all residue from the old footer sidebar buttons that
   Task 6 deleted. Verified via grep that no other file in ui/src
   reads paperclip.lastInstanceSettingsPath.

2. Consolidate two near-identical mobile-nav-visibility effects into
   one. The standalone isMobile-reset effect was a strict subset of
   the scroll-listener effect's early-return branch.

3. Drop onToggleSidebar / onTogglePanel stub callbacks.
   useKeyboardShortcuts declares these as optional and uses optional
   chaining internally; the stubs were "type contract satisfaction"
   the contract did not actually require.

No behavioral change. 38 frame tests still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:31:05 +00:00
Nexus Dev
d87f644cde refactor(nexus): mount new frame in Layout.tsx; kill old chrome (phase 8)
Rewrites Layout.tsx to compose the new Phase 8 frame (IconRail +
TopStrip) and remove the old chrome elements specified as killed in
docs/specs/2026-04-11-nexus-layout-overhaul.md §2:

Removed from chrome:
  - 280px collapsible Sidebar / InstanceSidebar
  - ChatPanel global slide-in right rail
  - PropertiesPanel global slide-in right rail
  - BreadcrumbBar (replaced by ModeBreadcrumb inside TopStrip)
  - Footer row with Docs link, version tooltip, instance settings button,
    chat toggle button, theme toggle button
  - Effect that closed PropertiesPanel when chat opened
  - Mobile sidebar drawer block
  - Mobile sidebar swipe gesture listener

Preserved:
  - Company-prefix URL sync and fallback redirect machinery
  - First-run onboarding trigger
  - WorktreeBanner, DevRestartBanner
  - Scroll-based mobile nav visibility tracking
  - Body overflow management
  - Instance settings path memory
  - Dialog overlays (NewIssue, NewProject, NewGoal, NewAgent)
  - ToastViewport, CommandPalette
  - MobileBottomNav (mobile only; Phase 15 replaces)

Added:
  - IconRail mount with derived companyPrefix from matchedCompany or
    selectedCompany
  - TopStrip mount above the main content area
  - hasUnknownCompanyPrefix fallback defaults to /assistant instead of
    /dashboard (Dashboard is killed in the new IA)
  - useKeyboardShortcuts.onSearch dispatches the same synthetic Meta+K
    keydown as the CmdKButton shim

The Sidebar, InstanceSidebar, BreadcrumbBar, ChatPanel, PropertiesPanel,
ThemeContext, and useChatPanel files remain in the repo; Phase 16
deletes dead files after the other Phase 8 tasks are proven stable.

Pages render unchanged in the new frame and will look visually wrong
until Phases 9-13 rebuild their internals. That is the expected
intermediate state per the spec.

Part of Phase 8 of the Nexus layout overhaul (task 6 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:21:09 +00:00
Nexus Dev
c521ae4403 feat(nexus): add TopStrip composite for layout overhaul (phase 8)
48px sticky top strip per docs/specs/2026-04-11-nexus-layout-overhaul.md
§4.2. Composes ModeBreadcrumb (left) + CmdKButton and GlobalMicButton
(right) inside a <header aria-label="Top bar"> landmark.

Completes the frame component set for Phase 8. The next task (task 6)
rewrites Layout.tsx to mount IconRail + TopStrip as the new global
chrome and delete the old sidebar/ChatPanel/PropertiesPanel/
BreadcrumbBar combination.

Part of Phase 8 of the Nexus layout overhaul (task 5 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:17:09 +00:00
Nexus Dev
bfcdf1f598 feat(nexus): add GlobalMicButton scaffold for layout overhaul (phase 8)
Visual-only mic button for the top strip per
docs/specs/2026-04-11-nexus-layout-overhaul.md §4.2. Renders three
specified states (idle / listening / speaking) but Phase 8 only
wires the idle state functionally. Phase 14 will toggle the state
prop from the voice pipeline without changing this component's
signature.

Uses text-primary/bg-primary for volt (already migrated in phases
1-3) and literal #166534 / #a0a0a0 for forest and silver, which
MIGRATION-PLAN.md §3 proposes as new semantic tokens that have not
yet shipped.

Part of Phase 8 of the Nexus layout overhaul (task 4 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:15:15 +00:00
Nexus Dev
c1647e70d7 feat(nexus): add CmdKButton shim for layout overhaul (phase 8)
Top-strip button that renders the ⌘K glyph and opens the existing
CommandPalette by dispatching a synthetic Meta+K keydown on document,
which CommandPalette.tsx already listens for at its useEffect
(lines 42-51). This is explicitly a Phase 8 shim; Phase 14 of
docs/specs/2026-04-11-nexus-layout-overhaul.md replaces it with a
proper command-palette context when globalizing the palette's
search index.

Part of Phase 8 of the Nexus layout overhaul (task 3 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:12:55 +00:00
Nexus Dev
1de78f855f fix(nexus): tighten settings-route guard in ModeBreadcrumb (phase 8)
Code-quality review for Task 2 caught a startsWith false-positive:
`/instance/settings-foo` was matching the settings branch of
deriveBreadcrumbSegments and producing nonsense output like
["SETTINGS", "-FOO"]. Tightened the guard to require an exact
match or a literal trailing slash before entering the settings
branch.

Added three test cases to lock it in:
  - /instance/settings (exact, no trailing) -> ["SETTINGS"]
  - /instance/settings-foo -> ["HOME"]
  - /instance/settings-foo/bar -> ["HOME"]

22 tests pass. No behavior change for any previously-tested path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:11:06 +00:00
Nexus Dev
bd4e7c5c5d feat(nexus): add ModeBreadcrumb for layout overhaul (phase 8)
Uppercase slash-separated breadcrumb that derives from the current
pathname per docs/specs/2026-04-11-nexus-layout-overhaul.md §4.2.
Leaf segment in text-primary (volt), non-leaf segments in
text-muted-foreground (silver). Pure function deriveBreadcrumbSegments
is exported for unit testing and covers 16 route patterns plus the
catch-all HOME fallback.

The derivation intentionally collapses Phase 11's soon-to-be-demoted
routes (issues/agents/routines/goals/approvals/costs/activity/inbox/
execution-workspaces) under the PROJECTS umbrella segment. When
Phase 11 lands and those routes become /projects/:slug/<tab>, the
derivation will naturally produce PROJECTS / PROJECT-SLUG without
code changes.

Part of Phase 8 of the Nexus layout overhaul (task 2 of 7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:04:31 +00:00
Nexus Dev
9c1525a0f9 refactor(nexus): apply Task 1 code-quality fixes to IconRail (phase 8)
Addresses two Important findings from the Task 1 code-quality review:

1. Swap literal #faff69 hex for the text-primary / bg-primary token
   (4 sites). The --primary CSS variable is already wired to volt via
   MIGRATION-PLAN.md phases 1-3, and 50+ existing files use the token.
   The original Task 1 commit landed on literal hex as a self-
   documenting placeholder; moving to the token aligns with the rest
   of the codebase and makes theme swaps a single-file change.

2. Add focus-visible styles to the Nexus mark link and each
   DestinationLink. Previously the component relied on browser-default
   outline, which on pure-black canvas + volt icons is both off-palette
   and visually weak. Now uses a volt ring with background offset for
   clear keyboard focus indication.

No behavior change. All 7 IconRail tests still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:01:17 +00:00
Nexus Dev
efbd1e451e refactor(nexus): remove redundant MemoryRouter re-export in router wrapper
Spec compliance review for Task 1 flagged that the explicit
`export { MemoryRouter } from "react-router-dom"` added in 332ed47b
is redundant — the line immediately above already does `export *`,
which includes MemoryRouter. The explicit line was cleanup debt from
defensive "just in case" reasoning, not an actual fix.

No behavioral change. IconRail.test.tsx still resolves MemoryRouter
via the export * and all 7 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:56:22 +00:00
Nexus Dev
332ed47bc0 feat(nexus): add icon rail component for layout overhaul phase 8
Introduces the 56px left icon rail specified in
docs/specs/2026-04-11-nexus-layout-overhaul.md §4.1. Four primary
destinations (Assistant, Studio, Projects, Settings) rendered as Lucide
icons with silver default + volt active state and a 2px volt bar on the
right edge of the active item. Destinations are company-prefixed except
Settings, which points at the global /instance/settings/general route.

The Studio icon also highlights on /convert because Phase 10 folds
ConvertPage into Studio as a workshop. The Projects icon is the umbrella
for all Phase 11 per-project-tab routes (issues, agents, routines,
goals, approvals, costs, activity, inbox, execution-workspaces).

The rail is not yet mounted in Layout.tsx — that happens in task 6.

Part of the Nexus v1.7 structural overhaul (Phase 8 of MIGRATION-PLAN.md
§8b). Companion tests cover all 4 destinations, active-state derivation,
and aria-current semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:52:18 +00:00
Nexus Dev
137bd3d0f6 fix(nexus): unblank assistant page on piper-tts import error
The usePiperTts hook imported a non-existent 'tts' namespace from
@mintplex-labs/piper-tts-web@1.0.4. The package exports named
functions (stored, download, predict, etc.) at the top level, not
under a tts namespace. The failing named-import threw at module-link
time, which crashed the lazy chunk for PersonalAssistant.tsx and
left /NEX/assistant blank with only a React error boundary fallback.

Two fixes in one file:

1. Import as namespace:
     import * as tts from "@mintplex-labs/piper-tts-web"
   ESM namespace imports synthesize a 'tts' object whose members are
   the package's named exports, so the existing tts.stored() /
   tts.download() / tts.predict() call sites bind to real functions
   without touching the hook body.

2. Wrap predict() Blob result in URL.createObjectURL() before passing
   to new Audio(). predict() returns Promise<Blob>, not a URL string,
   and Audio() cannot accept a Blob directly. Added a shared cleanup
   callback that revokes the object URL on onended/onerror and in the
   catch path so we don't leak blob URLs on every speak invocation.

Bug 1 was the page-blanking crash at module load. Bug 2 was a latent
runtime crash behind the speak button click handler, surfaced while
the file was already being edited.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 10:35:01 +00:00
Nexus Dev
d478cc3daf feat(nexus): nexus-first navigation and first-run onboarding trigger
Stop showing Paperclip's board UI by default. First-time users now
land on Personal Assistant (v1.5), see a Nexus-first sidebar, and the
NexusOnboardingWizard (built in v1.5) actually fires on first run
instead of sitting behind a dead "Start Onboarding" button click.

App.tsx
  - CompanyRootRedirect now reads useNexusMode() and lands the user
    at /${prefix}/assistant by default. Only project_builder mode
    lands at /${prefix}/dashboard. "personal_ai" and "both" (the
    default) both go to the Assistant.
  - NoCompaniesStartPage gutted: the old "Create your first company"
    button is gone. Single-workspace mode doesn't ask users to name
    workspaces; the onboarding wizard handles it. Replaced with a
    minimal "Setting up your workspace..." loading shim.
  - OnboardingRoutePage now auto-opens the wizard on mount when no
    companies exist. Closes the dead-button gap: previously the user
    had to click "Start Onboarding" to actually get the wizard; now
    the wizard opens itself as soon as they land.

Sidebar.tsx
  - Restructured around two mode-gated sections:
    * Always visible (Nexus essentials): Assistant, Content Studio,
      Convert, Inbox, Skills, Settings. Plus the New Issue button and
      plugin sidebar items.
    * project_builder-only: Work (Issues, Routines, Goals), Projects,
      Agents, and the remaining Workspace items (Org, Costs, Activity).
  - Top bar no longer renders a company switcher dropdown — single-
    workspace mode shows the workspace name as a static label with
    the search button beside it.
  - Dashboard link removed from the always-visible section. The
    default landing is /assistant; users who explicitly want the
    Paperclip dashboard can type the URL or switch to project_builder
    mode.

Layout.tsx
  - Removed both <CompanyRail /> renderings (mobile and desktop
    branches). Single-workspace mode doesn't need a multi-company
    icon rail. Import preserved with a [nexus] comment for upstream
    rebase compat.
  - Onboarding useEffect's authenticated-mode gate removed (root
    cause of the v1.5 wizard-not-firing bug on fresh DB). This
    effect is now a belt-and-suspenders fallback; the real auto-
    trigger lives in OnboardingRoutePage because Layout isn't
    actually mounted during the zero-company first-run state
    (CompanyRootRedirect navigates to /onboarding before Layout
    ever renders).

NexusOnboardingWizard.tsx
  - handleSubmit and handleStartChat both used to hardcode the post-
    creation navigation to /${prefix}/dashboard. Now mode-aware:
    project_builder lands at /dashboard, everything else lands at
    /assistant. Matches the Sidebar and CompanyRootRedirect logic —
    a fresh user never touches the Paperclip dashboard unless they
    explicitly chose project_builder during the wizard.

Not changed:
  - The Paperclip pages themselves (Dashboard, Issues, Projects,
    Agents, Org, etc.) — still present, still accessible by URL,
    still upstream-mergeable. Just hidden from the default nav.
  - CompanyRail.tsx, CompanySwitcher.tsx, NewCompanyDialog — files
    preserved for upstream rebase diff minimization. No call sites
    remain.
  - /NEX/companies route still registered in boardRoutes(), just
    unlinked from the default UI.

TypeScript: zero new errors (pre-existing errors in AgentConfigForm,
command.tsx, useKeyboardShortcuts, usePiperTts, useVadRecorder,
OnboardingSummaryStep.test, PersonalAssistant unchanged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:19:14 +00:00
Nexus Dev
50e91d8732 fix(nexus): register missing board route roots for prefix classifier
The company-aware router helper (applyCompanyPrefix / Link wrapper in
ui/src/lib/router.tsx) uses BOARD_ROUTE_ROOTS to tell the difference
between "this path is under /:companyPrefix/..." and "this is a raw
company prefix like /NEX." When a path segment isn't in BOARD_ROUTE_
ROOTS or GLOBAL_ROUTE_ROOTS, extractCompanyPrefixFromPath assumes
it's a company prefix and applyCompanyPrefix returns the path
unchanged instead of prepending the current company.

Every board route added in v1.5 (Personal Assistant) and v1.7
(Content Generation, Convert) was wired in App.tsx but never added
here. Sidebar's <SidebarNavItem to="/assistant"> therefore rendered
as /assistant (raw), the router treated "assistant" as a company
prefix, no company matched, and the user landed on the "Company not
found" 404 — this is how the /ASSISTANT/company/settings confusion
from the previous debug session was born: after the Layout
auto-recovery fix the broken link now redirects to /NEX/dashboard
but silently eats the Assistant navigation intent.

Added:
  - assistant     (v1.5 Personal Assistant)
  - content-studio (v1.7 Content Generation)
  - convert        (v1.7 Format Conversion)
  - plugins        (board-scoped PluginPage at /:prefix/plugins/:id)
  - tests          (dev-only /tests/ux/runs RunTranscriptUxLab)
  - settings       (LegacySettingsRedirect at /:prefix/settings, which
                    then redirects to /instance/settings/general)

Also added a block comment above the set explaining the invariant
so future additions to App.tsx's boardRoutes() don't drift out of
sync again. Existing company-routes.test.ts passes unchanged
(2/2 green).

Nothing in the sidebar / nav wiring changed — the Nexus v1.5 and
v1.7 pages were always correctly routed in App.tsx. They were just
unreachable via Link clicks because the prefix classifier lied about
what counted as a board route.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:40:53 +00:00
Nexus Dev
3a41ec7b9c feat(nexus): design system phase 3 raw utility sweep
Third phase of the DESIGN.md migration. Removes every raw Tailwind
color palette utility (bg-red-*, text-amber-*, border-blue-*, etc.)
from component source and replaces them with the semantic tokens
introduced in phases 1 and 2.

Scope:
  - 84 files touched under ui/src/
  - ~420 raw palette utility instances replaced
  - 23 hardcoded hex fallbacks replaced with var(--token) refs
  - Zero raw palette utilities remain in component source
    (verified with rg '(bg|text|border|ring)-(red|blue|green|amber|
    yellow|cyan|violet|purple|pink|slate|zinc|neutral|sky|teal|
    emerald|indigo|rose|orange|fuchsia)-[0-9]+' ui/src)

Mapping rules applied:
  - red-* -> destructive
  - amber-/yellow-/orange-* -> warning
  - green-/emerald-* -> success
  - blue-/cyan-/sky-* -> primary (info/in-progress) or muted-foreground
  - slate-/gray-/zinc-/neutral-* -> muted / muted-foreground / border
  - violet-/purple-/pink-/indigo-/rose-/teal-* -> collapsed to
    primary or muted (most were one-off decorative choices, not
    role-bearing). Role-bearing uses go through lib/agent-role-colors
    which was rewritten in phase 2.
  - Opacity modifiers preserved (/10, /15, /20, etc.)
  - dark: variant duplicates removed (theme tokens auto-switch)

Hardcoded hex fallbacks fixed:
  - #6366f1 (indigo) -> var(--primary) / var(--volt)
  - #64748b (slate) -> var(--muted-foreground) / var(--silver)
  - #4f46e5 (indigo) -> var(--primary)
  - #89b4fa (old Catppuccin blue) -> var(--primary) / #faff69
  - OrgChart status dots (#22d3ee/#4ade80/#facc15/#f87171/#a3a3a3)
    -> var(--primary) / var(--success) / var(--warning) /
    var(--destructive) / var(--muted-foreground) per status
  - VoiceWaveform fallback #89b4fa -> #faff69 (volt)

Legitimate hex values left untouched (12 total):
  - lib/color-contrast.ts WCAG reference constants
  - lib/worktree-branding.ts contrast fallback references
  - lib/mention-chips.ts runtime-generated SVG fills
  - context/ThemeContext.tsx theme metadata brand hexes
  - components/ThemeSeedInput.tsx user-facing hex picker

Ambiguous decisions (flagged for visual QA):
  - AgentDetail.tsx invocation-source badges (timer/assignment/
    on_demand) collapsed to primary/muted — visual distinction
    is reduced, labels still differ. Consider chart-role slots
    if differentiation matters.
  - AgentDetail.tsx mixed-opacity amber banners: bg-warning/60
    against new warning base reads heavier than original amber-50
    base.
  - Live-state dots in KanbanBoard/AgentDetail: bg-blue-* ->
    bg-primary — will glow volt in dark mode, probably desirable.

Verification:
  - npx tsc --noEmit in ui/ — zero errors introduced. Pre-existing
    errors in AgentConfigForm, command.tsx, useKeyboardShortcuts,
    usePiperTts, useVadRecorder, PersonalAssistant remain, all
    unrelated to color work.
  - Dev server on :6100 returns 200.

Not changed in this commit:
  - ui/src/lib/company-routes.ts — separate routing fix for broken
    Assistant/ContentStudio/Convert links, committed next.
  - Test files — a few will need assertion updates but are out of
    phase 3 scope.

Phase 4 follow-ups (rounded-xl/2xl collapse, soft shadow removal,
gradient removal) noted in .planning/AUDIT-RADIUS-SHADOWS.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:40:32 +00:00
Nexus Dev
4b8f8178ee feat(nexus): design system phase 2 status and role color dictionaries
Second phase of the DESIGN.md migration. Rewrites the two color
dictionaries that back most status/priority/role indicators across the
app, and adds the controlled "chart palette" exception for agent role
colors per user decision in MIGRATION-PLAN section 9.

status-colors.ts (full rewrite, 109 -> 122 lines)
  - All 5 dictionaries (statusBadge, statusDot, priorityColor,
    issueStatusIcon, agentStatusDot) + 5 defaults rewritten to use
    semantic token classes:
    * done/completed/approved -> bg-success/15 text-success border-success/30
    * error/failed/terminated/rejected -> bg-destructive/15 text-destructive border-destructive/30
    * pending/paused/in_review -> bg-warning/15 text-warning border-warning/30
    * running/in_progress -> bg-primary/15 text-primary border-primary/30
    * idle/planned/backlog/todo -> bg-muted text-muted-foreground border-border
    * blocked -> bg-destructive/10 text-destructive border-destructive/25 (softer)
    * cancelled/archived -> bg-muted/40 text-muted-foreground/70 border-border
    * priority urgent/high/medium/low -> destructive/warning/primary/muted
  - Zero raw palette utilities remain (rg verified).
  - All export identifiers and signatures preserved; 11 caller files
    across ui/src compile unchanged.
  - Note: timed_out was not explicitly mapped in the spec but exists
    in the source; agent chose warning (semantically closer than error).

agent-role-colors.ts (full rewrite, 17 -> 68 lines)
  - Controlled "chart palette" exception: 5 muted desaturated hues,
    passed WCAG AA for all 10 combinations (dark + light), 7/10
    also pass AAA.
  - 11 AgentRole entries cycle through 5 slots via mod-5:
    * slot 1 (volt #faff69 dark / olive #4f5100 light): general, pm
    * slot 2 (teal #6ee7b7 / #0f766e): devops, cto
    * slot 3 (lavender #c4b5fd / violet #6d28d9): designer, cmo
    * slot 4 (amber #fcd34d / #b45309): ceo, cfo, researcher
    * slot 5 (silver #a0a0a0 / gray #6b6b6b): engineer, qa
  - Hue collisions past slot 5 are intentional and documented inline;
    secondary differentiation relies on icons/labels.

index.css
  - Added 5 --chart-role-* vars to :root and .dark (light + dark modes).
  - Mirrored as --color-chart-role-N in @theme inline so
    text-chart-role-1..5 become valid Tailwind utilities.
  - Minimal surgical additions — nothing else touched.

Verification
  - npx tsc --noEmit in ui/: zero errors in modified files. Pre-existing
    errors in unrelated files (AgentConfigForm, command.tsx, etc.)
    remain unchanged.
  - rg '(bg|text|border|ring)-(red|blue|green|amber|yellow|cyan|violet|pink|slate|zinc|neutral|sky|teal|emerald|indigo|rose|orange)-\d'
    on modified files: zero matches.

Test follow-up (out of scope, flagged for next PR)
  - ui/src/lib/agent-role-colors.test.ts asserts each role has a
    "dark:" prefix (no longer true — CSS vars handle dark variants)
    and that all roles have unique colors (no longer true — 11 roles,
    5 slots). Both assertions need rewriting.

Phase 3 follow-ups
  - Sweep agent should verify no component layers raw palette
    utilities on top of dictionary output.
  - Consumers that previously wrapped statusBadge output in their own
    border-* class may now double-border — worth a visual audit.
  - agentStatusDot's animate-pulse modifier is gone except on
    "running" — if any caller expected animation on "active",
    inline handling needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:25:21 +00:00
Nexus Dev
e49144a4e8 feat(nexus): design system phase 1 tokens and inter font
First phase of the DESIGN.md (ClickHouse-inspired) migration. Rewrites
the foundation CSS variables and theme machinery; downstream phases
(status/role dictionaries, raw utility sweep) still pending.

index.css
  - Full rewrite of @theme inline block. Dark (.dark) and light (:root)
    token sets per MIGRATION-PLAN sections 3 and 5:
    * Dark: pure black canvas (#000000), Neon Volt primary (#faff69),
      Forest Green secondary (#166534), charcoal border (rgba(65,65,65,
      0.8)), near-black cards (#141414), silver muted (#a0a0a0).
    * Light: near-white canvas (#fafafa), Forest primary, Volt
      downgraded to dark olive (#4f5100) for border/active use only,
      silver inverted to #6b6b6b. Accessibility fallback, not brand.
  - Added --warning (#f59e0b / #b45309), --success, and direct brand
    token refs (--volt, --volt-pale, --volt-border, --forest, --near-
    black, --hover-gray, --silver, --charcoal-border) exposed as
    Tailwind utilities via --color-* mirrors.
  - Added --destructive: #ef4444 (#dc2626 in light).
  - Radius scale collapsed to 4px sharp / 8px comfortable / 9999px pill.
  - Deleted .theme-tokyo-night.dark block entirely (was dead code —
    ThemeContext never applied the class).
  - Rewrote hljs syntax highlighting: one dark block under .dark .hljs
    using volt for keywords, pale volt for strings, silver for
    comments; one light block under .hljs using forest/dark-olive/
    silver. Replaced all three Catppuccin + Tokyo Night hljs rule sets.
  - Rewrote scrollbar rules to use var(--muted) / var(--charcoal-
    border) / var(--hover-gray) instead of hardcoded oklch values.
  - Added @font-face declarations for Inter (normal + italic) from the
    self-hosted woff2 files at /fonts/InterVariable*.woff2. font-weight
    100-900 range unlocks weight 900 for DESIGN.md hero moments from
    a single variable font.
  - Set --font-sans to Inter-first stack; body rule pulls the token.

ThemeContext.tsx
  - Simplified to binary Theme = "light" | "dark". Dropped "custom"
    theme type, PaletteRole interface, ROLE_TO_TOKEN map, and the
    /api/nexus/settings custom-theme hydration effect.
  - applyTheme() now just toggles .dark on <html> and sets
    colorScheme. applyCustomTheme() left as a deprecated no-op (no
    external callers but keeping the export avoids churn).
  - Legacy localStorage values (catppuccin-mocha, tokyo-night, custom,
    catppuccin-latte) coerced to "dark" on read so existing users
    don't see a crash after the migration.
  - Default theme: "dark".

Layout.tsx
  - Dropped THEME_META import and the THEME_CYCLE map. Theme toggle
    is now a binary sun/moon flip via toggleTheme().

index.html
  - Added <link rel="preload" href="/fonts/InterVariable.woff2"
    as="font" type="font/woff2" crossorigin>.
  - Set inline style="background:#000000; color-scheme:dark;" on
    <html> so the pre-React paint is already dark — no white flash.
  - Boot script coerces legacy localStorage theme values and persists
    "light" or "dark" only.

ui/public/fonts/
  - Added InterVariable.woff2 (344 KB) and InterVariable-Italic.woff2
    (379 KB), both Inter v4.x from rsms.me/inter (the canonical
    upstream). Self-hosted for LAN/offline reliability.

Not changed:
  - lib/status-colors.ts, lib/agent-role-colors.ts — next phase
  - Any component files — phase 3
  - MIGRATION-PLAN.md — will be updated with resolved decisions later

Expected visual state: pages using theme tokens (bg-background,
text-muted-foreground, border-border, ~1,250 instances) immediately
render with the new palette. Pages using raw Tailwind utilities
(bg-red-500, text-amber-600, ~274 instances) still show old colors
until phase 3 sweep.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:19:56 +00:00
Nexus Dev
ab45bc063d fix(nexus): auto-recover from unknown company prefix
When the URL contains a :companyPrefix segment that doesn't match any
fetched company, Layout previously rendered a dead-end NotFoundPage
with no recovery link. The sidebar's company switcher depends on
selectedCompany being non-null, so users landing on a bogus prefix
had no way back without hand-typing a new URL.

Replace the NotFoundPage branch with a redirect to the same path under
the first available company (selectedCompany ?? companies[0]). If the
rest of the path is empty, fall back to /dashboard so the target is
guaranteed to exist. The hasUnknownCompanyPrefix condition is already
gated on companies.length > 0, so the fallback is reachable; the old
NotFoundPage remains as a theoretical safety net if somehow both
selectedCompany and companies[0] are null.

Triggered by a user session after the embedded-postgres wipe: the
browser had a stale localStorage selectedCompanyId and the user
hand-typed URLs with guessed prefixes like /ASSISTANT/company/settings.
Hitting any invalid prefix stranded them on the 404 with no UI to
pick a different company.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:11:17 +00:00
Nexus Dev
3d2117ee9f fix(nexus): auto-bootstrap invite and vite onnxruntime middleware
Zero-terminal first boot. Previously the bootstrap_ceo invite had to be
created via a CLI command (paperclipai auth bootstrap-ceo) and the UI
showed a code block instructing the user to run it. Nexus is meant to
be zero-terminal, so the server now auto-creates the invite on startup
when no instance admin exists and exposes its relative path through
/api/health. BootstrapPendingPage redirects straight to /invite/{token}.
The CLI command is left intact for headless/SSH-only setups.

Invite flow fixes that surfaced during testing:

  - InviteLanding's invite query had default React Query refetch
    behavior. After a successful bootstrap accept, the invite is marked
    accepted server-side, so the refetch returned "not available" and
    shadowed the success screen, making it look like the bootstrap had
    failed when it actually succeeded. Set staleTime: Infinity +
    refetchOnWindowFocus/Mount/Reconnect: false so the first fetch is a
    one-shot snapshot.

  - Reordered the render checks so result?.kind === "bootstrap" / "join"
    are evaluated before the invite-availability error check — defensive
    against any stray refetch that still leaks through.

  - On bootstrap success, window.location.replace("/") lands the new
    admin directly on the board; the "Bootstrap complete" confirmation
    screen is now an unreachable safety net.

Vite onnxruntime middleware replaces the earlier public/ dump. The
previous commit put ort-wasm-simd-threaded.{mjs,wasm} in ui/public/ so
VAD's onnxWASMBasePath: "/" would find them. That works at runtime but
trips vite's dep optimizer: it scans onnxruntime-web, resolves the
dynamic import string to the public asset, and errors with "files in
/public should not be imported from source code." Remove the files and
add a vite plugin (configureServer middleware) that serves the two URLs
straight from node_modules/.pnpm/onnxruntime-web@*/. Runtime keeps
working and the files never enter vite's module graph.

Production build caveat: the middleware only runs in dev. When building
a static dist for production, the wasm files will need a different
mechanism (e.g. generateBundle hook). Not addressed here.

Also bundled (load-bearing for LAN browser testing):

  - ui/src/lib/queryKeys.ts: add missing 'nexus' group. useNexusMode
    referenced queryKeys.nexus.settings since commit 7bb72a5a (Phase
    33-02) but the key was never added. Caused a blank screen crash on
    any page that mounts Sidebar.

  - ctl.sh: read PORT from .env instead of hardcoding 3100, and read it
    once at the top so every subcommand honors it. Fixes the Version /
    Mode showing '?' in status output after the port move to 6100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:50:28 +00:00
Nexus Dev
1c8a26dbb4 feat: Phase 45 — Content as Skills (9 SKILL.md files, Creative group, gap fixes) 2026-04-05 09:57:20 +00:00
Nexus Dev
d25d88d053 feat: Phase 44 — Video & Presentations (Remotion MP4, SSE progress) 2026-04-05 09:57:11 +00:00