Merges task KGRrdWohwZ6YcWjOZ0KqF (ReviewTab rawDiff → metadata file list,
phaseId/commitMode wiring) into the viewport virtualization phase branch.
Key resolution decisions:
- Keep viewport virtualization IntersectionObserver logic from HEAD
- Use activeFiles (not undefined `files`) in ReviewTab DiffViewer render — bug fix from incoming
- Keep FileDiff/FileDiffDetail split from HEAD (not deprecated FileChangeType)
- Keep FileDiff['status'] in parse-diff (status lives on base type)
- Drop spurious `comments` prop the incoming branch added to DiffViewer (unused)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves add/add conflict in diff-cache.ts (kept typed PhaseMetaResponse/
FileDiffResponse interfaces from HEAD over unknown-typed singletons from test
branch) and content conflict in phase.ts (kept both phaseMetaCache and
fileDiffCache imports; removed auto-merged duplicate firstClone/headHash/
cacheKey/cached declarations and unreachable empty-projects guard).
Also cleans auto-merged duplicate getHeadCommitHash in orchestrator.test.ts
and simple-git-branch-manager.ts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers IntersectionObserver placeholder rendering for 300-file diffs,
single-file bypass, sidebar ref registration, expandAll batch fetching
(25 files → 3 batches of 10), and all FileCard lazy-load states:
default collapsed, loading, success with HunkRows, error+retry, binary,
no-hunks, detail-prop pre-expanded, and collapse/re-expand UX.
Cherry-picks viewport virtualization implementation commit (f804cb19)
onto this branch so the tests run against the actual new code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DiffViewer now uses IntersectionObserver to replace off-viewport FileCards with
48px placeholder divs, eliminating thousands of DOM nodes at initial render for
large diffs. Files within 1× viewport buffer are rendered as real FileCards.
FileCard defaults to collapsed state and only fires getFileDiff when expanded,
with staleTime: Infinity to avoid re-fetches. Handles loading/error/binary/
no-hunks states. Commit mode passes detail prop to skip lazy loading entirely.
DiffViewer batches expand-all in chunks of 10, prefetching via tRPC utils to
saturate the network without blocking the UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Writes Vitest + RTL tests covering the virtual list threshold (>50 rows),
fallback path (≤50), directory collapse/expand, tab-switch scroll
preservation, file-click callback, and root-level files.
Also aliases react/react-dom to the parent monorepo copies in
vitest.config.ts so all components share the same ReactSharedInternals
dispatcher — fixing the pre-existing null-dispatcher hook errors that
affected all web component tests in this git worktree.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Split FileDiff into FileDiff (metadata-only) and FileDiffDetail (with hunks)
so the phase branch diff no longer requires client-side diff parsing
- ReviewTab now reads diffQuery.data.files (FileStatEntry[]) and maps
path → newPath for sidebar and DiffViewer; no parseUnifiedDiff call
in the phase branch view path
- Commit view still parses rawDiff via parseUnifiedDiff → FileDiffDetail[]
- Pass phaseId, commitMode, comments, expandAll to DiffViewer
- Pass totalAdditions/totalDeletions from server to ReviewHeader stats bar
- Wire Expand all button in ReviewHeader to expandAll toggle state
- Update FileCard to use FileDiffDetail and status (replaces changeType)
- Update ReviewSidebar to use file.status instead of file.changeType
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates diff-cache.ts module with generic DiffCache<T> class (TTL, prefix
invalidation, env-var configuration) and exports phaseMetaCache / fileDiffCache
singletons. Wires cache into getPhaseReviewDiff via getHeadCommitHash on
BranchManager. Adds 6 unit tests for DiffCache and 5 integration tests
verifying cache hit/miss behaviour, prefix invalidation, and NOT_FOUND guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prepares the review components for the backend phase that returns
metadata-only file lists from getPhaseReviewDiff. FileDiff now holds
only path/status/additions/deletions; FileDiffDetail extends it with
hunks. Renames changeType→status and adds 'binary' to the union.
Also fixes two pre-existing TypeScript errors: InitiativeReview was
passing an unknown `comments` prop to DiffViewer (should be
commentsByLine), and ConflictResolutionPanel destructured an unused
`agent` variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds DiffCache<T> module, extends BranchManager with getHeadCommitHash,
and wires phase-level caching into getPhaseReviewDiff and getFileDiff.
Cache is invalidated in ExecutionOrchestrator after each task merges into
the phase branch, ensuring stale diffs are never served after new commits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds windowed rendering to FilesView in ReviewSidebar.tsx using
react-window 2.x (List component). File lists with more than 50 rows
render only visible items, keeping the DOM lean for large diffs.
- Install react-window 2.x and @types/react-window in apps/web
- Flatten directory-grouped file tree into a typed Row[] array via useMemo
- Use VariableSizeList-equivalent react-window 2.x List with rowHeight fn
(32px for dir-headers, 40px for file rows); falls back to plain DOM
render for ≤50 rows to avoid overhead on small diffs
- Directories are collapsible: clicking the dir-header toggles collapse,
removing its file rows from the Row[] and from the virtual list
- Preserve sidebar scroll offset across Files ↔ Commits tab switches via
filesScrollOffsetRef passed from ReviewSidebar into FilesView
- Clicking a file calls listRef.scrollToRow({ index, align: "smart" })
to keep the clicked row visible in the virtual list
- Root-level files (directory === "") render without a dir-header,
preserving existing behavior
- Add resolve.dedupe for react/react-dom in vitest.config.ts to prevent
duplicate-React errors after local workspace package installation
- Add 6 Vitest + RTL tests covering: large-list DOM count, small-list
fallback, collapse, re-expand, tab-switch smoke, root-level files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add single-chunk equivalence test (≤200 lines produces complete token
map with no missing line entries) and AbortController abort spy test
(unmount during chunked fallback triggers abort signal) to cover the
remaining spec scenarios not included by the implementation branch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites getPhaseReviewDiff to return file-level metadata (path, status,
additions, deletions) instead of a raw diff string, eliminating 10MB+
payloads for large repos. Adds getFileDiff for on-demand per-file hunk
content with binary detection via numstat. Multi-project initiatives
prefix file paths with the project name to avoid collisions.
Adds integration tests that use real local git repos + in-memory SQLite
to verify both procedures end-to-end (binary files, deleted files,
spaces in paths, error cases).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a 2-worker pool in use-syntax-highlight.ts so shiki tokenisation
runs off the main thread. Callers continue to receive null while the
worker is in flight and a LineTokenMap once it resolves — no caller
changes needed.
Fallback: if Worker construction is blocked (e.g. CSP), the hook falls
back to the existing createHighlighter singleton but processes 200 lines
at a time, yielding between chunks via scheduler.yield()/setTimeout(0)
to avoid long tasks.
Also adds worker.format:'es' to vite.config.ts (required when the app
uses code-splitting) and covers all paths with Vitest tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds FileStatEntry type and two new primitives to the BranchManager
port and SimpleGitBranchManager adapter, enabling split diff
procedures in the tRPC layer without returning raw multi-megabyte diffs.
- FileStatEntry captures path, status, additions/deletions, oldPath
(renames), and optional projectId for multi-project routing
- diffBranchesStat uses --name-status + --numstat, detects binary
files (shown as - / - in numstat), handles spaces in filenames
- diffFileSingle returns raw unified diff for a single file path
Adds FileStatEntry type and two new primitives to the BranchManager
port and SimpleGitBranchManager adapter, enabling split diff
procedures in the tRPC layer without returning raw multi-megabyte diffs.
- FileStatEntry captures path, status, additions/deletions, oldPath
(renames), and optional projectId for multi-project routing
- diffBranchesStat uses --name-status + --numstat, detects binary
files (shown as - / - in numstat), handles spaces in filenames
- diffFileSingle returns raw unified diff for a single file path
Build a Map<string, ReviewComment[]> once in ReviewTab and thread it
down through DiffViewer → FileCard → HunkRows, replacing O(n) filter
calls with O(1) map lookups. With 200 comments and 5000 diff lines,
this reduces ~1M iterations per render cycle to ~5K.
Key: "${filePath}:${lineNumber}:${lineType}" for line-level comments,
"${filePath}:file" for file-level (lineNumber === null) comments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the original design document / brainstorm with a focused project
README covering what Codewalkers does, getting started, architecture,
supported providers, CLI reference, workflow, development, testing, and
links to detailed docs.
Replaces the placeholder body in apps/web/src/routes/hq.tsx with the
full Headquarters page. The component fetches from getHeadquartersDashboard,
subscribes to live SSE invalidations via useLiveUpdates, and renders the
four action sections or an empty state. Includes skeleton loading and
error-with-retry states. Also adds apps/web/src/components/ui/skeleton.tsx
(standard shadcn component not yet in the project).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Derives currentTodos from the most recent TodoWrite tool_call on each render
- Renders a TASKS strip between the header and scroll area with status icons
(CheckCircle2 for completed, Loader2/animate-spin for in_progress, Circle for pending)
- Caps the strip at 5 rows and shows "+ N more" for overflow
- Updates collapsed tool_result preview to show "{subagent_type} result" for
Task tool results instead of the raw first-80-chars substring
- Adds 10 new tests covering all todo strip states and Task preview variants
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extend ParsedMessage.meta with toolInput to expose raw tool arguments
- Add toolUseRegistry to correlate tool_result blocks back to originating tool_use
- Set toolInput on tool_call messages and populate meta.toolName/toolInput on tool_result
- Fix tool_result with is_error:true now correctly produces type "error"
- Add catch-all for unknown top-level event types (emits system message)
- Add catch-all for unknown assistant content block types (emits system message)
- Add unit tests covering all 8 scenarios including regression cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tool_result messages now render as a single collapsible preview line
(ChevronRight + first 80 chars) and expand on click to show full content.
system messages drop the Badge/border-l and render as a dim mono inline
line. expandedResults state resets when agentId changes.
Adds full test coverage in AgentOutputViewer.test.tsx (9 tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the five section components consumed by the HQ page:
- HQWaitingForInputSection, HQNeedsReviewSection, HQNeedsApprovalSection,
HQBlockedSection, HQEmptyState with typed props from RouterOutputs.
- Shared types.ts defines the 5 item types from getHeadquartersDashboard.
- Adds RouterOutputs export to trpc.ts via inferRouterOutputs<AppRouter>.
- 22 unit tests verify rendering, navigation CTAs, truncation, and edge cases.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aggregates all user-blocking action items into a single composite query:
- waitingForInput: agents paused on questions (oldest first)
- pendingReviewInitiatives: initiatives awaiting content review
- pendingReviewPhases: phases awaiting diff review
- planningInitiatives: active initiatives with all phases pending and no running agents
- blockedPhases: phases in blocked state with optional last-message snippet
Wired into appRouter and covered by 10 unit tests using in-memory Drizzle DB
and an inline MockAgentManager (no real processes required).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create apps/web/src/routes/hq.tsx with HeadquartersPage shell component
- Add "HQ" as first entry in AppLayout.tsx navItems array
- Change root redirect from /initiatives to /hq
- Regenerate routeTree.gen.ts with the new /hq route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
retryBlockedTask was not in the centralized invalidation map, so the
mutation succeeded server-side but the UI never refreshed. Also changed
the button to wait for the mutation response before closing the slide-over
and show a loading spinner.
The `get(id)` method on SimpleGitWorktreeManager used `path.endsWith(id)`
to find worktrees. Since all agents working on the same project create
worktrees with the same project name suffix (e.g., "codewalk-district"),
cleanup for one agent could match and delete another agent's worktree.
Fix: match on `basename(worktreesDir)/id` so each manager's lookups are
scoped to its own worktree base directory.
- Adds syncAllMutation calling trpc.syncAllProjects once for all cards
- Shows Sync All button in header when ≥1 project exists (hidden otherwise)
- Propagates isPending to ProjectCard.syncAllPending to disable per-card sync
- On success: toasts all-success or failure count; invalidates listProjects + getProjectSyncStatus per project
- On network error: toasts Sync failed with err.message
- Adds unit tests for ProjectSyncManager.syncAllProjects: empty list, all succeed, partial failure, result shape, and failure counting logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously a single "that name or URL" message was thrown regardless of which
column violated uniqueness. Now the catch block inspects the error string from
SQLite to emit a name-specific or url-specific message, with a generic fallback
when neither column can be identified.
Adds vitest tests covering all four scenarios: name conflict, url conflict,
unknown column conflict, and non-UNIQUE error passthrough.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>