HiringPipeline carried three panels that restated information already
shown — one with more emphasis — on the Team page or in the stage
filter directly above them:
1. "Pending approval" mini-Panel (lines 121-187): listed the same
candidates that the user reaches by clicking the `pending_approval`
chip in the stage filter at the top of the page. The mini-Panel
also carried its own Approve / Reject buttons, but those approvals
are resolvable from Approvals section too. Doubling the approval
path increases the chance of racing clicks between the two
surfaces. Deleted.
2. 4-card summary row at the bottom (lines 246-267): Total agents /
Pending hires / Departments / Monthly payroll. The first two are
rendered on the Team page by TeamHeaderSummary (commit 36bed1b).
Payroll can return via a Team-page dedicated surface when there
is a business reason; until then it is clutter. Deleted.
3. "Department headcount" Panel (lines 269-293): grid of active+
pending count per department. OrgChart already shows this through
the department colour on each agent card, and TeamHeaderSummary
covers the aggregate. Two places describing the same fact is one
too many. Deleted.
Also:
- `onRefresh` prop removed from HiringPipeline + OrganizationSections
caller — only the (now-deleted) approve/reject buttons used it.
- `Card`, `UserPlus`, `Check`, `unwrap` imports dropped because the
deletions made them unused.
- Unused `i` index in Dashboard.tsx activeWork map callback cleaned
up (left over from the previous commit that removed the
arbitrary animation gate).
File size: HiringPipeline.tsx 296 LOC → 175 LOC (net −121).
Typecheck + lint pass.
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.
Before this commit one piece of state (`selectedAgentId`) did three
jobs: roster highlight, navigation destination, and Edit-modal open
flag. The three jobs had incompatible lifecycles — highlight should
persist across modal close, navigation should land on a screen
without opening a modal, and the modal itself only wants to open on
explicit intent. The conflation was the root cause of the "Edit: CEO
auto-opens on first Team view" bug (commit 41132e2 patched the most
visible symptom by not pre-selecting the lead agent, but the coupling
remained).
Split into two slice fields:
- `selectedAgentId` — which agent is currently highlighted in the
roster / OrgChart. Survives modal close. Set by: row click, card
click, navigation from Inbox / Hiring (all of which pass through
`openAgentEditor` which sets both so the highlight follows the last
interaction).
- `editingAgentId` — which agent's Edit modal is open, or
NEW_ENTITY_ID for the "Request new hire" modal. Only set by
explicit user action; cleared on modal close. Never set by
navigation or onboarding — the pre-select pathway cannot
accidentally open a modal again.
Plumbing:
- `selection.ts`: new `editingAgentId` + `setEditingAgentId`, plus
clear on `clearCompanyScopedSelections`.
- `useCompanyData.ts`: derive `editingAgent` record from the id.
- `useCompanyController.ts`: forward `editingAgent` on the section
context.
- `OrganizationSections.tsx`: `agents` section modals read
`editingAgentId` / `editingAgent`. "Request new hire" button uses
`setEditingAgentId(NEW_ENTITY_ID)`. Single-click handlers go
through `openAgentEditor(id)` which updates both states.
Also improved Edit modal title (was "Edit: CEO", a machined English
prefix on a Chinese UI with zero context about the role):
- zh: "编辑 {name}({role})"
- en: "Edit {name} ({role})"
Renders as "编辑 CEO(首席执行官)" in the screenshot example.
Covers J9 / S2.
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.
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.
Sections now subscribe directly to the Zustand store via useSectionContext
(shared data derivation) and useAppStore fine-grained selectors. Delete
useAppController, useExecutionController, useOrganizationController, and
useCommunicationController; the three domain forwarders held no logic
beyond re-exporting store slices.
SectionContentView becomes a 50-line layout dispatcher taking only
{section, error, pageTransition}; cross-domain run actions (startRun /
cancelRun) move into App.tsx and are exposed to sections through a new
AppActionsContext alongside refresh / setError / openPathWithFeedback.
App.tsx now calls useUIController + useCompanyController directly and
composes palette + navigation plumbing itself.