Commit Graph

746 Commits

Author SHA1 Message Date
Eyejoker
42079d5796 refactor: remove host-mode reviewer, container-only
Reviewer always runs in Docker container. No more snapshot file copy —
reviewer mounts the owner workspace directly as read-only. Removes
REVIEWER_CONTAINER_ENABLED toggle and host-mode fallback code.
2026-03-30 00:39:50 +09:00
Eyejoker
53ed437034 fix: handle directory-to-non-directory conflict in copySnapshotPaths
When git ls-files returns directory entries (nested git repos),
cpSync fails trying to overwrite a file placeholder with a directory.
Now removes the conflicting target file before copying.
2026-03-30 00:36:36 +09:00
Eyejoker
09b2ec2b8e fix: skip directories in reviewer snapshot fingerprint to prevent EISDIR
git ls-files can return directory entries (e.g. submodules). readFileSync
on these causes EISDIR error, blocking reviewer trigger for all non-brain
paired channels.
2026-03-30 00:33:44 +09:00
Eyejoker
3899271b9a fix: sync Codex skills to ~/.agents/skills/ instead of .codex/skills/
Codex CLI reads skills from ~/.agents/skills/ (user-level) and
{workDir}/.agents/skills/ (project-level), not from .codex/skills/.
Skills were being copied to the wrong path and never discovered.
2026-03-30 00:26:12 +09:00
Eyejoker
eb40971d4c fix: prevent duplicate final message when progress already shows the same text
When intermediate text was displayed as a progress message and the final
result had the same content, both the progress message and a separate
final message were sent to Discord. Now detects when the progress message
already shows the final text and finalizes in-place without sending a
duplicate.
2026-03-30 00:14:47 +09:00
Eyejoker
60c3dd5fb9 fix: await onOutput chain in container runner to prevent duplicate delivery
Container runner was calling onOutput with fire-and-forget (void),
so work items were still in 'produced' status when the drain loop
checked after container exit. This caused the same message to be
delivered through both reviewer and owner channels.

Now chains onOutput calls and waits for completion before resolving,
matching the host-process runner's outputChain pattern.
2026-03-30 00:05:42 +09:00
Eyejoker
382c9f49d4 fix: bind credential proxy to docker0 interface on Linux
Proxy was binding to 127.0.0.1 which is unreachable from Docker
containers. Now uses PROXY_BIND_HOST (detects docker0 IP on Linux)
so containers can reach the proxy via host.docker.internal.
2026-03-29 23:56:46 +09:00
Eyejoker
6036fd988b fix: remap host paths to container mount points for EJCLAW_WORK_DIR and CLAUDE_CONFIG_DIR
Host paths passed via envOverrides don't exist inside the container,
causing ENOENT when the SDK tries to spawn with a non-existent CWD.
Now remaps EJCLAW_WORK_DIR to /workspace/project and CLAUDE_CONFIG_DIR
to /home/node/.claude (matching the container mount points).
2026-03-29 23:52:32 +09:00
Eyejoker
29ab284c60 fix: use stable per-channel owner worktree to preserve session across tasks
Owner worktree path was per-task (data/workspaces/{folder}/tasks/{taskId}/owner/),
causing Claude SDK to treat each new task as a different project and lose session
context. Now uses a stable per-channel path (data/workspaces/{folder}/owner/).

- Channel isolation preserved (different folders get different worktrees)
- Session persists across tasks (same path = same Claude SDK project)
- Reviewer snapshot still reads from owner worktree correctly
- Fix stale approvedHead reference from cherry-pick conflict
2026-03-29 23:31:14 +09:00
Eyejoker
504be9e41a refactor: use tree hash + git diff for post-approval change detection
- Replace commit hash comparison with git tree hash (HEAD^{tree}) to
  avoid false re-review triggers on commit-only operations
- Use git diff --quiet for actual file change detection
- Update source_ref on failed reviewer execution with done verdict
- Add tests for empty-commit finalize and code-change re-review
- Document Codex reviewer bash mutation gap in reviewer-runtime
2026-03-29 23:24:48 +09:00
Eyejoker
2e4de9cca8 fix: isolate reviewer Claude config dir from owner
Reviewer was picking up owner's cached session from the shared
.claude directory, causing "No conversation found" errors. Now uses
data/sessions/{folder}-reviewer/.claude/ as a separate config dir.
2026-03-29 23:05:24 +09:00
Eyejoker
c28fba0478 feat: re-review on post-approval changes, container improvements
- Compare git HEAD at reviewer approval vs owner finalize to detect
  new code changes after DONE verdict. Re-trigger reviewer if changed,
  complete if unchanged (commit-only is fine).
- Record approved HEAD in source_ref when reviewer says DONE
- Add pnpm store mount, pre-flight checks, idle timeout for containers
- Clean up orphaned containers on shutdown
2026-03-29 23:00:56 +09:00
Eyejoker
b965a7e43d feat: add pnpm store mount, pre-flight checks, and idle timeout for reviewer containers
- Detect pnpm projects and mount global store read-only so hardlinks resolve
- npm/yarn/bun need no extra mounts (node_modules is self-contained)
- Pre-flight check: verify Docker running + image exists (cached after first call)
- Add activity-based idle timeout (reset on stdout/stderr like host runner)
- Clean up orphaned containers on shutdown (not just startup)
2026-03-29 22:46:20 +09:00
Eyejoker
88fa0858ea feat: strengthen reviewer prompts with critical review, completion status, and stagnation awareness
- Expand claude/codex-review paired-room prompts with full review structure
- Add completion status protocol (DONE/DONE_WITH_CONCERNS/BLOCKED/NEEDS_CONTEXT)
- Add stagnation detection (spinning, oscillation, diminishing returns)
- Add communication discipline (fact-based, verification-based)
- Remove <internal> tag instructions from owner platform prompt
- SSOT: all 3 reviewer prompts share the same structure
2026-03-29 22:46:09 +09:00
Eyejoker
8049db0d47 fix: separate reviewer session from owner to preserve review context
Reviewer was starting with undefined sessionId every turn, losing all
conversation context. Now uses `group.folder:reviewer` as the session key
so owner and reviewer maintain independent session histories.

Also updates README to reflect unified service architecture and paired
review workflow.
2026-03-29 22:22:45 +09:00
Eyejoker
33baf2970d fix: strip <internal> tags before verdict detection, add Approved/LGTM as done markers 2026-03-29 21:34:08 +09:00
Eyejoker
b33cf6c058 debug: forward runner query lifecycle logs to INFO for diagnosis 2026-03-29 21:29:06 +09:00
Eyejoker
8119b0bb8f fix: treat execution as success when output was delivered despite null result 2026-03-29 21:24:49 +09:00
Eyejoker
f28b8861bf fix: match verdict markers only at start of first line to prevent false positives 2026-03-29 21:22:45 +09:00
Eyejoker
b8075be92d fix: check reviewer verdict on failed execution to prevent infinite loop 2026-03-29 21:20:16 +09:00
Eyejoker
4f87c840ba refactor: remove /review command and clean up unused imports 2026-03-29 21:17:14 +09:00
Eyejoker
787c05a561 fix: /compact masked by room role header in both runners
prependRoomRoleHeader was called BEFORE /compact detection, turning
"/compact" into "[PAIRED ROOM: owner]\n/compact" which failed the
exact match check. Now session commands are detected first.
2026-03-29 21:14:04 +09:00
Eyejoker
83ddc4faff fix: reset paired task after all session commands to prevent reviewer auto-trigger 2026-03-29 21:09:32 +09:00
Eyejoker
9127841359 fix: /clear resets paired task to prevent reviewer auto-triggering after session clear 2026-03-29 21:07:34 +09:00
Eyejoker
d8cb4b691e feat: verdict-based ping-pong control with merge_ready finalization
- DONE → merge_ready → owner final turn (commit/push) → completed
- DONE_WITH_CONCERNS → active → owner addresses → ping-pong continues
- BLOCKED/NEEDS_CONTEXT → completed (escalate to user, stop ping-pong)
- Regular feedback → active → ping-pong continues
- Owner detects merge_ready status and finalizes autonomously
2026-03-29 20:59:08 +09:00
Eyejoker
16d20fe627 feat: paired review system overhaul — unified service, configurable agents, silent output removal
- Remove UNIFIED_MODE legacy: single service manages all 3 Discord bots
- Add OWNER_AGENT_TYPE / REVIEWER_AGENT_TYPE env vars for configurable agent selection
- Fix Discord channel routing: reviewer output goes through correct bot (discord/discord-review)
- Fix channel name assignment: explicit names prevent agentTypeFilter collision
- Remove silent output suppression system: harness-level protections replace prompt workarounds
- Add reviewer approval detection: DONE marker on first line stops ping-pong
- Include user's original message in reviewer prompt for context
- Fix round_trip_count auto-reset on new user message
- Fix session ID conflict: reviewer doesn't reuse owner's session
- Fix pending progress text flush in runner on close sentinel
- Promote buffered intermediate text to final result when result event has no text
- Remove legacy files: .env.codex.example, .env.codex-review.example, migrate-unify.cjs
- Update CLAUDE.md: server-side build deployment, unified architecture docs
2026-03-29 20:50:32 +09:00
Eyejoker
8a4d83e2c1 fix: let both bots see each other's messages in paired rooms for natural ping-pong 2026-03-29 19:22:01 +09:00
Eyejoker
a8bf6b806d fix: run reviewer turn on review_ready status even without new messages 2026-03-29 19:19:47 +09:00
Eyejoker
4ec7b55105 fix: reset task to active on failed execution to prevent stuck state 2026-03-29 19:17:19 +09:00
Eyejoker
fcb854e9de fix: move tmpfs to /tmp instead of inside read-only mount 2026-03-29 19:16:23 +09:00
Eyejoker
c78c97cff7 refactor: use host runners/agent-runner/ in container instead of duplicate code 2026-03-29 19:15:03 +09:00
Eyejoker
aae7714062 fix: align container SDK version with host runner 2026-03-29 19:13:27 +09:00
Eyejoker
a8a21045dd fix: use claude-agent-sdk query() API in container runner 2026-03-29 19:12:20 +09:00
Eyejoker
9a365854c5 fix: add @types/node to container agent-runner 2026-03-29 19:11:06 +09:00
Eyejoker
c36ef36940 fix: slim down Dockerfile apt packages for compatibility 2026-03-29 19:10:13 +09:00
Eyejoker
3e607d7344 fix: enqueue next turn after paired execution so reviewer runs without new message 2026-03-29 19:08:07 +09:00
Eyejoker
4dc4b3bc01 fix: route paired room turns by task status instead of SERVICE_ID
In unified mode, SERVICE_SESSION_SCOPE doesn't match the lease's
owner/reviewer service IDs. Determine the effective role from the
paired task status: review_ready/in_review → reviewer, otherwise → owner.
This restores the owner↔reviewer ping-pong in unified single-process mode.
2026-03-29 19:04:51 +09:00
Eyejoker
9e0de1297c fix: /clear now resets both owner and reviewer sessions in paired rooms 2026-03-29 19:00:42 +09:00
Eyejoker
a1e5418a2f feat: auto-trigger reviewer after owner completion with round trip limit
- Owner finishes → auto mark review_ready → reviewer executes
- Reviewer finishes → task back to active → owner can respond
- Automatic ping-pong until consensus or round trip limit
- PAIRED_MAX_ROUND_TRIPS env var (default 10, 0 = unlimited)
- round_trip_count tracked per task in DB
2026-03-29 18:57:16 +09:00
Eyejoker
338c1f6a1d fix: restrict stop command to /stop only 2026-03-29 18:48:06 +09:00
Eyejoker
05c06fc467 feat: graceful agent abort on SIGTERM via AbortController
When /stop sends SIGTERM, the runner now calls AbortController.abort()
on the Claude SDK query, allowing graceful cleanup of in-flight API
requests before the process exits. Falls back to SIGKILL after 5s.
2026-03-29 18:47:26 +09:00
Eyejoker
2ec90fe086 feat: add /stop command to kill running agent process
- /stop, /cancel, /kill aliases all supported
- SIGTERM first (allows graceful AbortController cleanup), SIGKILL after 5s
- Wired through session-commands → GroupQueue.killProcess()
2026-03-29 18:44:37 +09:00
Eyejoker
d01f98bd61 feat: unify 3 bot services into single process
- Add UNIFIED_MODE flag (default on, disable with UNIFIED_MODE=0)
- Register all 3 Discord bots in one process (claude, codex, review)
- Load all registered groups regardless of agent_type (codex/owner priority)
- Start credential proxy and container cleanup at unified startup
- shouldServiceProcessChat returns true in unified mode
- Add findChannelByName for role-based response routing
- Backward compatible: UNIFIED_MODE=0 restores per-service behavior
2026-03-29 18:37:17 +09:00
Eyejoker
fc9f2867b9 refactor: simplify paired review system and add reviewer container isolation
Phase 1 — Strip over-engineering (-3,665 lines):
- DB tables: 7 → 3 (remove executions, approvals, artifacts, events)
- Task statuses: 11 → 5 (active, review_ready, in_review, merge_ready, completed)
- Remove plan governance, event sourcing, gate/verdict, freshness guards
- paired-execution-context: 980 → 299 lines
- Session commands: remove /risk, /plan, /approve-plan, /request-plan-changes

Phase 2 — Container isolation for reviewers:
- Add container-runtime.ts (Docker abstraction)
- Add credential-proxy.ts (API key injection without container exposure)
- Add container-runner.ts (reviewer-specific read-only mount + tmpfs)
- Add container/Dockerfile + agent-runner (Chromium, Claude Code, Codex)
- agent-runner.ts: auto-route reviewer execution to container mode
2026-03-29 18:24:56 +09:00
Eyejoker
3dd41f749e fix: harden stack restart migration 2026-03-29 07:33:13 +09:00
Eyejoker
ca578d1627 feat: add stack restart orchestration 2026-03-29 07:08:01 +09:00
Eyejoker
0e12a560a4 feat: finalize paired tasks on deploy completion 2026-03-29 06:42:03 +09:00
Eyejoker
dad6f50937 feat: enforce paired reviewer gate verdicts 2026-03-29 06:03:45 +09:00
Eyejoker
29310ce67f feat: recover paired executions after restart 2026-03-29 05:06:25 +09:00
Eyejoker
bb30330c66 feat: add paired execution freshness guard 2026-03-29 04:37:42 +09:00