Purge remaining hardcoded color values from the component layer and main
process so every surface flows through the kami token system.
src/main/desktop-shell-window.ts
- BrowserWindow.backgroundColor no longer paints pure white before the
renderer mounts. It now picks kami parchment (#F5F4ED) or deep-dark
(#141413) based on nativeTheme.shouldUseDarkColors so the startup
flash respects the user's OS theme preference.
src/shared/constants.ts
- DEPARTMENT_COLORS collapses 12 unrelated hex brand colors (violet,
red, green, cyan, pink, ...) into six kami tokens: var(--primary),
var(--kami-ink), var(--kami-dark-warm), var(--kami-charcoal),
var(--kami-olive), var(--kami-stone). Departments are distinguished
by warm-neutral ramp + ink-blue, never by second chromatic hue.
src/renderer/components/communication/{MessageItem,SearchResults}.tsx
- Replace the string-concat tint pattern `${color}18` with
color-mix(in oklab, ${color}, transparent 91%). The old pattern
produced invalid CSS (e.g. `var(--primary)18`) whenever `color`
was a CSS custom property, silently falling back to no background.
color-mix works for both hex and var() inputs, eliminating the
latent bug at the source.
src/renderer/components/ui/ActionButton.tsx
- Replace hand-rolled rgba(74,93,122,0.35) and rgba(220,53,69,0.3)
drop shadows with kami ring shadows routed through color-mix over
var(--primary) and var(--destructive).
src/renderer/views/CommandPaletteView.tsx
- Replace rgba(15,23,42,0.08) cold-slate shadow with a kami whisper
shadow driven by color-mix over var(--foreground).
src/renderer/components/skills/SkillAgentToggles.tsx
src/renderer/components/execution/ModelManagementShared.tsx
- Toggle thumbs switch from bg-white to bg-background so they
inherit parchment in light mode and deep-dark in dark mode,
removing the last pure-white surfaces from the renderer.
Third-party brand hex values in src/renderer/components/social/
social-config.ts are retained: they represent external brand identity
(Twitter blue, YouTube red, ...), not kami chrome, and collapsing them
to one hue would destroy recognition.
Typecheck clean. No business-logic signatures changed.
One flat list of `status = in_review` tasks couldn't tell the
operator whether the next decision belonged to the board (approval
pending), to the human user (top-level assignee or explicit
`requires_user_review`), or to a middle manager walking up the
`reports_to` chain. Three very different escalation paths rendered
identically, so "reviewed by X" was the only signal and operators
missed the ones actually waiting on them.
`PendingReviewList` now classifies each in-review task into one of
three kinds (`board` / `user` / `manager`) using the pending
approval set plus the existing assignee/manager lookup. Rendering
groups them under labelled sub-headings in priority order (board
first — the operator is the bottleneck there), each with its own
awaiting-text so the row's status is unambiguous at a glance.
The component accepts approvals via prop; the caller passes the
existing `pendingApprovals` it already holds. Translations for both
locales carry the new group labels.
Full-codebase ts-prune + manual audit sweep. Every change below is a pure
deletion (or a 2-line re-wire where a dead function was supposed to be
alive). No scope additions.
Dead wrappers / phantom features
- shouldAutoApproveApproval: zero-value wrapper over shouldAutoApprove.
Four call sites now import shouldAutoApprove from execution-policy
directly.
- evaluateRotation(session, connectorId, thresholdsOverride): the
connectorId arg was never read (underscored), and thresholdsOverride
was only exercised by the test suite — no production caller. Signature
collapses to evaluateRotation(session). DEFAULT_THRESHOLDS /
RotationThresholds exports and their tests go with it.
- installLogRedactor: defensive console wrapper that was never wired
from worker-entry. buildRedactor handles the known threat (env-value
leaks); the pattern-based redactTokens stays for tests and future
composition.
Dead files
- resume-interrupted-tasks.ts: helper function never imported.
- service-graph-orchestration-deps.ts: three factory closures
superseded by the inline closures in create-orchestration-service.
Dead exports
- listConnectorsByAuthKind (shared/connector-catalog)
- getLocalePromptInstruction + whole localization.ts file
- ImportantDocType
- main-side ConnectorAuthKind (duplicated the shared one; not referenced)
- CheckpointEngineService, RunHousekeepingService types
- WorkspaceResolution type
- cmdOrCtrlSymbol
- AgentEventPush, CredentialPayload type aliases
- parseApprovalPayloadJson, parseEnvValue, ProviderCard
- getSOP — buildSOPSection reads the SOPS map directly; the individual
getter had no callers. Deduped to a flat array.
- renderWakeReason in wake-reasons/en.ts: byte-for-byte duplicate of the
one in wake-reasons/index.ts.
Forgotten wiring (not cleanup, but adjacent)
- registerAgentStandupRoute: the GET /api/agents/me/standup endpoint is
advertised in every system prompt and wake-reason doc, but the route
handler was never registered in api-server/index.ts. Agents have been
hitting 404 since Pi Mono. Registered.
Dead DB column
- v44 migration drops runs.context_snapshot_json. Column was added by
v16-v25, never written or read, and v43's deliberate preservation
was an interim measure. Same idempotent table-rebuild pattern as
v41/v43.
Test tree
- Phantom-feature threshold override tests removed (3). The rest of
the rotation suite re-expressed against the real maxRuns=20 floor.
495/495 unit tests pass. typecheck + build clean.
Onboarding was a 7-step linear wizard, same information hierarchy as
Settings but behaving like a modal. Replace with the SettingsShell tab
pattern: one row of six tabs (nodecheck / providers / company /
modelpool / executionmode / mission), free navigation, per-tab
completion ticks derived from the wizard's own validation rules.
Sub-fixes on the same pass:
- ActionButton: add whitespace-nowrap + shrink-0 so "选择" never
stacks vertically on narrow containers. Global — every caller gets
the correct behavior
- CompanyStep: CEO Runtime Provider/Model go vertical (space-y-3
instead of grid-cols-2) so long names like "Fireworks AI" /
"Kimi K2.5 Turbo Preview 0911" stay readable; workspace-path Input
wrapped in min-w-0 flex-1 container so it yields, not the button
- MarkdownBody: render react-markdown in chat bubbles (was plain text)
i18n: added onboarding.stepsAria for the tablist label.
Profile path and secret backend are **read-only diagnostic facts** —
they tell you which directory the profile lives in and which keystore
ciphertexts are stored against. They are not preferences; they do not
belong in the General tab alongside the theme / language / auto-update
controls.
Moved both into a new "Profile" group inside the Data tab, rendered
via a small `ReadOnlyRow` helper so both pieces of information share
consistent bordered-card styling and get a clear label/value split.
General tab now contains only the three actual preferences (theme,
language, updater) + the inline updater hint.
The previous General rendering dropped profile path as raw mono text
under the updater checkbox — visually tangled with the preference
control. With diagnostics in Data and preferences in General, each
tab reads with a single purpose.
No new i18n keys required — reused `exec.profile` / `exec.profilePath`
/ `exec.secretBackend`, which were already translated in zh + en.
Follow-up polish; no behavioral change. Typecheck + lint pass.
The settings page was a single vertical scroll containing seven
unrelated concerns. Users had to scroll past theme preferences to
reach Providers — which is the most-touched surface — and mode +
secrets + shortcuts lived below that. There was no signal about
where anything was.
Introduced `SettingsShell` with a horizontal tab row along the top:
General | Execution mode | Providers | Shortcuts | Data | Secrets
Each tab swaps the content pane. Active tab is visually anchored
with a 2px primary underline + foreground text; inactive tabs stay
muted with a hover promotion. All tabs are real `role="tab"` +
`aria-selected` so screen readers announce the current pane; the
nav has `aria-label="Settings categories"`.
Tab state is local — settings are ephemeral navigation, no reason
to persist across company switches or app restarts. Default is
"general" because that's where first-time users land.
Refactor consequences:
- `OperationsSections.tsx` now renders `<SettingsShell />` + the
OAuth prompt modal portal; the old double-Panel wrapper with
"Profile and release" + "Providers" titles is gone. The "Profile
and release" label always fought with what was actually inside
the pane — Jobs audit item S-S2 addressed.
- Providers moved from a separate sibling Panel into the main IA.
Previously Providers came AFTER a 7-section scroll; now it is a
peer of General / Mode / etc., reachable in one click.
- Deleted the now-unused `SettingsPane.tsx` orchestrator. Its
single-column rendering survived only as a composition step that
SettingsShell replaces.
Added i18n keys (zh + en):
settings.tabs.general / mode / providers / shortcuts / data /
secrets; settings.tabsAria.
Covers J-S2 / J-S7 / S-S5 / S-S7 from the settings audit.
The previous SettingsPane.tsx hid 8 unrelated concerns behind 73 "lines"
of JSX where each line ran 400-2000 chars. Reading the file was
infeasible; editing it meant scrolling horizontally through a single
statement spanning every button, modal, and table row.
Split by concern into `settings-sections/`:
SettingsPane.tsx 62 LOC orchestrator
GeneralSection.tsx 94 LOC theme / language / auto-update
ExecutionModeSection.tsx 105 LOC AI CEO ↔ AI Co-Founder toggle
ShortcutsSection.tsx 40 LOC keyboard reference (read-only)
DataSection.tsx 123 LOC update check / backup / restore /
secret backend
SecretsSection.tsx 102 LOC per-company secret bindings
Every file now reads top-to-bottom with normal indentation.
Behavioral changes bundled (they belong with the restructure, not a
separate pass):
- Preferences save model unified: theme already auto-saved on change;
language and auto-update checkbox now do the same. The global
"Save settings" button is gone — there is no longer a state where
a toggled language sits unpersisted waiting for a second click.
- Execution mode card gained a subheader line that names the current
company ("Applies to current company: X"). Multi-company users can
no longer confuse which company they just flipped.
- Auto-updater checkbox gained an inline hint: "Check for application
updates at launch" — the previous naked checkbox never explained
what happens when it is off.
- Restore profile moved into DataSection's backup/restore group,
given `tone="danger"`, and gated behind a `window.confirm` with
copy warning about running work being lost. Backup stays neutral.
The previous layout put Restore in the same 2-col grid as Save +
Check Updates + Backup, making a profile-replacing operation look
as routine as saving a theme preference.
- Secret backend display promoted from a naked text line to a
bordered card so it matches the surrounding section rhythm.
Added i18n keys (zh + en):
settings.updaterHint / settings.modeScope / settings.backupRestore /
settings.restoreConfirm / settings.restoreHint.
Covers J-S1 / J-S6 / S-S1 / S-S2 / S-S3 / S-S4 from the settings audit.
Remaining work (tab sidebar, Providers IA reordering, copy sweep)
lands in follow-up commits.
Onboarding ProvidersStep now reads the 26-entry CONNECTOR_CATALOG and
renders featured connectors (claude-pro, openai-codex, anthropic, openai,
amazon-bedrock) up front with a collapsible advanced list for the
remaining 21. Each auth kind gets a dedicated card component: OAuth
button flow, inline API-key form, and a cloud deep-link to Settings.
Delete legacy Claude/Codex/Gemini hardcoded cards and the compatible-API
slot rotation. Swap ModelManagement Settings panel for the new
ProvidersPanel driven by credentialsList IPC.
Create src/renderer/components/ui/icons.ts as the single re-export point
for every Phosphor icon the renderer touches, then rewrite the 95 files
that previously imported directly from "@phosphor-icons/react" to pull
from the relative-path-to-barrel instead. The TitleBar commit earlier
in this series already used ui/icons; this commit extends the pattern
to the rest of the renderer.
Benefits now wired in:
- one location where bundle tree-shaking can be reasoned about,
- typos like Briefcase vs BriefCase show up against a finite list,
- re-theming or swapping icon sets is a one-file edit,
- a lint rule that flags raw @phosphor-icons/react imports can be
added later without mass-rewriting call sites.
The barrel also re-exports the `Icon` type so call sites that type
icon props against the Phosphor component type work unchanged.
Test count stays at 363 passing; typecheck stays at 156 errors.
- Replace all bg-[color:var(--old)] with bg-background/bg-card/bg-muted/bg-primary etc.
- Replace all text-[color:var(--old)] with text-foreground/text-muted-foreground/text-primary etc.
- Replace all border-[color:var(--line)] with border-border
- Replace all inline var(--old) with var(--new) equivalents
- Replace all rounded-[6px]/rounded-[8px] with rounded-lg
- Replace .surface class with bg-card border border-border shadow-sm
- Replace .focus-ring class with focus-visible Tailwind utilities
- Replace .mono class with font-mono
- Replace data-theme attribute with class-based dark mode
- 99 files updated, ~1265 lines changed, zero old references remaining
- BudgetSummary shows policy-level utilization bars when companyId provided, falls back to agent-level
- BudgetAlertBanner loads open incidents for policy-based alerts (warn_threshold/hard_stop), falls back to agent thresholds
- AgentEditor reads budget from budget_policies and writes budget as policy on save (D-11)
- Dashboard passes companyId through to BudgetAlertBanner
- Create PolicyList with utilization bars, edit/delete, empty state
- Create PolicyFormModal with scope selector, dollar amount, warn/hard-stop toggles
- Create IncidentList with status filtering and manual resolve action
- Add tabbed navigation (Cost Entries | Policies | Incidents) to costs section
- Export new components from execution barrel file
- Add deliverable review gate: pause workflow when agents produce important
documents (PRDs, design docs, etc.) for human review before continuing
- Add company-level reviewDeliverables setting with onboarding card selector
- Block agent heartbeats when deliverable review is pending (agent + CEO)
- Trigger review at document creation time in API server for reliability
- Fix autoApproveHires vs reviewDeliverables conflict in approval-policy
- Fix app quit: force-exit on all platforms (macOS Dock, Windows tray, Linux)
- Add multi-platform CI/CD pipeline (macOS arm64/x64, Windows x64/arm64,
Linux x64/arm64 with deb/AppImage/rpm/snap) with code signing support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create SocialDraftPreview with platform icon, color, content, char count, media URLs, schedule display
- Integrate SocialDraftPreview into ApprovalInspector for social_post approvals
- Add schedule picker with immediate/scheduled radio group and datetime-local input
- Add social_post to type badge with accent styling
- Approve button shows 'Approve & Post' for social_post approvals
- Accessibility: role=region, aria-label, aria-live=polite, role=radiogroup
- Fix test type annotation for TS compatibility
- Install @tanstack/react-virtual for virtualized list rendering
- Create useRovingTabIndex hook in src/renderer/lib/keyboard.ts for arrow-key navigation
- Add virtualization to 6 lists: TaskQueue, ApprovalList, ActivityList, CostList, AgentRoster, MessageList
- MessageList uses measureElement for variable-height message items with auto-scroll
- All 6 lists have role="listbox" with role="option" on rows for accessibility
- All 6 lists support ArrowDown/ArrowUp/j/k/Home/End keyboard navigation
- Add 8 unit tests for useRovingTabIndex covering all key bindings and edge cases