Commit Graph
110 Commits
Author SHA1 Message Date
ZacharyZhang-NY e87170f105 refactor(ui): render explicit CaretDown on Select; normalize affordance across platforms
Native <select> chrome varies: macOS renders a dual-arrow indicator,
Windows and Linux render a single caret, and on dark themes the
native caret can drop to near-invisible contrast. The agent Edit
modal (image 5 in the audit) had five side-by-side Selects where the
indicator read as a subtle decoration, making the field look closer
to a filled-in text input than a pick-from-list.

Switched Select to:
- `appearance-none` to strip native chrome
- Phosphor CaretDown rendered absolutely on the right, bold weight,
  muted-foreground so it reads against both card and background
- Padding-right increased to reserve space so long option labels
  do not overlap the caret
- Opacity drop when disabled so the affordance tracks the field state

Scope: only the shared Select primitive; every caller inherits the
fix automatically. No API change.

Covers J10.
2026-04-17 19:58:42 -04:00
ZacharyZhang-NY 36bed1b0f1 feat(team): add TeamHeaderSummary strip + delete unreachable orgchart section
Two S-level audit items landed together because they are the same
"first-second orientation" concern on the team page:

S1 — Empty OrgChart was the first thing the board saw; the canvas
     carried no summary data. Now a compact summary strip sits above
     the chart showing Members / Running / Attention / Pending hires /
     This-month budget (spent/cap/%). All five numbers computed from
     the existing snapshot — no extra IPC.
S9 — Activity and budget context previously required visiting three
     separate pages. The summary strip collapses the minimum-useful
     subset into a single glance; the full drill-down paths (Work,
     Approvals, Costs) remain one click away.

S10 — The `orgchart` SectionId was a dead rendering branch:
       - Not in the sidebar (useUIController sidebarGroups)
       - Not in the command palette (paletteOnlyNavDefs)
       - Not reachable from any navigateTo() call in renderer code
     The only callers were an automation post-hire notification and a
     unit test — both pointed at "orgchart" as a navigation target.
     Routed both to "agents" instead (which renders the OrgChart at
     the top anyway, plus the new summary strip and the roster), so
     clicking the "Agent Hired" notification now lands on the team
     page with the new hire highlighted.

Removed:
- `orgchart` from SectionId enum (src/shared/types/enums.ts)
- `section === "orgchart"` render branch (OrganizationSections.tsx)
- `nav.orgchart` i18n key (zh + en)

Added:
- src/renderer/components/organization/TeamHeaderSummary.tsx
- team.summary.* i18n keys (zh + en)

Covers S1 / S9 / S10.
2026-04-17 19:57:58 -04:00
ZacharyZhang-NY 9981ee342b refactor(agent-form): split EditFormBody into 5 semantic sections + add Cancel/dirty-state + always-visible Thinking Effort + binary OrgChart hierarchy
Previously the agent Edit modal was a 351-LOC wall of 17 stacked form
fields with no visual grouping, mixed header styles between the
skills heading and the field labels, a Save button that was always
enabled, no Cancel affordance, and a Thinking Effort field that
silently disappeared when the selected connector did not support it.

This commit restructures the edit surface into five named sections
— Identity, Runtime, Budget & scope, Behavior, Skills — each with a
consistent SectionHeader (11px uppercase muted, border-top divider).
Every section lives in its own ≤150-LOC file under
agent-form-fields/edit-sections/ so nothing stays god-sized:

  EditFormBody.tsx          199 LOC (orchestrator: isDirty, handlers)
  IdentitySection.tsx        74 LOC
  RuntimeSection.tsx        114 LOC
  ConstraintsSection.tsx     58 LOC
  SkillsSection.tsx          36 LOC
  EditActionBar.tsx          87 LOC
  SectionHeader.tsx          18 LOC

Behavioral changes:

- Save is disabled until the form is dirty (trimmed-string + numeric
  budget comparison against the persisted agent record). The board
  sees whether changes are captured without clicking Save to find out.
- Cancel button added, wired through AgentForm → AgentEditor →
  OrganizationSections, which clears selectedAgentId (same as the
  modal's close X — a proper bottom action avoids users reaching for
  the corner).
- Thinking Effort is always shown. When the connector provider does
  not accept the hint the Select is disabled with an inline note,
  so the option is discoverable instead of silently hidden.
- Budget shows a live BudgetBar + "Spent $X of $Y" line when the
  budget or spend is non-zero. The raw number input on its own left
  burn state invisible.
- Capabilities placeholder replaced with a concrete example
  ("writes React components, reviews PRs, ...") — the previous
  "What this agent does" was a circular prompt.
- OrgChart role bar went from three tiers (CEO 4px primary /
  manager 3px warning / IC 2px muted 30%) to binary (root 4px
  primary / everyone else 2px border). Orange-warning for managers
  read as an alert, not a rank; managerial position is already
  encoded in the tree's topology.

Also added i18n keys (zh + en):
  agent.form.sectionIdentity / sectionRuntime / sectionConstraints /
  sectionBehavior / effortNotSupported / budgetBurn; updated
  capabilitiesPlaceholder copy.

Covers J2 / J7 / J8 / S5 / S6 / S7 / S8.
2026-04-17 19:50:53 -04:00
ZacharyZhang-NY 62d0964af3 feat(org): add OrgChart status legend + CEO delete/terminate guards
Two audit items addressed together because they share the same org-page
render surface and both enforce "read the screen correctly before acting":

1) Status dot colors in OrgChart were undocumented — users saw five
   distinct colors (running/active/idle/paused/error/terminated/
   pending_approval) with no key. Added a compact top-left legend that
   only lists the statuses actually present in the current org, so a
   healthy all-running company shows two rows, not seven. Label copy
   reuses existing agent.form.status* keys (zh/en).

2) The agent Edit modal offered Delete and Status=terminated on every
   agent, including the root (no reportsTo) — the CEO. Deleting or
   terminating a root orphans every direct report. Added `isRootAgent`
   check in EditFormBody:
   - Delete button: disabled, with `rootDeleteBlocked` inline note
     telling the user to reassign reports first.
   - Status select: `terminated` option hidden; `rootTerminateBlocked`
     note explains why.

Scope note: this is UI-level guardrail only. The deleteAgent IPC /
API route is unchanged — same-company power users with DB access can
still do surgical fixes; the board-facing UX just refuses to be a
footgun. Covers J5 / S3 / S4.
2026-04-17 19:45:32 -04:00
ZacharyZhang-NY 50a7196b3e refactor(org): drop legacy adapterLabels / claude_compat fallback; unify connector label resolution
OrgChart and AgentRoster both carried a `claude_compat`-prefix fallback
chained against `adapterLabels[connectorId] ?? connectorId` to paper
over the pre-pi-mono connector id space (claude_local / codex_local /
gemini_local / claude_compat_2..10). The migration to pi-mono's catalog
retired those ids months ago — the fallback is dead code that still
runs `String.prototype.startsWith` on every render and forks the label
resolution into two subtly different shapes in two files.

Replaced with `formatConnectorLabel(connector, fallbackId)` in
lib/formatters.ts, used by both OrgChart and AgentRoster.

Also in this commit (typography polish bundled because touching the
same render paths):

- OrgChart connector label: 9px uppercase tracking-0.08em → 11px
  tracking-tight. The 9px chip was unreadable at default zoom.
- OrgChart zoom controls: `+` / `−` / "Fit" mixed text + character ops
  replaced with Plus / Minus / CornersOut icons (imported from the
  existing icon barrel) + a focus-visible ring for keyboard users.

No rendering changes for the non-cosmetic path (status dots, card
layout, edges). The removed `adapterLabels` map was exported from
orgTreeLayout.ts and had no other importers.

Covers J1 / J3 / J4 / J6 from the team-page audit.
2026-04-17 19:43:12 -04:00
ZacharyZhang-NY 00b0e0aaed refactor(ui+skills): split import.ts + primitives.tsx god files
Two more > 400 LOC modules split along responsibility seams. Zero
behavior change — public APIs re-export from the original file paths,
so every consumer import stays working.

| Original                              | Split into                    | LOC each |
|---------------------------------------|-------------------------------|----------|
| src/main/skills/import.ts (458)       | import (orchestrator) /       | 132      |
|                                       | discovery (findSkillMdFiles,  | 153      |
|                                       | buildLocalInventory)          |          |
|                                       | materialize                   | 24       |
|                                       | import-local                  | 147      |
|                                       | import-url                    | 85       |
| src/renderer/components/ui/           | primitives (barrel re-export) | 11       |
| primitives.tsx (419)                  | Card / Eyebrow / Badge /      | 9–59     |
|                                       | Spinner / Panel / Field /     | each     |
|                                       | Input / Select / TextArea /   |          |
|                                       | ActionButton / SectionButton  |          |

`createSkillImporter` still returns `{ importFromSource, importFromGitHub,
importFromLocal, importFromUrl, cleanupSkillFiles, parseGitHubUrl,
findSkillMdFiles, buildLocalInventory }` — every existing consumer
resolves unchanged.

`primitives.tsx` is now a pure barrel re-exporting the 11 primitives.
Consumers can migrate to direct imports (`../ui/Card`) opportunistically
but the barrel path keeps working.
2026-04-17 16:35:30 -04:00
ZacharyZhang-NY f012f8e880 refactor(skills): drop company_skills.markdown column, read from disk via IPC
`company_skills.markdown` stored the full SKILL.md body. The on-disk
file under `profile/skills/{companyId}/{slug}/SKILL.md` is what pi-
coding-agent actually reads via `loadSkills` at runtime. The DB column
was a duplicate — import wrote to both, renderer read from DB. Any
discrepancy between disk and DB was invisible until the agent behaved
differently from what the user saw in SkillDetail.

Disk becomes the single source of truth. DB keeps name / description /
metadata / source info / file inventory — the cheap lookup row. UI reads
SKILL.md content live via a new IPC channel.

## Migration v37 (rebuild company_skills without markdown)

Table-rebuild pattern matching v35/v36: `fn:` form, PRAGMA walk for
drift columns, row-count assertion before DROP, `defer_foreign_keys`
for the `agent_skill_assignments(skill_id)` reference. Indexes
recreated. Idempotent via `schema_migrations` ledger.

Existing DB rows' markdown content is discarded — but the on-disk
SKILL.md files are the authoritative copy, so no user-visible data
loss. Any agent using those skills keeps working; the UI just starts
reading from disk instead of the DB cache.

## Code changes

- `src/main/database/skills.ts`: drop `markdown` from read mapping,
  upsert input type, INSERT column/bind list, ON CONFLICT DO UPDATE.
- `src/shared/types/records-core.ts`: `SkillRecord.markdown` removed.
- `src/main/skills/import.ts` + `import-github.ts`: drop `markdown:`
  from 4 `saveCompanySkill` call sites. Disk-write via
  `materializeSkillFiles` unchanged (it's what the file agent reads).
- `src/main/skills/index.ts`: expose `skillFilesDir` for the new IPC
  handler to resolve `${profile}/skills/${companyId}/${slug}/SKILL.md`.
- `src/shared/contracts/channels.ts` + `schemas-execution.ts` +
  `desktop-api/skills.ts`: new `getSkillMarkdown` channel
  `{ companyId, slug } → DesktopResult<string>`.
- `src/preload/index.ts`: expose `getSkillMarkdown(companyId, slug)`.
- `src/main/ipc-skill-handlers.ts`: handler validates schema, ensures
  company, resolves path, `readFile`, returns content. Fails closed
  with `SKILL_NOT_FOUND` on ENOENT, `SKILL_READ_FAILED` on other
  read errors.
- `src/renderer/components/skills/SkillDetail.tsx`: `useEffect` fetches
  markdown via IPC when the skill detail loads; shows "Loading…" until
  resolved, surfaces read errors. Preserves `stripFrontmatter`.
- Tests: drop `markdown:` from 2 fixture sites
  (`skill-types.test.ts`, `skill-aware-assignment.spec.ts`).

## Verification

- typecheck / lint / 578 tests green
- `grep 'skill\.markdown' src/` → 0 DB-record reads
- `grep '\.markdown\b' src/main/database/skills.ts` → 0
- Remaining `.markdown` references are all `ghSkill.markdown` (GitHub
  fetcher's intermediate struct feeding `materializeSkillFiles` to
  disk) and parameter names in `stripFrontmatter(markdown: string)` /
  fs-writer helpers — none read from a DB row.
2026-04-17 16:22:31 -04:00
ZacharyZhang-NY 7e1d4199ee refactor(schema): make runs.task_id nullable, delete heartbeat workaround
The NOT NULL constraint forced heartbeat runs to synthesize a fake task
row (`[Heartbeat] X — Check for work`) so the FK could resolve. Three
layers of code — acquireHeartbeatTask, findReusableHeartbeatTask,
cleanupDuplicateHeartbeatTasks, plus the is_heartbeat flag column + UI
filter — existed to paper over one bad constraint. Remove the
constraint and the whole workaround vanishes.

SQLite table rebuild with data preservation. Live production DB, no
backup — migration is atomic, idempotent, and verified before DROP.

## Migration v35 (rebuild runs with nullable task_id)

Uses `fn:` form so it can:
- Snapshot `COUNT(*) FROM runs` before copy
- Walk `PRAGMA table_info(runs)` to detect drift columns (v34 declared
  a column set; any additive ALTER since v14 shows up here)
- Assert `COUNT(*) FROM runs_new == beforeCount` before `DROP TABLE runs`
- `PRAGMA defer_foreign_keys = ON` so run_events(run_id) FK defers to
  commit time, by which runs has the same id set under the new name
- Recreate all 6 indexes on the new table

If the assertion fails, the migration throws inside the existing
`transaction()` wrapper. SQLite rolls back the whole migration, old
`runs` table is untouched, `schema_migrations` never records v35. Next
app start retries cleanly.

## Migration v36 (drop tasks.is_heartbeat + synthetic rows)

Runs now carry `task_id = NULL` for heartbeat dispatches. The
`is_heartbeat` flag is dead data going forward.

1. `UPDATE runs SET task_id = NULL WHERE task_id IN (SELECT id FROM tasks WHERE is_heartbeat = 1)`
2. `DELETE FROM tasks WHERE is_heartbeat = 1` (synthetic rows, not real work)
3. Rebuild `tasks` without the `is_heartbeat` column (same defer-FK +
   row-count-assert pattern as v35)

## Code cleanup

- `RunRecord.taskId: string | null`; `createRun` / `cancelRunForDeletion`
  / `finishRun` guard every `UPDATE tasks` path with a null-check.
- `TaskRecord.isHeartbeat` removed; every SELECT / filter / UI reference
  dropped.
- Delete `acquireHeartbeatTask` / `findReusableHeartbeatTask` /
  `cleanupDuplicateHeartbeatTasks` — dead after the constraint lifts.
- Delete `src/main/orchestration/heartbeat-task.ts` (pure workaround
  constant file).
- `heartbeat.ts` creates runs with `taskId: null` when the agent has no
  assigned work. Turn context already supports nullable task
  (heartbeatFallbackSection); prompt surface unchanged.
- `validateTaskCreation` drops the `isHeartbeat` parameter — user-
  authored + automation-authored tasks are never heartbeat.
- Renderer: `run.taskId ? taskMap.get(...) : undefined` in Dashboard /
  RunCenter / RunDetail / RunQueue.
- Delete `tests/unit/heartbeat-coalescing.test.ts` (12 tests that
  exercised the deleted DB methods).
- Drop 1 isHeartbeat-bypass test from `task-quality-gates.test.ts`.

## Grep verification

- `isHeartbeatTaskTitle src/` → 0
- `acquireHeartbeatTask src/` → migration comment only
- `is_heartbeat src/` → schema.ts migration history only
- `isHeartbeat src/` → 0

Tests: 578/578 (was 591 — delta is 13 tests that tested deleted code).
2026-04-17 16:16:08 -04:00
ZacharyZhang-NY 5547c29491 update 2026-04-17 14:03:56 -04:00
ZacharyZhang-NY 7fc707ab8c fix(onboarding): stabilize connector-model state so native <select> stays open
The CEO-runtime model dropdown (and the team-model pool) collapsed the
instant a user clicked to open it whenever the app received a background
snapshot refresh. Root cause:

  useEffect(executionReadyConnectors, ...)
    → loadModels() loads catalog for every ready connector
    → setConnectorModelsById(Object.fromEntries(entries))
      — always a brand-new object reference, even if the catalog
        returned from the IPC is byte-identical to the previous load
    → connectorModels useMemo picks up a new reference
    → modelOptions useMemo picks up a new reference
    → <Select> re-renders its options children
    → Chromium's native <select> treats DOM mutation of <option>
      children as "popup stale" and closes the dropdown.

Because snapshot refreshes fire on every domain-changed event
(credentials-changed, run-status, heartbeat-status, etc.), the user
effectively could not hold the dropdown open long enough to pick a
model.

Fix: structurally compare the freshly-loaded catalog against the
previous state and bail out of setState when the shape is identical
(same connector ids, same per-connector model lists in order, same
id/label/enabled flags). Same guard on the error-path reset so an
already-empty map does not trigger a re-render either.

typecheck 0 / lint 0-0 / test:unit 508/508 / build 0-0.
2026-04-17 10:28:19 -04:00
ZacharyZhang-NY 9b45b12508 fix(onboarding): drop "Default" sentinel from model picker; auto-select real model
The CEO-runtime model dropdown on the Company onboarding step rendered
a {value:"", label:"默认 / Default"} sentinel as the first option, a
holdover from the adapter era when "" meant "let the connector pick".
Under pi-mono every connector carries a defaultModel and every run
must bind a concrete model id, so the sentinel is now misleading —
picking it left selectedModel empty and the wizard would advance
without a valid model.

- CompanyStep: drop the sentinel from the options array; the dropdown
  now shows only real models (or a single "Loading..." entry while
  the per-connector fetch is in flight).
- useConnectorModelSelection: when the connector's current model is
  not enabled, auto-select the connector's defaultModel if enabled,
  otherwise the first enabled model. An empty enabled list still
  falls back to "" so the CompanyStep hint UI behaves correctly.

Result: Fire Pass now shows "Kimi K2.5 Turbo (Fire Pass)" as the
sole option (pre-selected). Every other connector picks its catalog
default up-front.

typecheck 0 / lint 0-0 / build 0-0 / test:unit 508/508.
2026-04-17 09:54:46 -04:00
ZacharyZhang-NY e94939a562 test(connector-id): migrate fixtures and assertions to pi-mono ids
Replaces legacy adapter ids (codex_local / claude_local / gemini_local /
claude_compatible) across ~42 test files with pi-mono equivalents:
- codex_local → openai-codex
- claude_local → claude-pro
- gemini_local → gemini-cli
- claude_compatible → anthropic

Hand-rewrites where fixture semantics changed:
- connector-matching: TASK_TYPE_PREFERENCE ordering rebased (claude-pro
  first for code/content/management; google first for research)
- connector-model-policy: getModels(provider) from pi-ai instead of the
  deleted adapter.module.models array
- session-management + edge-case-session: adapter exemption removed;
  rotation now uniform under pi-mono
- ipc-core-admin-handlers: CSV compat model replaced with pi-ai anthropic
- connectors.spec: lookup by openai-codex id (catalog now starts with claude-pro)
- onboarding-model-pool: listProviderModels(provider) helper; label text
  changed to "ChatGPT Plus/Pro (Codex)"
- database integration: added vault insecure-fallback env + electron mock
- command-paths: uses new @main/connector-helpers normalizeCommandValue
- pi-mono-migrations: added `as string` casts for 3 negative-existence
  assertions verifying legacy ids were removed

Drops:
- pnpm-workspace.yaml packages: [] (was "packages/*"; directory empty)
- obsolete CONNECTOR_STRENGTHS import; .module.models probing

Verification:
- pnpm typecheck → 0 errors
- pnpm lint → 0 errors / 0 warnings
- pnpm build:verify → 0 errors / 0 warnings
- pnpm test:unit → 508/508 pass
- pnpm test:integration → 96/96 pass
- All 6 adapter hard gates pass (NOT FOUND / 0 matches / empty)
- pi-ai / pi-agent-core / pi-coding-agent all resolved @0.67.6
2026-04-17 02:36:53 -04:00
ZacharyZhang-NY 4a120f4fc6 feat(settings): drive providers panel from pi-mono catalog
ProvidersPanel reads all 26 CONNECTOR_CATALOG entries and groups them by
auth kind (OAuth / API key / Azure / AWS / Vertex). ProviderRow
dispatches to the matching dedicated form component per auth kind. AWS,
Azure, Vertex forms rewritten with Tailwind classes and a shared field
style module so the Settings pane renders with the same aesthetic as the
rest of the app.
2026-04-17 02:19:56 -04:00
ZacharyZhang-NY 7fdd3c16bf feat(renderer): rewrite providers UI on pi-mono catalog shape
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.
2026-04-17 02:16:35 -04:00
ZacharyZhang-NY 1f310a121e i18n(automation): localize RuleEditor and WorkflowEditor form strings
Adds ~40 automation.ruleEditor.* and automation.workflowEditor.* keys
to en.ts + zh.ts (labels, placeholders, error messages, action labels).
RuleFormModal and WorkflowFormModal now route every hardcoded English
string through useT(), including validation errors and modal titles.

Closes remaining Z-B i18n work after the prompt-builder locale
dispatch (cf92e0c), dept-keywords typing (a78bdb9), and sidebar/
formatters/notification/skills i18n landed earlier.
2026-04-17 01:13:19 -04:00
ZacharyZhang-NY 75022f82c5 refactor(renderer): route all Phosphor icon imports through ui/icons barrel
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.
2026-04-16 23:37:35 -04:00
ZacharyZhang-NY d513497341 i18n(skills): localise SkillsSection strings
SkillsSection had nine hardcoded English strings (header, filter
placeholder/aria-label, empty-state copy, button labels). Move them to
skills.* keys in en.ts/zh.ts and swap the component to useT().
2026-04-16 23:35:21 -04:00
ZacharyZhang-NY dd007760b8 refactor(ui): promote BudgetBar and InlineConfirmDelete to shared ui/
BudgetBar and ConfirmDelete were only sitting in organization/helpers.tsx
but have no organization-specific behaviour. Move them under
components/ui/ and keep the old exports as re-exports so existing
importers continue to resolve. HeartbeatSection stays in helpers.tsx
because it owns agent-specific heartbeat IPC.
2026-04-16 23:34:12 -04:00
ZacharyZhang-NY 61aba41e77 refactor(renderer): rename EntityWorkspace to TwoPaneLayout for clarity 2026-04-16 23:33:55 -04:00
ZacharyZhang-NY 933fcf27d0 refactor(runs): extract useRunLogs hook from RunCenter
RunCenter had three useEffects juggling log-chunk loading, tailing, and
older-chunk fetches. Move the whole lifecycle into useRunLogs, which
uses a single effect + AbortController to scope every IPC call to the
current run and pauses polling while document.hidden.
2026-04-16 23:32:02 -04:00
ZacharyZhang-NY 0c9d8269c1 refactor(dashboard): consolidate BudgetAlertBanner dual source into helper 2026-04-16 23:31:58 -04:00
ZacharyZhang-NY c6935ff983 refactor(ui): move AttentionRow to ui and reuse in Inbox 2026-04-16 23:29:58 -04:00
ZacharyZhang-NY 736cff9371 refactor(titlebar): replace hand-drawn restored-window SVG with Phosphor Copy icon
P3-56 (renderer): the maximized-state button used a custom 3-rect SVG to
render the 'restored window' glyph while the minimize and close buttons
used Phosphor icons. Swap the SVG for Phosphor's Copy icon so all three
titlebar buttons share the same icon family and weight, and the visual
bundle no longer carries a one-off raster-like SVG block.
2026-04-16 23:29:04 -04:00
ZacharyZhang-NY 3edb2fc6c1 refactor(main): extract getOrCreateStore in RuntimeBridge
P3-56: three call sites repeated the same 'lookup → fall back to
new → set if missing' dance for session stores. The duplication hid the
fact that the fallback allocation in the second and third sites could
be wasted when the entry already existed. Extract a single
getOrCreateStore helper that does one map lookup, one conditional
construction, and always returns the cached instance.
2026-04-16 23:27:57 -04:00
ZacharyZhang-NY 326bfb3e38 refactor(communication): extract ComposerShell from MessageComposer and TaskDiscussionComposer 2026-04-16 23:27:41 -04:00
ZacharyZhang-NY aa1204babe refactor(organization): extract shared GoalForm and ProjectForm shells
Introduce goal-form/ and project-form/ directories each holding a
single shared shell that handles both create and edit modes via a
discriminated mode prop. The existing GoalCreator/GoalEditor and
ProjectCreator/ProjectEditor become thin wrappers that forward props
to the shared shell, eliminating duplicated state, JSX, and save/
delete handlers across the four files.
2026-04-16 23:27:40 -04:00
ZacharyZhang-NY 6809fa13af polish: reformat AutomationActivityLog JSX; InlineNotice alert role for actionable warns (P1-20, P3-63) 2026-04-16 23:12:57 -04:00
ZacharyZhang-NY a7ad4e5a8b polish: composite breadcrumb keys; pause run-log polling when tab hidden (P3-58, P3-59) 2026-04-16 23:11:25 -04:00
ZacharyZhang-NY 96ab72472c ConfirmDialog: accept confirmTone/confirmLabel/cancelLabel props (P3-57) 2026-04-16 23:10:48 -04:00
ZacharyZhang-NY ca9b94b5a6 titlebar: await windowMaximize promise instead of setTimeout race (P2-39) 2026-04-16 23:10:19 -04:00
ZacharyZhang-NY 3a1f79324d toast: cap message max-height with scroll; assertive role for danger toasts (P2-38, P3-55) 2026-04-16 23:09:11 -04:00
ZacharyZhang-NY cd85419d1d oauth/inbox: use native ordered list; explicit severity fallback (P2-45, P2-48) 2026-04-16 23:08:44 -04:00
ZacharyZhang-NY 7290338b11 composer: unify getAgentInitials; replace invalid focusRingColor with focus:ring-ring (P2-49, P2-32) 2026-04-16 23:07:40 -04:00
ZacharyZhang-NY 5da9945792 skills: replace hand-rolled primary button with ActionButton; use Input primitive (P2-33, P2-34) 2026-04-16 23:06:35 -04:00
ZacharyZhang-NY 41fee50c55 EntityWorkspace: normalize typography to match Panel (20px/bold), use Eyebrow primitive (P1-10, P2-27) 2026-04-16 23:04:52 -04:00
ZacharyZhang-NY 296c72eb3f composer/settings: use ActionButton iconOnly send; replace deprecated navigator.platform with lib/platform (P1-9, P2-46) 2026-04-16 23:02:29 -04:00
ZacharyZhang-NY f1dd9786e5 onboarding: remove AsciiAnimation decoration and 'by ELYDORA' signatures (P1-17, P1-18); move hero-drift keyframes to global CSS (P3-60) 2026-04-16 23:01:11 -04:00
ZacharyZhang-NY 1b11b53f84 onboarding: extract OnboardingStep shell (P1-11); eliminate 6 copies of motion+header chrome 2026-04-16 23:00:01 -04:00
ZacharyZhang-NY ca936aabe6 dashboard: extract AttentionRow, cap stagger at 8, limit pulse animation to first 2 rows (P1-15, P2-44, P2-47) 2026-04-16 22:56:09 -04:00
ZacharyZhang-NY be0ff5a041 lib(renderer): add safeJson helper; migrate 6 JSON.parse try/catch call sites 2026-04-16 22:54:20 -04:00
ZacharyZhang-NY bea8aa9542 ui: add Card/Eyebrow/Spinner primitives, iconOnly ActionButton, drop ui.tsx barrel 2026-04-16 22:52:16 -04:00
ZacharyZhang-NY de77f5f38e refactor(onboarding): replace 50-prop drilling with OnboardingContext
OnboardingStepPanel previously forwarded ~50 props to seven step
components, so adding a single wizard field meant touching four layers.
Introduce a renderer-local React Context (OnboardingProvider /
useOnboarding) that carries all wizard state and actions (connectors,
node status, company inputs, model selection, execution mode, mission,
launch summary, locale, t, error/loading). Step panels now receive only
their navigation callbacks (onContinue / onBack / onLaunch / onFinish)
and read everything else via useOnboarding(). CompanyOnboarding wraps
the step panel tree in OnboardingProvider with a memoised value.

Business logic is unchanged: each onContinueFrom* transition, the
bootstrap call, and the welcome-step branch remain exactly as before;
this refactor only moves the data-passing mechanism.
2026-04-16 22:11:45 -04:00
ZacharyZhang-NY f3816c6115 refactor(ui): unify success/danger/warning color tokens across Toast/StatusPill/InlineNotice 2026-04-16 22:10:43 -04:00
ZacharyZhang-NY a443312f1a refactor(automation): replace ACCENT/LINE constants with design tokens 2026-04-16 22:10:04 -04:00
ZacharyZhang-NY f177e6c877 refactor(settings): use Modal for OAuthPromptDialog 2026-04-16 22:06:59 -04:00
ZacharyZhang-NY 0f1b313272 refactor(execution): split ApprovalInspector 346-line god-component
Decompose monolithic src/renderer/components/execution/ApprovalInspector.tsx
into src/renderer/components/execution/approval-inspector/:
- ApprovalInspector.tsx (168 lines, orchestrator only)
- ApprovalSummary.tsx, ProposedHireCard.tsx, ApprovalDocumentsPanel.tsx
- DeliverableReviewPanel.tsx, SchedulePicker.tsx, DecisionForm.tsx
- TypeBadge.tsx, useApprovalPayload.ts, index.ts (barrel)

Hoist JSON.parse out of JSX IIFE into useApprovalPayload hook with typed
return. Replace roll-your-own role=radio spans with native
input[type=radio]. Extract getTypeBadgeClass switch into TypeBadge
component. Update execution.tsx barrel to re-export from new path.

Business logic preserved: approval decision flow, schedule validation,
deliverable review, document editing, delete confirmation.
2026-04-16 22:06:39 -04:00
ZacharyZhang-NY 69724f0188 refactor(social): use Modal for AddAccountDialog 2026-04-16 22:04:24 -04:00
ZacharyZhang-NY 8541836b26 refactor(automation): use Modal for WorkflowEditor 2026-04-16 22:03:57 -04:00
ZacharyZhang-NY 183f22e922 fix(onboarding): derive step counter from STEP_ORDER
All six step panels previously hard-coded "X / 5" or "X / 6" in their
header captions, which drifted out of sync with the 7-step STEP_ORDER in
OnboardingHero. Export STEP_ORDER / TOTAL_STEPS / getStepNumber from
OnboardingHero and consume them in every step so the counter is always
the single source of truth.
2026-04-16 22:03:57 -04:00
ZacharyZhang-NY 5a5587c2a1 refactor(automation): use Modal for RuleFormModal 2026-04-16 22:03:24 -04:00