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
This commit is contained in:
Eyejoker
2026-03-29 18:16:28 +09:00
parent 3dd41f749e
commit fc9f2867b9
24 changed files with 1174 additions and 4900 deletions

View File

@@ -42,14 +42,8 @@ import { runAgentForGroup } from './message-agent-executor.js';
import { MessageTurnController } from './message-turn-controller.js';
import { createSuppressToken } from './output-suppression.js';
import {
approveRoomPlan,
finalizeRoomDeployment,
formatRoomReviewReadyMessage,
markRoomReviewReady,
planPairedExecutionRecovery,
recordRoomPlan,
requestRoomPlanChanges,
setRoomTaskRiskLevel,
} from './paired-execution-context.js';
import { buildRoomRoleContext } from './room-role-context.js';
import {
@@ -574,7 +568,7 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
// Session commands are explicit control events, so owner-only and
// reviewer-only commands resolve against the target role rather than
// whichever service happened to pick up the slash command first.
markReviewReady: async (dedupeKey) => {
markReviewReady: async () => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
@@ -584,77 +578,9 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
group,
chatJid,
roomRoleContext,
dedupeKey,
});
return formatRoomReviewReadyMessage(result);
},
finalizeDeployment: async () => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
lease.owner_service_id,
);
return finalizeRoomDeployment({
group,
chatJid,
roomRoleContext,
});
},
setTaskRiskLevel: async (riskLevel, dedupeKey) => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
lease.owner_service_id,
);
return setRoomTaskRiskLevel({
group,
chatJid,
roomRoleContext,
riskLevel,
dedupeKey,
});
},
recordPlan: async (plan, dedupeKey) => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
lease.owner_service_id,
);
return recordRoomPlan({
group,
chatJid,
roomRoleContext,
dedupeKey,
...plan,
});
},
approvePlan: async (dedupeKey) => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
lease.reviewer_service_id ?? lease.owner_service_id,
);
return approveRoomPlan({
group,
chatJid,
roomRoleContext,
dedupeKey,
});
},
requestPlanChanges: async (note, dedupeKey) => {
const lease = getEffectiveChannelLease(chatJid);
const roomRoleContext = buildRoomRoleContext(
lease,
lease.reviewer_service_id ?? lease.owner_service_id,
);
return requestRoomPlanChanges({
group,
chatJid,
roomRoleContext,
note,
dedupeKey,
});
},
},
});
if (cmdResult.handled) return cmdResult.success;