Commit Graph

153 Commits

Author SHA1 Message Date
Lukas May
940b0f8ed2 feat: Add errands persistence layer — repository port, Drizzle adapter, migration, and tests
- Add errand-repository.ts port with ErrandRepository, ErrandWithAlias, ErrandStatus types
- Add DrizzleErrandRepository adapter with create, findById (left-joins agents for alias),
  findAll (optional projectId/status filters, desc by createdAt), update, delete
- Wire exports into repositories/index.ts and repositories/drizzle/index.ts
- Add migration 0035_faulty_human_fly.sql (CREATE TABLE errands) and drizzle snapshot
- Add 13 tests covering CRUD, filtering, ordering, agentAlias join, cascade/set-null FK behaviour
- Update docs/database.md to document the errands table and ErrandRepository

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 12:35:06 +01:00
Lukas May
5d1292c7ad fix: Use update-ref for fast-forward to avoid dirty working tree failures
fastForwardBranch used git merge --ff-only which fails when the clone
has uncommitted files. This caused the ff to be silently skipped, the
merge to proceed on stale main, and the push to fail (non-fast-forward).

Switched to update-ref which only moves the branch pointer without
touching the working tree.
2026-03-06 12:34:21 +01:00
Lukas May
6a76e17cef feat: Add errands table, errand agent mode, and push rollback on merge failure
- Add `errands` table to schema with status enum and relations to agents/projects
- Add `errand` mode to agents.mode enum
- Add push rollback in orchestrator: if push fails after merge, reset to previousRef to preserve the review diff
- Extend MergeResult with previousRef for rollback support; update branch-manager and simple-git-branch-manager
- Add orchestrator tests for push rollback behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 12:31:41 +01:00
Lukas May
00e426ac00 fix: Roll back merge when push fails in initiative approval
When merge_and_push failed at the push step, the local defaultBranch ref
was left pointing at the merge commit. This made the three-dot diff
(defaultBranch...initiativeBranch) return empty because main already
contained all changes — causing the review tab to show "no changes."

Now mergeBranch returns the previous ref, and approveInitiative restores
it on push failure. Also repaired the corrupted clone state.
2026-03-06 12:31:35 +01:00
Lukas May
eac03862e3 fix: Prevent lost task completions after server restart
Three bugs causing empty phase diffs when server restarts during agent
execution:

1. Startup ordering race: reconcileAfterRestart() emitted agent:stopped
   before orchestrator registered listeners — events lost. Moved
   reconciliation to after orchestrator.start().

2. Stuck in_progress tasks: recoverDispatchQueues() only re-queued
   pending tasks. Added recovery for in_progress tasks whose agents
   are dead (not running/waiting_for_input).

3. Branch force-reset destroys work: git branch -f wiped commits when
   a second agent was dispatched for the same task. Now checks if the
   branch has commits beyond baseBranch before resetting.

Also adds:
- agent:crashed handler with auto-retry (MAX_TASK_RETRIES=3)
- retryCount column on tasks table + migration
- retryCount reset on manual retryBlockedTask()
2026-03-06 12:19:59 +01:00
Lukas May
a69527b7d6 fix: Remove upward box-shadow on ReviewHeader that covers tab bar
The sticky ReviewHeader had shadow-[0_-50px_0_0_hsl(var(--background))]
which painted a 50px background-color rectangle upward, overlapping the
tab navigation bar (only ~12px away). The header's bg-card is already
opaque, making the shadow unnecessary for scroll coverage.
2026-03-06 12:18:31 +01:00
Lukas May
17f92040c7 fix: Ensure agents write signal.json to the correct directory
Two additional fixes to prevent agents from writing .cw/output/ in the
wrong location:

1. Always create .cw/output/ at the agent workdir root during spawn,
   even when no inputContext is provided. This gives the agent a visible
   anchor directory so it doesn't create one inside a project subdir.

2. Add absolute output path instruction to the workspace layout prompt
   for multi-project agents, explicitly telling them to write .cw/output/
   relative to the workdir root, not their current cd location.
2026-03-06 12:14:37 +01:00
Lukas May
9f5715558e fix: Auto-dismiss conflict panel and re-check mergeability on completion
Instead of showing a manual "Re-check Mergeability" button after the
conflict agent finishes, auto-dismiss the agent and trigger mergeability
re-check immediately when the state transitions to completed.
2026-03-06 12:14:16 +01:00
Lukas May
f4dbaae0e3 fix: Guard worktree creation against branch === baseBranch
Throws if branch and baseBranch are identical, preventing
git branch -f from force-resetting shared branches (like
the initiative branch) when accidentally passed as both.
2026-03-06 12:12:32 +01:00
Lukas May
b853b28751 fix: Resolve agent workdir probing for initiative project subdirectories
Conflict-resolution agents (and any initiative-based agent) can write
.cw/output/signal.json inside a project subdirectory (e.g.
agent-workdirs/<name>/codewalk-district/.cw/output/) rather than the
parent agent workdir. This caused two failures:

1. spawnInternal wrote spawn-diagnostic.json before registering the
   agent in activeAgents and starting pollForCompletion. If the .cw/
   directory didn't exist (no inputContext provided), the write threw
   ENOENT, orphaning the running process with no completion monitoring.

2. resolveAgentCwd in cleanup-manager and output-handler only probed
   for a workspace/ subdirectory (standalone agents) but not project
   subdirectories, so reconciliation and completion handling couldn't
   find signal.json and marked the agent as crashed.

Fixes:
- Move activeAgents registration and pollForCompletion setup before
  the diagnostic write; make the write non-fatal with mkdir -p
- Add project subdirectory probing to resolveAgentCwd in both
  cleanup-manager.ts and output-handler.ts
2026-03-06 12:03:20 +01:00
Lukas May
2814c2d3b2 fix: Fetch remote before merge/push in initiative approval
approveInitiative was merging and pushing with a stale local
defaultBranch, causing "rejected (fetch first)" when origin/main
had advanced since the last project sync. Now fetches remote and
fast-forwards the target branch before merging.
2026-03-06 11:59:16 +01:00
Lukas May
1e723611e7 feat: Allow editing review comments
Add update method to ReviewCommentRepository, updateReviewComment tRPC
procedure, and inline edit UI in CommentThread. Edit button appears on
user-authored comments (not agent comments) when unresolved. Uses the
existing CommentForm with a new initialValue prop.
2026-03-06 11:58:08 +01:00
Lukas May
49970eb1d7 fix: Use overflow-clip instead of overflow-hidden on FileCard
overflow-hidden creates a scroll container that breaks sticky positioning
for file headers. overflow-clip provides the same visual clipping for
rounded corners without affecting the scroll/sticky context.
2026-03-06 11:44:45 +01:00
Lukas May
0c04a1d273 fix: Prevent conflict resolution agent from destroying initiative branch
spawnConflictResolutionAgent was passing the initiative branch as branchName,
causing SimpleGitWorktreeManager.create() to force-reset it to the target
branch. Now spawns on a unique temp branch based off the initiative branch,
with the agent using git update-ref to advance the initiative branch after
resolving conflicts. Also fixes stale diff/commits cache after resolution.
2026-03-06 11:40:22 +01:00
Lukas May
f428ec027e fix: Sticky file headers sit below review header using CSS variable
Sets --review-header-h on the card wrapper from measured header height.
FileCard reads it for sticky top offset so file headers dock just below
the review header instead of overlapping it.
2026-03-06 11:36:28 +01:00
Lukas May
c87aac44cc fix: Cover transparent gap above sticky header with upward box-shadow
Uses a 50px upward box-shadow in bg-background color to paint over the
main padding gap that shows above the stuck review header.
2026-03-06 11:33:31 +01:00
Lukas May
cc181ee6ba fix: Parse merge-tree output from stdout instead of catch block
simple-git's .raw() resolves successfully even on exit code 1,
returning stdout content. git merge-tree --write-tree outputs
CONFLICT markers to stdout (not stderr), so the catch block
never fired and conflicts were reported as clean merges.
2026-03-06 11:27:32 +01:00
Lukas May
5b497b84a0 fix: Restore sticky header and sidebar by simplifying layout
Removed wrapper divs that broke sticky positioning. ReviewHeader now
accepts a ref prop directly, with sticky top-0 on its own root element.
Card wrapper restored as the tall containing block so both header and
sidebar have room to stick within it.
2026-03-06 11:26:43 +01:00
Lukas May
4664644cda fix: Use pseudo-element to cover all transparent space above sticky header
Replaces negative margin hack with a ::before that extends upward from
the sticky header to paint bg-background over the main padding gap.
2026-03-06 11:22:30 +01:00
Lukas May
19cd0a2cb0 fix: Cover transparent gap above sticky review header
Use negative margin to pull sticky header into the parent space-y-3 gap,
with matching padding and bg-background to paint over it when stuck.
2026-03-06 11:21:08 +01:00
Lukas May
6a2f9c6d57 migration: Drop orphaned approval columns from initiatives and tasks
merge_requires_approval (initiatives) and requires_approval (tasks)
were removed from schema.ts in the task-approval removal but left in
the DB because 0030 assumed SQLite couldn't DROP COLUMN. SQLite 3.35+
supports it. These orphaned columns caused the old stale-build approval
code path to silently set detail tasks to pending_approval, stranding
them and blocking phase completion.
2026-03-06 11:20:05 +01:00
Lukas May
01f2279735 fix: Eliminate whitespace above sticky review header
Moved card border/rounding onto the sticky header wrapper itself so it
scrolls flush to top-0 with no gap. The body grid gets its own border-x
and border-b to preserve the card appearance. ResizeObserver now measures
border-box size for accurate sidebar offset.
2026-03-06 11:17:29 +01:00
Lukas May
6cf6bd076f feat: Add merge conflict detection and agent resolution in initiative review
Pre-merge mergeability check via `git merge-tree --write-tree` (dry-run, no
side effects). When conflicts exist the "Merge & Push" button is disabled and
a ConflictResolutionPanel shows conflict files with options to resolve manually
or spawn a conflict-resolution agent. Agent questions appear inline via
QuestionForm; on completion the mergeability re-checks automatically.

New server-side: MergeabilityResult type, BranchManager.checkMergeability,
conflict-resolution prompt, checkInitiativeMergeability query,
spawnConflictResolutionAgent mutation, getActiveConflictAgent query.

New frontend: useConflictAgent hook, ConflictResolutionPanel component,
mergeability badge + panel integration in InitiativeReview.
2026-03-06 11:17:25 +01:00
Lukas May
3a01b9e9ca fix: Restore sticky positioning on header wrapper div 2026-03-06 11:15:25 +01:00
Lukas May
09624e9cb7 fix: Sidebar accounts for sticky header height via ResizeObserver
Measures the review header dynamically and offsets the sidebar's sticky
top and max-height accordingly, eliminating the gap when scrolled.
2026-03-06 11:15:02 +01:00
Lukas May
d4b466ce6d fix: Make review sidebar sticky to viewport with internal scrolling
Removed overflow-hidden from grid container and changed sidebar to
sticky positioning with viewport-relative max-height. Sidebar content
scrolls independently while staying visible during diff navigation.
2026-03-06 11:11:46 +01:00
Lukas May
50043f4c61 fix: Use instant scroll for discussion navigation 2026-03-06 11:10:50 +01:00
Lukas May
3fcfa61914 fix: Scroll to exact comment location when clicking sidebar discussions
Adds data-comment-id attributes to comment thread rows so clicking a
discussion in the sidebar scrolls directly to the comment, not just the
file card. Includes a brief ring highlight on the target row.
2026-03-06 11:09:07 +01:00
Lukas May
14d09b16df fix: Phase completion check runs regardless of branch/merge status
handleTaskCompleted and handlePhaseAllTasksDone both bailed early when
initiative had no branch, silently skipping phase status transitions.
Also, merge failures would skip the phase completion check entirely.

- Decouple phase completion check from branch existence
- Wrap merge in try/catch so phase check runs even if merge fails
- Route updateTaskStatus through dispatchManager.completeTask when
  completing, so the task:completed event fires for orchestration
2026-03-06 11:07:01 +01:00
Lukas May
157fa445c5 fix: Show individual discussion threads in review sidebar with navigation
Discussions section was showing only aggregate counts (total/resolved/unresolved)
with no way to see or navigate to individual threads. Now lists each root comment
with file:line location, body preview, resolved status, and reply count. Clicking
a discussion scrolls to its file in the diff viewer.
2026-03-06 11:03:27 +01:00
Lukas May
1bc3f85d6a fix: Merge worktree conflict when target branch already checked out
Use a temp branch + update-ref to avoid "already checked out" error
when merging into the default branch. Also show actual branch name
in the Merge & Push button instead of "Default".
2026-03-06 10:54:42 +01:00
Lukas May
bdc95bcb26 fix: Handle existing branch in worktree creation
When re-dispatching tasks, the branch from a previous run may still
exist. Instead of failing with "a branch named X already exists",
reset the existing branch to the base and check it out.
2026-03-06 10:54:33 +01:00
Lukas May
1e2819eeff fix: Request changes uses in-app confirmation, requires review comments
- Replace window.prompt with in-app dropdown confirmation (matches merge dialog pattern)
- Disable button when no unresolved comments exist (comments-only, no summary)
- Remove initiative-level request changes button (no comment system there)
2026-03-06 10:43:36 +01:00
Lukas May
4656627a59 fix: Restore drizzle-kit generate by syncing snapshot baseline
Snapshots were stale since migration 0008. Generated a schema-derived
snapshot at 0032 so drizzle-kit generate works again (zero diff on
current schema.ts). Also fixed migration 0032 to use statement-breakpoint
separator required by better-sqlite3.

- Added 0032_snapshot.json derived from current schema.ts
- Fixed 0032 SQL to use --> statement-breakpoint between statements
- Updated CLAUDE.md and database-migrations.md with correct workflow
2026-03-06 10:36:07 +01:00
Lukas May
eb667dd3d7 fix: Register migration 0032 in drizzle journal
The migration file existed but wasn't in _journal.json, so drizzle-kit's
migrator never applied it. Adds the journal entry for 0032_add_comment_threading.
2026-03-06 10:27:44 +01:00
Lukas May
7695604da2 feat: Add threaded review comments + agent comment responses
Introduces GitHub-style threaded comments via parentCommentId self-reference.
Users and agents can reply within comment threads, and review agents receive
comment IDs so they can post targeted responses via comment-responses.json.

- Migration 0032: parentCommentId column + index on review_comments
- Repository: createReply() copies parent context, default author 'you' → 'user'
- tRPC: replyToReviewComment procedure, requestPhaseChanges passes threaded comments
- Orchestrator: formats [comment:ID] tags with full reply threads in task description
- Agent IO: readCommentResponses() reads .cw/output/comment-responses.json
- OutputHandler: processes agent comment responses (creates replies, resolves threads)
- Execute prompt: conditional <review_comments> block when task has [comment:] markers
- Frontend: CommentThread renders root+replies with agent-specific styling + reply form
- Sidebar/ReviewTab: root-only comment counts, reply mutation plumbing through DiffViewer chain
2026-03-06 10:21:22 +01:00
Lukas May
65bcbf1a35 fix: Fix review task completion bug + add initiative-level Request Changes
Critical: review/merge tasks hit an early return in handleTaskCompleted()
that skipped the phase completion check, leaving phases stuck in
in_progress forever. Changed to an if-block wrapping only the merge step.

Also adds requestChangesOnInitiative() which creates/reuses a
"Finalization" phase for initiative-level review feedback, with dedup
guards for both phase and initiative request-changes flows.
2026-03-06 09:41:28 +01:00
Lukas May
1b8e496d39 fix: Switch preview gateway from path-prefix to subdomain routing
Path-prefix routing (`localhost:9100/<id>/`) broke SPAs because absolute
asset paths (`/assets/index.js`) didn't match the `handle_path /<id>/*`
route. Subdomain routing (`<id>.localhost:9100/`) resolves this since
all paths are relative to the root. Chrome/Firefox resolve *.localhost
to 127.0.0.1 natively — no DNS setup needed.
2026-03-05 22:38:00 +01:00
Lukas May
aedf149471 fix: Use container-internal port 80 in gateway Caddyfile
The Caddyfile was using the host port (e.g., 9100) as the Caddy listen
address, but Docker maps host:9100 → container:80. Caddy inside the
container was listening on 9100 while Docker only forwarded to port 80,
causing all health checks to fail with "connection reset by peer".
2026-03-05 22:16:19 +01:00
Lukas May
84250955d1 fix: Show completed phase diffs in review tab
Completed phases showed "No phases pending review" because:
1. Frontend filtered only pending_review phases
2. Server rejected non-pending_review phases
3. After merge, three-dot diff returned empty (merge base moved)

Fix: store pre-merge merge base hash on phase, use it to reconstruct
diffs for completed phases. Frontend now shows both pending_review and
completed phases with read-only mode (Merged badge) for completed ones.
2026-03-05 22:05:28 +01:00
Lukas May
cdb3de353d fix: Use SSE events instead of polling for preview status updates
httpBatchLink batches polling queries behind the long-running
startPreview mutation, so refetchInterval never fires independently.
Replace polling with preview: event invalidation via the existing
useLiveUpdates SSE subscription — preview:building/ready/stopped/failed
events now trigger listPreviews and getPreviewStatus invalidation.
2026-03-05 22:01:16 +01:00
Lukas May
df84a877f2 fix: Prioritize polled preview status over mutation pending state
The startPreview mutation blocks server-side through the entire Docker
build + health check + seed cycle. isPending was checked first, so even
after polling detected containers running, the UI stayed on "Building..."
until the full mutation resolved. Now polled status (running/failed)
takes priority, falling back to isPending only when no status exists.
2026-03-05 21:58:02 +01:00
Lukas May
4958b6624d fix: Refetch previews on start and switch to path-based routing
Two fixes:
- Call previewsQuery.refetch() in startPreview.onSuccess so the UI
  transitions from "building" to the preview link without a page refresh.
- Switch from subdomain routing (*.localhost) to path-based routing
  (localhost:<port>/<id>/) since macOS doesn't resolve wildcard
  localhost subdomains.
2026-03-05 21:56:05 +01:00
Lukas May
0e61c48c86 fix: Use lowercase alphanumeric nanoid for Docker compose project names
Docker compose requires project names to be lowercase alphanumeric
with hyphens/underscores only. The default nanoid alphabet includes
uppercase and special characters, causing build failures.
2026-03-05 21:49:01 +01:00
Lukas May
13e009a82d feat: Add preview controls to initiative-level review
Extract PreviewControls into shared component and wire up preview
start/stop to InitiativeReview header alongside Push Branch and
Merge & Push to Default buttons.
2026-03-05 21:47:06 +01:00
Lukas May
39d92d5de9 fix: Add changeset reconciliation to deleteTask and fix missing invalidation
Same orphaned-changeset pattern as deletePhase: manually deleting all
tasks from a detail changeset now marks it reverted. Also added
deleteTask to the invalidation map (was missing entirely).
2026-03-05 21:35:14 +01:00
Lukas May
ff398f84ac fix: Move useMemo above early returns in ReviewTab to fix hooks ordering crash
The allFiles useMemo was declared after two early-return branches. Approving
the last phase empties pendingReviewPhases, triggering the early return and
causing React to see fewer hooks than the previous render.
2026-03-05 21:35:10 +01:00
Lukas May
7b93cfe7d7 feat: Remove checkpoint task types — per-phase review is sufficient
Checkpoint tasks (human-verify, decision, human-action) silently blocked
auto-dispatch with no UI to resolve them. Per-phase review + initiative
review already cover human verification, making checkpoints redundant.

Removed from: schema, dispatch manager, tRPC validators, detail prompt,
frontend types, tests, and docs.
2026-03-05 21:30:22 +01:00
Lukas May
39bb03e30b fix: Reconcile orphaned changesets when phases are manually deleted
Manually deleting phases left their parent changeset as "applied",
causing the Plan tab to show a stale "Created N phases" banner with
no phases visible.

- deletePhase now checks if all phases from a changeset are gone and
  marks it reverted
- PlanSection filters out dismissed agents so dismissed banners stay
  hidden
- revertChangeSet marks reverted before entity deletion to prevent
  ghost state on partial failure
- deletePhase invalidation now includes listChangeSets
2026-03-05 21:29:38 +01:00
Lukas May
47fa924927 fix: Recover in-memory dispatch queues from DB on server startup
Both phaseQueue and taskQueue are in-memory Maps lost on restart. Now
the orchestrator's start() method scans active initiatives and:
- Re-queues approved phases into the phase dispatch queue
- Re-queues pending tasks for in_progress phases into the task dispatch queue
- Triggers a dispatch cycle if anything was recovered

This fixes stuck phases/tasks after server restarts.
2026-03-05 21:10:32 +01:00