Skip to content

Chat as the product surface: redesign + Capability Bus + local Coding Agent - #39

Open
monatruong wants to merge 146 commits into
mainfrom
feat/chat-redesign
Open

Chat as the product surface: redesign + Capability Bus + local Coding Agent#39
monatruong wants to merge 146 commits into
mainfrom
feat/chat-redesign

Conversation

@monatruong

@monatruong monatruong commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Native chat redesign → chat as the product surface: Capability Bus + local Coding Agent

Scope note: this branch has grown well past the original "chat redesign" title. It now delivers three connected things: (1) the redesigned chat home, (2) the Capability Bus that lets chat drive every other feature through one verb vocabulary, and (3) the Coding Agent — Engineering that makes real code changes in a linked project, on the user's own machine. Suggest retitling the PR accordingly (see below).

Base: main · Head: feat/chat-redesign · ~145 commits · 137 files · +16.5k / −0.6k


⚠️ Ship-together requirement (read first)

The Coding Agent's cloud piece (the edit_code Anthropic tool in companyChat, repo Murror/CodePet-Cleandevpet-8f4b1) is built but deployed separately and currently held. The client and the function must ship together: if the function announces edit_code before the client can render the run card, prod users get "byte promises, nothing happens"; if the client ships first, Engineering asks silently fall back to a cloud turn. Merge this PR and deploy the companyChat change in the same window.

Also note: 38 commits on the local branch are unpushed (the Capability Bus + Coding Agent work, held per the review gate). Push before merging.


What's in this PR

1. Chat, redesigned into the home surface

  • Hero empty-state + elevated composer; Ask / Plan / Build modes shape the outgoing message (no separate backend mode).
  • Parallel department-agent fan-outRoadmapEngine.nextMoves planner + CompanyStore fan-out engine + the inline AgentsWorkingRow (Codex-style live multi-agent view).
  • Thread persistence — chat threads persist to Firestore and hydrate on launch; the recent thread resumes.
  • Offline mock harness-CODEPET_MOCK_CHAT YES + an in-app gallery, so every chat surface is demoable/testable with no API key.
  • Onboarding polish (stage slider fixes, companion-picker step cut).

2. Capability Bus (Part 1) — chat ⇄ everything

One integration layer so a chat turn can drive the rest of the app coherently:

  • CompanyContext — a typed read-surface all grounding flows through (groundingString for chat, a lean runTaskGroundingString for run-task). Byte-parity preserved.
  • Verb vocabulary — existing run_task / nav / setup / remember plus new re_plan and walkthrough reply verbs (parsed + dispatched, with ordering locked by tests).
  • Handoff rule — work-producing actions from any surface (roadmap, tasks board) land in chat.
  • Client-only invariant — the coding-agent project slice (path, CLAUDE.md, diffs) is a real type but is never included in any cloud grounding string. Locked by test_context_lockClientOnlyProjectSlice.

3. Coding Agent (Part 2) — Engineering makes real code changes

The PRD "agent coding" feature: link a real project, run the user's own claude CLI locally, review a diff, commit to a throwaway git branch or safe-apply. The user's code never leaves the machine.

  • 2A — Linking: ProjectLink + ProjectProbe (git / CLAUDE.md detection) + CompanyStore.linkProject with consent-gated CLAUDE.md bootstrap (never clobbers an existing one); cleared on account switch.
  • 2B — Commit engine: GitRunner + CodeCommitService. Git path: stash dirty → throwaway codepet/<slug> branch off HEAD → commit accepted files, or abort-and-restore. Shadow path: temp copy → apply-with-backup / undo. Never merges, pushes, or deploys.
  • 2C-1 — Orchestration: CodingRunCoordinator over a CodeRunning seam; ClaudeCodeRunAdapter bridges the real ClaudeCodeRunner (spawns claude -p --output-format stream-json via a login shell) to an async outcome. Runs on the user's Claude subscription — 0 Codepet credits.
  • 2C-2 — Run card: CodeRunCardView — one evolving in-chat card: plan-preview → live step checklist → per-file diff review with accept toggles → committed / failed. Streams live tool-use steps.
  • 2C-3 — Link UI: Environment "Linked project" section (status + link button + recent-project chips), the card's .noProject link offer, and a composer chip showing which folder the agent will touch. Shared ProjectLinker (picker + consent) so both surfaces behave identically.
  • 2C-server — CF tool: edit_code Anthropic tool + validator in companyChat (held; see above).
  • 2D — Routing: Engineering tasks/pills route to the local agent when a project is linked (adaptive), else the normal cloud turn.

Security / trust model

  • Code-blind cloud: the CF never receives project code; edit_code returns only the intent (ask, planned_files, needs_bash). All code access is client-side.
  • User's own subscription: heavy generation runs on the user's local claude (their auth, their PATH), not a Codepet key — 0 credits.
  • Consent + reversibility: CLAUDE.md is only written on an explicit yes; commits go to a throwaway branch or a backed-up apply; nothing is pushed/merged/deployed.

Testing

  • Bus + coding-agent logic is unit-tested throughout: CompanyContext, CompanyChatClient, CompanyStoreVerbDispatch, RoadmapDispatch, EditCodeRouting, EditCodeRun, CodingRunCoordinator, CodeExecSteps, ProjectLink, ProjectLinkSuggestions, GitRunner, CodeCommitService — plus the chat fan-out suites. All green.
  • Each destructive/orchestration task passed an adversarial code review (2A/2B/2C-1/2D findings fixed and locked with tests).
  • Live end-to-end smoke (manual, not in CI): real claude on a scratch repo → edited hello.js → run reached .reviewing with the diff → approve → committed to codepet/… branch, nothing pushed. Confirms the seam that carried the 2C-1 critical bugs against reality.
  • SwiftUI views are build-verified + #Previews across states; founder visual pass pending (no screen-recording in CI).

Not in this PR / follow-ons

  • Deploy of the companyChat edit_code tool (paired deploy — see top).
  • "Open full diff" viewer and shadow-apply Undo are stubs.
  • Overnight/autonomous coding runs (foundation only).

Suggested retitle

Chat as the product surface: redesign + Capability Bus + local Coding Agent

monatruong and others added 30 commits July 27, 2026 20:16
Centered hero + elevated composer + Ask/Plan/Build modes; view
refactor + restyle only, no backend/fake-affordance changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 TDD tasks: ChatMode + ChatComposer + ChatEmptyState + wire-in
(delete old input/greeting/lets-build) + bubble restyle & light/dark verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-build

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-assert keyboard focus at the end of send() so the docked composer's
TextField (which SwiftUI rebuilds when chatMessages flips empty→non-empty)
doesn't drop focus after the first message. Also give the composer's outer
container the theme's floatingShadow so it reads as elevated per spec §2,
instead of a flat bordered card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the + quick-actions and mode menus from .borderlessButton (which
draws a system disclosure chevron .menuIndicator(.hidden) doesn't suppress
on macOS) to .menuStyle(.button)+.buttonStyle(.plain), leaving only the
mode control's own chevron.down after the label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ns, icon pills

- CompanionOrb: reusable luminous gradient sphere (angular accent gradient +
  glossy highlight + depth shading), used as hero focal, message avatar, and
  thinking indicator. Glow gated on accessibilityReduceTransparency.
- QuickAction: small Identifiable model (title + systemImage) so quick-action
  pills/menu carry an icon without breaking localization.
- ChatComposer: quickActionsMenu items now render as Label(title, systemImage);
  sendButton fills a purple→pink LinearGradient + accent glow when canSend;
  the composer's outer container gets a subtle ambient accent glow layered
  under the existing floatingShadow.
- ChatEmptyState: CompanionOrb(78) now anchors the hero above the greeting
  (replacing the old greeting-only radial glow); pills show an icon + title.
- CopilotChatView: quickActions rebuilt as [QuickAction] (checklist/map/
  square.grid.2x2/doc.text); typingRow replaced with an orb + "Thinking…" row;
  producingRow gets a leading orb; CopilotBubble's textBubble now branches —
  the `me` bubble is untouched, the companion bubble gains a 28pt orb avatar
  and a Copy + Regenerate action row (Regenerate is a pure client-side resend
  of the last `me` message through the existing sendChat path, no backend
  change).

Verified: xcodebuild build (CODE_SIGNING_ALLOWED=NO) → BUILD SUCCEEDED.
Full test suite: 303 tests pass; the 2 known CompanyStoreScaffordOnboardingTests
Firebase-init flakes crash-and-retry as expected (no new regressions).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make chat the full-width default page; retire Overview; split
Roadmap/Second Brain into two pages; keep onboarding + top-bar look.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd Brain tabs

Codepet.select(.chat) is now the store's default/reset view and the wordmark's
Home action; AppShellView drops the 50% copilot side panel and floating chat
toggle so `content` fills the shell full-width. AppView retires `.overview` and
gains `.chat`/`.secondBrain`, with Roadmap and Second Brain promoted to their
own top-bar tabs (extracted views from Task IA-1). OverviewView.swift deleted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotChatView now renders full-width as the app's main page, so message
bubbles and the composer were stretching edge-to-edge. Wrap the message
list VStack and the docked composer each in an HStack{Spacer;content
.frame(maxWidth:720);Spacer} so they read as a centered ~720pt column
(matching Axora/LIX/DeepThink-style reference layouts) while the full
ScrollView/background stays full-width. Per-message me/companion alignment
is unchanged inside the column. Bumped ChatEmptyState's composer cap from
680 to 720 to match the docked composer width for a consistent column edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ick-action cards

Chat v3: time-of-day + founder-name greeting with a purple-gradient second
line, a soft purple brand wash behind the empty state, a purple-forward
CompanionOrb, a purple-gradient composer border, and quick-action cards
(icon + title + muted helper detail + purple accent bar) replacing the
old pills.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Top bar -> left SidebarView (brand, New chat, Recent/History, Workspace
nav, Upgrade/account); remove the Your-team chat header. 3 build-green tasks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sidebar (SB-1/SB-2) now owns nav, brand, and chat history (Recent), so
CopilotChatView's redundant "Your team / guiding · {company}" header
and its History toggle -> ThreadListView switcher are dead chrome.
Removes header, showHistory state, and the now-unreferenced
ThreadListView struct; body simplifies to empty-state vs
messageList+composer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full status, commit map, resume instructions, gotchas, and the
human-GUI punch-list so a fresh session can pick up cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-restructure cleanup on files that outran their own docs/dead code:

- AppShellView: drop the now-unused `accent` computed prop (and the
  `appState` EnvironmentObject only it read) — the companion-accented
  Copilot side panel it fed is gone (chat is the full-width content).
  Refresh the type doc to describe the sidebar+content shell it actually
  is, not the old "sidebar + placeholder Copilot panel".
- SidebarView: the "NOT WIRED YET / SB-2 will own the binding" header and
  `collapsed` doc are false — the view IS wired in AppShellView, which
  owns `sidebarCollapsed`. Rewrite both to match reality.
- SidebarView: the thread-row ellipsis Menu still used
  .menuStyle(.borderlessButton), which paints a stray system disclosure
  chevron next to the ellipsis on macOS — the exact bug the composer menus
  fixed in df83ef8. Switch to .button + .plain + .menuIndicator(.hidden).

No behavior change. xcodebuild build (CODE_SIGNING_ALLOWED=NO) SUCCEEDED;
full suite 303 tests / 0 failures (the overall TEST FAILED is the known
CompanyStoreScaffordOnboarding Firebase-init flake, fixed on PR #40).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contain the active-conversation composer in the 720 column (fix the
edge-to-edge sprawl), share one ambient purple backdrop behind both the
empty and active states, and drop the full-width divider. Structure-
preserving containment/cohesion pass — no composer/logic rewrite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two tasks: (1) extract ChatBackdrop + share it behind empty+active,
(2) contain the docked composer in the 720 column and drop the divider.
Build-gate test cycle (no new pure logic); suite stays 303/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract ChatEmptyState.brandWash into a reusable ChatBackdrop and hang it
once behind CopilotChatView so the empty hero and the active conversation
read as one continuous purple-washed surface. ChatEmptyState drops its
private brandWash (and the reduceTransparency env only it used); no visual
change to the empty state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The active-conversation composer rendered full-width (composerView.padding
(10)); wrap it in the same Spacer/maxWidth:720/Spacer column the message
list uses so its edges track the bubbles at every width. Drop the
full-width Divider() — the composer's floatingShadow carries the
separation over the shared ChatBackdrop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The docked composer's centering HStack omitted spacing: 0 that
messageList uses, so default ~8pt spacing shrank the composer column
and the two 720 columns diverged at some window widths. Add spacing: 0
for a byte-identical centering container.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ambient wash moved to ChatBackdrop (applied by CopilotChatView) in
57e34e6; the struct doc still listed it as something ChatEmptyState
renders. Comment-only; no code change. (Final-review M1.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ntained

720 left only ~15pt margins on a ~1000pt window (sidebar takes 250), so the
composer still read as full-width. Drop the shared column (active composer,
empty-state composer, empty-state cards, message list) to 600 so containment
is clear on a laptop-sized window while staying generous on wide ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dmark

The 22pt gradient square read as a second, competing brand next to the
pixel wordmark. The row is now just "Codepet" (still the home button).
newChatGradient stays — the New chat button and Upgrade CTA still use it.
Adapts Phase 2 chat spec §1/§2/§4/§8 to feat/chat-redesign: reimplement
CompanionOrb as a luminous companion-tinted sphere (breathe on isWorking)
+ a ChatThinkingRow that names the work ("Drafting {title}…" / "Working on
it…") with a subtle shimmer. Second-hue per companion + chatCanvas/chatOrbCore
tokens. Orb perf (Canvas) is the schedule risk; Metal fallback budgeted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
monatruong and others added 28 commits July 29, 2026 15:43
handleDoneAction applies re_plan inline (→ generateRoadmap) and RETURNS the
walkthrough task id; sendMessage drains it after the send completes
(isStreaming == false), because a guided turn starts a new send that the
in-flight send's busy-guard would otherwise block. cid-guarded, no-op on an
unknown/stale id. Fallback path carries the new verbs too.

A dispatch test caught the busy-guard blocking synchronous walkthrough
dispatch — hence the deferred drain (design deviation from the plan's
inline call).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lets `-CODEPET_MOCK_CHAT YES` drive a real walkthrough dispatch end to end
without the (other-repo) companyChat CF. Triggers on the token "walkthrough"
(not "walk me through") so it can't match walkThroughTask's own composed
"Walk me through …" message and loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A reply carrying both verbs: re_plan replaces tasks first, then the
walkthrough id resolves against the POST-replan set and starts the guided
turn. Pins the deliberate ordering in handleDoneAction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes Part 1's implementation: Tasks board obeys the "work lands in chat"
rule (Layer 4 gap); run-task grounding routed through CompanyContext
(Layer 1 done). Layer 3 (unified lifecycle) deferred to Part 2 — only one
backend today; unifying now = engine-ahead-of-plays.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The board dispatched runTask/walkThroughTask without navigating, so the
founder watched nothing happen while the work streamed into chat. Now it
uses the same RoadmapDispatch.navigatesToChat rule the roadmap uses:
work-producing taps land in chat; approve + open stay in place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…us Layer 1)

Adds runTaskGroundingString (brief+tasks+decisions, byte-identical to the
old inline call — no library grounding, no payload change) and migrates the
run-task site to it. ChatContext.compose is now called only inside
CompanyContext — the single Layer-1 read-surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First of four Part-2 sub-plans. ProjectLink model + .git/CLAUDE.md probe,
CLAUDE.md bootstrap from brief+decisions, CompanyStore.linkProject + active
link + client-only project-slice wiring. Picker UI deferred pending a UI
design discussion; subprocess/edit/commit are 2B/2C.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure ProjectLink value type + ProjectProbe (.git / CLAUDE.md detection) with
its client-only CompanyContext.ProjectSlice mapping, and ClaudeMdBootstrap —
a pure seed-markdown composer from brief + decisions for a linked project
with no CLAUDE.md. No subprocess yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ring (Part 2A)

linkProject(path:) probes a folder, sets activeProjectLink, persists a
security-scoped bookmark, bootstraps CLAUDE.md when absent (never clobbers,
re-probes), and feeds the client-only project slice into the chat-send
CompanyContext (cloud payload unchanged — regression test added). Picker UI
deferred pending a UI-design discussion. ProjectLinkTests 6/6,
CompanyContextTests 7/7, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Final-review Important: activeProjectLink + its bookmark key were added to
the per-account @published cluster but not cleared in hydrate's
accountChanged branch or reset() — a cross-account leak of local project
data once a UI wires it. Now cleared in both (bookmark key extracted to a
constant). Plus: tighten hasClaudeMd to reject a dir named CLAUDE.md; note
the bookmark is best-effort/untested (2B revisits). Test locks the
account-switch clear. ProjectLinkTests 7/7, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CodeCommitService: git path (throwaway codepet/<slug> branch off HEAD, stash
dirty work, commit-on-approve / abort-restores) + shadow path (temp copy,
apply-with-backup / undo) + GitRunner. ClaudeCodeRunner reused unchanged.
Orchestration + approval gate + chat UI deferred to 2C.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Synchronous /usr/bin/git wrapper (stdout/stderr/exit, never throws) + a pure
kebab CommitSlug. Foundation for CodeCommitService. 3/3 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…it engine (Part 2B)

Git path: stash dirty work → throwaway codepet/<slug> off HEAD → commit
accepted files on approve (never main/merge/push) → abort hard-resets +
deletes branch + restores stash. Shadow path (non-git): temp copy (heavy dirs
excluded) → apply accepted files to real tree with backup (tombstoning new
files) → undo restores exact pre-apply state. ClaudeCodeRunner reused
unchanged. 8/8 tests (real git in temp repos + shadow file IO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…al review)

Opus final review (1 Critical + 5 Important), all fixed:
- CRITICAL: applyShadow no longer overwrites an original whose backup failed
  (backup is now a hard precondition — skip apply, no data loss).
- abortGit runs `git clean -fd` so run-created untracked files are removed
  (reject is a full undo), and gates branch -D / stash pop on the checkout
  actually switching back.
- commitGit returns GitCommitResult; on an overlapping stash-pop conflict it
  resets to the clean commit and retains the founder's work in the stash
  (stashRestored=false) instead of leaving conflict markers + lying.
- commitGit no longer pops on commit failure (avoids double-pop).
- GitRunner drains stdout/stderr concurrently (no pipe-buffer deadlock on
  chatty hooks).
- `stashed` set from the stash push result, not just a dirty tree.
Plus: beginShadow cleans its temp dir on failure; nested-node_modules note.
Tests added for untracked-abort + overlap-conflict. 13/13, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testable logic core, no UI: edit_code reply verb (client parse), pure
EditCodePlanner/EditCodeRun (preview gate + git/shadow backend), and
CodingRunCoordinator driving propose→execute→approve/reject over an injected
CodeRunning seam + the real CodeCommitService (fake runner + temp repos in
tests). 2C-2 (card UI), 2C-3 (Environment link UI), 2C-server (CF tool) follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Additive EditCodeAction {ask, planned_files, needs_bash} on the reply
contract + SSE done frame; executed locally, never sent to the cloud.
16/16 CompanyChatClientTests (2 new + no regression).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…C-1)

Pure lifecycle phases + preview-gate (multi-file/Bash → preview) + backend
selection (git branch vs shadow) from the linked project. 3/3 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…C-1)

Drives a coding run over a CodeRunning seam (ClaudeCodeRunAdapter in prod,
fake in tests) + the real CodeCommitService: propose (preview gate + backend
select), execute (begin session → run → reviewing/failed with teardown),
approve (commit git / apply shadow), reject (abort/discard). CompanyStore
routes edit_code from the reply to codingRun.propose. 5/5 coordinator tests
(fake runner + real temp git repos); build green. UI is 2C-2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opus final review (2 Critical + 3 Important), all fixed:
- CRITICAL: ClaudeCodeRunner published `.finished` BEFORE computing diffs, so
  the adapter always read an empty fileDiffs → commits nothing. Now diffs are
  published and `.finished` flips together (also cleaner for the exercise flow).
- CRITICAL: the adapter reused one runner; @published replayed a stale
  `.finished`, so every run after the first resumed instantly with old diffs +
  orphaned a live subprocess. Now a fresh ClaudeCodeRunner per run() call.
- propose() tears down any un-resolved prior session (no orphaned branch/shadow)
  and refuses to clobber an in-flight run.
- approve() honors commitGit/applyShadow results → routes to .failed + abortGit
  on failure (no false "committed", stash restored) instead of lying.
- phase guards on execute (.readyToRun/.previewing), approve/reject (.reviewing).
- relPath standardizes the root so nested paths don't flatten.
Tests added for teardown, failed-commit, and phase-guard. 8/8 coordinator
tests, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stream live run steps (seam onStep + pure CodeExecSteps mapping, testable),
CodeRunCardView (the agreed evolving card: preview → steps → inline per-file
diff review → Reject/Approve, honest 0-credits/branch/undo labels), wired
into the transcript like AgentsWorkingRow + MockChat exercise. Views are
build-verified + the founder's visual pass. 2C-3 (Environment link UI),
2C-server (CF tool), full-diff sheet deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CodeRunning seam gains an onStep callback; ClaudeCodeRunAdapter forwards
tool-use events (append-only $events) via a pure CodeExecSteps mapping;
CodingRunCoordinator republishes them as @published steps for the card
(cleared per propose). Pure mapping + coordinator step-collection tested
(12/12 across CodeExecSteps + CodingRunCoordinator); build green. Card
views (Tasks 2-3) are the founder's to build/verify visually.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testable suggestion helper (I execute) + the Environment 'Linked project' section, .noProject inline offer, and composer chip (founder's SwiftUI + visual pass). Reuses 2A linkProject; matches the agreed 2C UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure ProjectLinkSuggestions.suggest over ProjectStore.sortedProjects (excludes the active link, capped) for the Environment link chips. 3/3 tests, build green. Environment section / .noProject offer / composer chip (Tasks 2-3) are the founder's SwiftUI + visual pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adaptive: Engineering routes to local edit_code only when a project is linked, else cloud run_task (zero disruption). Pure routing rules (RoadmapDispatch .editCode + EditCodeRouting) + roadmap/tasks/composer dispatch wiring. Overnight trigger foundation-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adaptive: Engineering .codepetCanDo tasks and the Engineering composer pill
route to the LOCAL coding agent (codingRun.propose → edit_code) ONLY when a
project is linked; with no link they keep today's cloud run_task behavior
(zero disruption). Pure rules (RoadmapDispatch .editCode via
action(for:isEngineering:projectLinked:) + EditCodeRouting) are tested;
roadmap/tasks/composer dispatch wiring is build-verified. edit_code
navigates to chat (Layer 4). Overnight trigger stays foundation-only.
10/10 routing tests, full build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ask (2D review)

Final review (1 Important + 1 Minor), fixed:
- IMPORTANT: sendChat's Engineering-pill route returned early without appending
  the founder's message, so their ask vanished from the transcript (the run card
  that'd show it is 2C-2, not built) — a visible regression if 2D ships first.
  Now echoes the ask as a .me message before staging the run. Two store tests
  lock it: eng+link → run staged + ask echoed + NO cloud turn; no-link → normal
  cloud turn (guard can't mis-fire).
- MINOR: hoisted the duplicated task→ask construction to RoadmapDispatch.editCodeAsk
  (used by all 3 dispatch sites).
16/16 routing+dispatch tests, full build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sks 2-3)

One evolving in-chat card renders every phase of CodingRunCoordinator.run:
- previewing → ask + "may run terminal commands · you review everything" + Run/Cancel
- running   → avatar + ENGINEERING kicker + "0 credits" + live step checklist
- reviewing → branch chip, "N files changed", per-file accept toggles (deselected
              files dim + drop from the commit), monospace unified diff (+/- tints,
              capped 40 lines/file), Open-full-diff stub, Approve N / Reject
- committed → git branch + "nothing pushed/merged"; shadow "Applied · Undo" stub
- discarded / noProject / failed (with the install-&-sign-in enablement line)
Bilingual, CodepetTheme tokens only, #Preview across all phases for visual QA.

Wiring: CopilotChatView renders the card at the transcript level when a run is
active (beside AgentsWorkingRow) + auto-scroll; MockChat "edit code"/"code:" route
stages a run so the card is exercisable under -CODEPET_MOCK_CHAT YES.

Coordinator: cancel() (dismiss a not-yet-running/finished card — reject() only
covers .reviewing) + a DEBUG-only .preview(_:steps:) factory for the #Preview.

18/18 (CodeExecSteps + CodingRunCoordinator + verb-dispatch), build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, composer chip (Part 2C-3 Tasks 2-3)

- ProjectLinker (shared): native directory picker → CLAUDE.md-bootstrap CONSENT
  prompt (never writes without a yes) → CompanyStore.linkProject. One home for the
  flow so both surfaces ask consent identically.
- EnvironmentView "Linked project" section: linked → folder name + path + git /
  CLAUDE.md badges + Change…; unlinked → value line + "Link a project folder" +
  one-tap recent-project chips (ProjectLinkSuggestions). Built in the view's own
  CodepetCard/.pixelSystem idiom. #Preview (unlinked).
- CodeRunCardView .noProject: real "Link a project" button that runs the shared
  flow and re-proposes THIS run's ask (no retype).
- ChatComposer: quiet active-project chip (⑂/folder + name) → Environment, so the
  founder always sees which folder the agent will touch. Store injected; preview
  host updated.

25/25 (ProjectLinkSuggestions + ProjectLink + CodingRunCoordinator + verb-dispatch),
build green. Views — need founder visual pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@monatruong monatruong changed the title Native chat redesign: hero empty-state + elevated composer + Ask/Plan/Build modes Chat as the product surface: redesign + Capability Bus + local Coding Agent Jul 30, 2026
Hardening + correctness from a full interactive test of the coding-agent flow
(link → chat edit_code → run → diff → Approve → commit on a codepet/* branch).
Five real bugs found and fixed, each with a regression test:

1. `.readyToRun` never executed — a chat-triggered run sat at "Getting started…"
   forever (nothing called execute()). The card now auto-runs a readyToRun job.
2. Double-send race — a rapid duplicate proposal superseded an in-flight run
   mid-launch and stranded it. propose() now refuses to clobber a .running/
   .readyToRun run (test_propose_ignoresDuplicateWhileInFlight).
3. Branch orphaning / data loss — abortGit deleted the codepet/* branch
   unconditionally (reject / supersede / failed-approve), destroying committed
   work. It now NEVER deletes a branch that carries commits
   (test_abortGit_keepsBranchThatHasCommits).
4. False "Committed" — "nothing to commit" was treated as success. commitGit now
   distinguishes nothingToCommit / alreadyOnBranch; approve only reports committed
   when a commit truly exists (test_commitGit_flagsNothingToCommit).
5. Card stuck until a tab switch — @published fires in willSet, so a synchronous
   re-render read the pre-assignment value; deferring the refresh one runloop turn
   makes running→reviewing→committed update live.

Also lands the card's "Open full diff" sheet (was a dead stub), inline card
placement (renders after the ask, not pinned to the transcript bottom), and a
MockCodeRunner behind CODEPET_MOCK_CHAT so the whole flow is exercisable offline
with zero Claude usage. 31/31 coding-agent tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant