summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-06-04fix(validator): require repository_url for claude/gemini agent typesPeter Stone
ContainerRunner fails at runtime with a non-descriptive error if repository_url is missing. Catch it at task creation time instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04fix(executor): skip checker task for local-runner executionsClaude
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04fix(local-runner): retry on tool-400, fix preamble gate, set summary from ↵Claude Sonnet 4.6
text output - Retry Chat() without tools when error contains "does not support tools" (case-insensitive); tinyllama fast-path still skips the first round-trip. - Pass mcpEnabled=len(tools)>0 to buildAgentInstructions so tool-less models don't receive a planning preamble they can't act on; tools must be decided before messages are built, so reorder accordingly. - Collect all assistant text into fullText across turns; after a non-blocked run, if e.Summary is empty set it to the first 500 chars of fullText so handleRunResult has something to store when report_summary is never called. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04test: fix createTask signature in testsPeter Stone
2026-06-04executor: don't send tool definitions to tinyllama modelsPeter Stone
2026-06-04cli: add --repository-url flag and improve error reportingPeter Stone
2026-06-04feat: execution row click opens unified detail + log viewerPeter Stone
Replace the per-row "View Logs" button with a click handler on the entire execution row. Clicking opens the logs-modal showing a metadata grid (id, status, agent, exit code, cost, times, error, log paths) followed by an inline streaming log viewer. The EventSource is torn down on modal close. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03fix: serve log content for executions in READY and BLOCKED statesPeter Stone
terminalStates in logs.go only included Completed/Failed/TimedOut/Cancelled/ BudgetExceeded. Executions in READY state (task awaiting user accept/reject) fell through both branches, causing the log viewer to emit an immediate done event with no content — visually an empty log panel. BLOCKED is also added: ask_user suspends execution with the log fully written. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03fix: release dispatch slot on all early-return paths in execute()Peter Stone
p.active was incremented by dispatch() before launching execute(), but execute() had four early-return paths (budget gate, dep terminal failure, deps not ready, per-agent limit) that returned before the defer decActiveAgent was registered. Each such return leaked a slot, eventually saturating maxConcurrent and permanently blocking dispatch() on doneCh. Introduce releaseDispatchSlot() (decrements p.active + signals doneCh) and defer it at the very top of execute() and executeResume(), so every exit path releases the dispatch slot. Remove p.active and doneCh management from decActiveAgent so it only handles the per-agent counter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03fix: embed execution log in checker instructions and add noopChannel to testsPeter Stone
The checker task previously had no context about what the agent did — it tried to reach the API (potentially unavailable), search /home for artifacts, and clone the repo, all of which fail for LocalRunner tasks. Now spawnCheckerTask reads the tail of the execution's stdout.log and embeds it directly in the checker's instructions so the checker can verify output without needing Docker, API access, or repo cloning. Also fixes container_test: adds noopChannel to satisfy the AgentChannel parameter added by the OSS Runner interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03merge: integrate oss branch — budget gating, MCP back-channel, events, ↵Peter Stone
loopback-only bind Key features from OSS branch: - Budget gating: rolling per-provider spend caps with BUDGET_EXCEEDED task state - Agent MCP back-channel: runners mint tokens; /mcp endpoint resolves them - Chatbot MCP server at /chatbot/mcp (requires api_token in config) - Event timeline: unified event stream replacing ad-hoc question/summary flows - Loopback-only default bind (127.0.0.1:8484); external_bind_allowed=true to expose - repository_url required on task creation (enforces traceability) - Auto-checker: spawns verification task after each execution Conflict resolutions: - serve.go: keep cfg.Runners.XEnabled() conditional registration from main + agentRegistry from oss - config.go: keep RunnersConfig from main + BudgetConfig/ExternalBindAllowed from oss - server.go: handleStaticFiles (base-path rewrite) kept; deduplicated duplicate from both sides - executor/claude.go: accepted oss deletion (ClaudeRunner replaced by ContainerRunner) - container_test.go: added noopChannel + AgentChannel parameter to Run call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03feat: add --base-path flag so UI routes API calls to correct prefixPeter Stone
Allows running multiple instances (e.g. /claudomator and /claudomator-oss) behind a reverse proxy. The server rewrites the base-path meta tag in index.html at request time rather than baking it into the embed. Also adds --branch support to deploy script for isolated branch builds via git worktrees. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03feat: add --base-path flag to configure UI URL prefixPeter Stone
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03fix: clone from local mirror and sync from GitHub push eventsPeter Stone
- ContainerRunner now resolves local project path for non-story (CI) tasks, cloning from the local bare repo instead of the HTTPS GitHub URL to avoid auth failures on the host. - CI failure tasks now use SSH URLs (git@github.com:...) instead of HTTPS to avoid credential prompts even when no local path is found. - GitHub push webhook events now trigger an async git fetch into the local bare repo, keeping the local mirror in sync when work happens directly on GitHub. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27docs: update CLAUDE.md to current architecture (Phase 8)Claude
Corrects the now-false sections after Phases 2-7: the executor package overview (ContainerRunner for claude/gemini, LocalRunner, MCP Registry, budget, events — the subprocess ClaudeRunner/GeminiRunner stub are gone); the execution model and agent MCP/tool-use back-channel (replacing the /tmp git-sandbox and the removed claude.go "known bugs"); the planning preamble (MCP tools, not file/REST conventions); the storage schema (events table, tokens, agent column); task creation now chatbot/MCP-driven (elaborate endpoint + create form removed); and new Budget and Auth/binding sections. The REST table gains events/budget/MCP routes. Design Debt now lists the remaining Phase 8 follow-ups: the stories/checker backend teardown, dropping deprecated task columns, and the unverified gemini tool-use spike. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-27refactor(executor): retire the file-based question/summary fallback (Phase 8)Claude
With the MCP agent channel validated (the spike confirmed real claude calls ask_user/report_summary through it), the container runner no longer needs the pre-MCP file fallback that read question.json/summary.txt after the agent exited. Removes that block and the now-orphaned isCompletionReport/ extractQuestionText helpers and their test. The MCP path (channelPendingQuestion) is now the sole question/summary transport. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26feat(config,cli): loopback-default binding with fail-loud on external (Phase 7)Claude
The server now defaults to binding 127.0.0.1 and refuses to bind a non-loopback address (:8484, 0.0.0.0, a LAN IP, …) unless external_bind_allowed = true is set in config — failing loud at startup instead of silently exposing itself. External access is expected to go through a reverse proxy (Tailscale / Cloudflare Access / Caddy), per the locked auth model. - config: ExternalBindAllowed flag + ValidateBindAddr/isLoopbackHost (tested across loopback, all-interfaces, LAN, and override cases). - cli: --addr default is now 127.0.0.1:8484; serve() validates before binding. Per-client bearer rotation stays deferred (single shared token for now). https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26feat(api,web): budget headroom endpoint + UI chips (Phase 6)Claude
Adds GET /api/budget returning per-provider rolling-window headroom (empty when budget gating is unconfigured), wired from serve.go via SetBudget. The web UI polls it and renders a chip per limited provider in the header, flagging any under 20% remaining. New pure JS helpers formatBudgetHeadroom/ renderBudgetHeadroom are unit-tested; the endpoint is covered by Go handler tests (empty/reports/500). UI render not browser-verified in this environment. Completes Phase 6: spend accounting + dispatcher gating + observability surface. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26feat(executor): budget-gate the dispatcher — reroute to local or block ↵Claude
(Phase 6) The pool now consults a BudgetGate before taking an agent slot. If running on the chosen paid provider could breach its rolling window (estimated by the task's max_budget_usd), it reroutes to the free local runner when one is registered; otherwise it stops before running and marks the task BUDGET_EXCEEDED. serve.go builds a budget.Accountant from [budget] config and wires it into the pool (only when limits are configured). Allows the QUEUED → BUDGET_EXCEEDED transition, since the gate blocks a queued task before it ever reaches RUNNING. Covered by pool tests for both the block and reroute paths against a fake gate. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26feat(budget,storage,config): per-provider spend accounting substrate (Phase 6)Claude
Adds the budget accountant foundation: - storage: a per-execution `agent` column (additive migration, populated by the dispatcher) and SpendByProviderSince(since), summing cost_usd per provider in a window. Accurate attribution survives a task running on different providers across retries. - internal/budget: Accountant over a SpendSource + Limits, exposing Headroom (remaining/fraction per provider), Allow (would estCost breach the cap), and All (one query, sorted). Unconfigured/local providers are unlimited. - config: a [budget] section (window + provider_5h_usd map). No default cap — gating is opt-in by configuring limits. Fully unit-tested; dispatcher gating and the API/UI surface follow. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26fix(executor): set IS_SANDBOX=1 so claude honors bypassPermissions under rootClaude
The spike found that `claude --permission-mode bypassPermissions` (emitted by buildInnerCmd) is rejected when the process runs as root, and buildDockerArgs maps the container --user to the host uid — which is 0 when claudomator runs as root, breaking all claude tool execution in production. The container is an isolated sandbox, exactly the case the claude CLI gates behind IS_SANDBOX=1; setting it in the container env makes bypassPermissions work regardless of the host uid. Verified empirically against claude 2.1.150 (rejected as root without it; succeeds with it). Harmless for gemini containers. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26test(spike): manual agent-MCP validation harness + Phase 2 validation findingsClaude
Adds cmd/spike, a manual (never run by CI) harness that starts the agent MCP server on the host and points a real agent CLI at it to confirm tool calls reach the AgentChannel. Validates Phase 2 end-to-end without Docker. Findings against real claude 2.1.150 (sonnet-4-6): - PASS: claude discovers the server via --mcp-config (streamable HTTP + bearer) and calls record_progress + report_summary; both reach the AgentChannel, zero permission denials. - PASS: claude calls ask_user, gets the ErrAgentBlocked "end your turn" response, and ends its turn cleanly (human-in-the-loop path). - PRODUCTION NOTE: claude rejects --permission-mode bypassPermissions under root — the in-container agent must run non-root (ContainerRunner sets --user to the host uid), or tools must be pre-allowed via --allowedTools. - GEMINI spike could not run (no gemini binary); the harness's gemini path writes the production mcpServers/httpUrl settings and is ready to run where a binary exists. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-26feat(executor,llm): LocalRunner agent-channel via OpenAI tool-use (Phase 5)Claude
LocalRunner previously ignored the AgentChannel and produced a single fire-and-forget completion. It now declares the four agent back-channel tools (ask_user/report_summary/spawn_subtask/record_progress) as OpenAI function-calling definitions and runs a tool-use loop: each turn feeds tool results back as message history (re-feed) until the model stops calling tools, bounded by maxLocalToolTurns. ask_user converts a buffered question into a *BlockedError so the task blocks like the container runners. Adds tool-use support to the llm client (Tool/ToolCall/ToolFunction types, Tools on ChatRequest, ToolCalls on ChatResponse + wire request/response). The loop uses non-streaming Chat (tool_calls don't stream cleanly); assistant text is still written to stdout.log in the Claude stream-json envelope so summary/ changestats parsing is unchanged. Fully tested against a mock OpenAI endpoint + storeChannel: spawn/summary/ progress dispatch, ask_user blocking, token accumulation, and the llm tools round-trip. NOTE: local resume re-feeds conversation state (Decision #8) — not yet wired, so a blocked local task resumes fresh for now. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25feat(executor): wire the agent MCP back-channel for gemini containers (Phase 4)Claude
ContainerRunner previously skipped the MCP back-channel for gemini agents, so they ran tool-less. It now mints a per-task token for gemini too and registers the agent MCP server in the gemini CLI's user settings (agentHome/.gemini/settings.json → $HOME/.gemini in-container) using the gemini-cli mcpServers/httpUrl schema with a bearer header. With MCP enabled, gemini also receives the planning preamble that points at the ask_user/ report_summary/spawn_subtask/record_progress tools. Config generation is unit-tested (TestWriteGeminiMCPSettings) and the write is in the run setup path. CAVEAT: whether the gemini CLI actually invokes these tools in non-interactive (-p) mode — and how it handles tool auto-approval — is NOT verified against a live gemini binary in this environment; this lands the plumbing for a follow-up spike. No regression risk for gemini runs: a config issue degrades to the prior tool-less behavior. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25refactor(executor): delete the dead GeminiRunner stub and its sandbox ↵Claude
helpers (Phase 4) The production "gemini" agent type is served by ContainerRunner (Docker), not by the standalone subprocess GeminiRunner, which was never wired into serve.go and returned canned/simulated output — the dangerous landmine called out in CLAUDE.md's design debt. Removing it (gemini.go + tests). The subprocess sandbox helpers (setupSandbox/teardownSandbox/sandboxCloneSource in sandbox.go), preserved in Phase 2 only because GeminiRunner used them, are now orphaned — ContainerRunner has its own git/workspace handling — so they go too. No production behavior change: nothing instantiated GeminiRunner. Build green; executor tests unchanged except the pre-existing git commit-signing failures. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25refactor(web): remove the stories dashboard from the UI (Phase 3)Claude
The observability UI drops the stories surface: the Stories tab, the stories panel, the New/Detail story modals, and all their app.js code (STORY_STATUS_LABELS, storyStatusLabel, renderStoryCard, renderStoriesPanel, openStoryDetail, openStoryModal, renderElaboratedPlan, the story-modal init handlers, and the panel-dispatch 'stories' case) plus stories.test.mjs. The stories BACKEND (table, /api/stories* handlers, story-elaborate) is left intact for the Phase 8 cleanup pass; this only removes the UI surface. Dead CSS for the removed elements is likewise deferred to Phase 8. Verified with node --test (251 JS tests pass) and a module-import check. Not browser-verified in this environment. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25refactor(web): remove the create-task modal from the UI (Phase 3)Claude
The observability UI no longer creates tasks; chatbots do that via the chatbot MCP server. Removes the New Task button, the #task-modal (manual form + the now-defunct AI-elaborate textbox + validate panel), their handlers (elaborateTask/validateTask/buildValidatePayload/renderValidationResult/ openTaskModal/closeTaskModal/createTask) and the orphaned newTaskButtonShouldShowOnTab helper plus its test. Verified with node --test (267 JS tests pass) and a JS syntax check. Not browser-verified in this environment. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25feat(api,web): event-stream timeline (Phase 3)Claude
Adds GET /api/tasks/{id}/events (seq-ordered, ?since_seq for incremental polling) as the timeline data source, and a per-task Timeline section in the web UI that fetches and renders the observability event stream. New pure, unit-tested JS helpers: formatEventText, renderEventTimeline, fetchTaskEvents. The timeline load is async and guarded on a global fetch so the synchronous renderTaskPanel path (and its DOM-mock unit tests) is unaffected. Verified via Go endpoint tests and node --test (272 JS tests pass). Note: the live in-browser panel render was not exercised in this environment — only unit-level coverage. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25refactor(api): remove the natural-language elaborate REST endpoint (Phase 3)Claude
Chatbots now elaborate tasks in-conversation and submit them via the chatbot MCP server, so the POST /api/tasks/elaborate shell-out path is no longer needed. Removes the route, handleElaborateTask, and the task-elaborate-only helpers (buildElaboratePrompt, sanitizeElaboratedTask, readProjectContext, appendRawNarrative, elaborateWith{Claude,Local,Gemini}, and the elaboratedTask/ elaboratedAgent types) plus their dedicated tests. Shared helpers (extractJSON, claude/geminiBinaryPath, claude/geminiJSONResult, elaborateTimeout, the per-IP limiter) are retained — the story-elaborate endpoint and task-validate endpoint still use them. Stories themselves are untouched here; their removal stays in the Phase 8 cleanup pass. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-25feat(api): chatbot-facing MCP server for task orchestration (Phase 3)Claude
Adds a chatbot-facing MCP server mounted at /chatbot/mcp, guarded by the shared API bearer token (new config api_token, wired through SetAPIToken). Tools: submit_task, list_tasks, get_task, get_events, answer_question, accept_task, reject_task, cancel_task. submit_task creates and immediately queues a task, resolving the repository URL from a named project when not given explicitly. To keep the REST API and the MCP surface from drifting, the accept/reject/ cancel/answer operations are extracted into a shared service layer (taskops.go) with typed errors mapped to REST status codes; the existing HTTP handlers now delegate to it. The endpoint is only served when a token is configured and presented as a bearer. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24refactor(executor): delete unused ClaudeRunner; keep sandbox helpers (Phase 2)Claude
ClaudeRunner was never instantiated in production (serve.go wires only ContainerRunner) and was not wired for MCP. Remove it and its CLI/buildArgs/ execOnce tests. The sandbox helpers it defined (sandboxCloneSource, setupSandbox, teardownSandbox) are still used by GeminiRunner, so they move to sandbox.go; their tests (plus the shared initGitRepo helper and the tailFile test) move to sandbox_test.go. No production behavior change — this removes dead code and the last file-based agent wire that lived in ClaudeRunner. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24feat(executor): MCP-oriented planning preamble, applied by ContainerRunner ↵Claude
(Phase 2) Rewrites the planning preamble to point the agent at the four MCP tools (ask_user/report_summary/spawn_subtask/record_progress) instead of the CLAUDOMATOR_QUESTION_FILE / CLAUDOMATOR_SUMMARY_FILE conventions: ask_user ends the turn, report_summary replaces the summary file, spawn_subtask replaces the POST-to-/api/tasks planning step. Git discipline is retained. ContainerRunner previously applied no preamble at all; it now prepends this preamble (via buildAgentInstructions) when the MCP back-channel is active and skip_planning is false, so the in-container agent is actually guided to use the tools. Gemini agents (no MCP) are unaffected. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24feat(executor,api): wire agent MCP into ContainerRunner + mount /mcp (Phase 2)Claude
ContainerRunner now mints a per-task MCP token (from an injected Registry), writes a claude mcp-config into the workspace pointing at the host agent MCP server over host.docker.internal with that bearer, and adds --mcp-config to the in-container claude invocation. The token is revoked when the run ends. After the run, a buffered ask_user (PendingQuestion on the channel) is converted into a BlockedError — the MCP path to BLOCKED — with the file-based question.json kept as a fallback for in-flight tasks started on the old wire. The API server mounts the StreamableHTTP MCP handler at POST/GET/DELETE /mcp when a registry is provided; serve.go constructs one Registry shared by the runners (mint) and the server (resolve). Minting is skipped for gemini agents. Tests: writeMCPConfig output shape, buildInnerCmd flag presence/absence, and an api-level end-to-end MCP tool call through NewServer/Handler proving the route is mounted (and absent without a registry). https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24feat(executor): per-task agent MCP server + token registry (Phase 2)Claude
Adds the agent-facing MCP transport foundation: a Registry that mints a per-task bearer token bound to a freshly built MCP server exposing the four agent tools (ask_user, report_summary, spawn_subtask, record_progress), and an HTTP handler (StreamableHTTP) that resolves the token to that server. The server never trusts an agent-supplied task ID — context comes from the token. The default storeChannel now buffers summary and question signals under a mutex (an MCP tool call lands on an HTTP-handler goroutine mid-run), exposing ReportedSummary/PendingQuestion. The pool flushes the buffered summary onto the execution after the run, replacing the runner's direct exec.Summary write and keeping the read race-free. ask_user follows the record-and-resume model: it buffers the question, returns ErrAgentBlocked, and the tool tells the agent to end its turn; the run blocks and resumes later via claude --resume (no live slot held). Tests cover registry lifecycle, in-memory tool dispatch, and HTTP end-to-end with bearer auth (valid token dispatches; invalid token rejected). Not yet wired into the runners or mounted on the API server — next increment. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24feat(executor): introduce AgentChannel seam for runner signalsClaude
Defines AgentChannel — the normalized interface by which a runner reports agent-originated signals (AskUser, ReportSummary, SpawnSubtask, RecordProgress) — plus a default storeChannel implementation backed by storage. Runner.Run now takes an AgentChannel; the pool constructs one per execution. The file transport routes its post-exit summary detection through ch.ReportSummary (buffered onto the execution so the pool still applies its extract/synthesize fallbacks, no double-write). AskUser returns ErrAgentBlocked since write-and-exit cannot answer in-session; question persistence stays with the pool's BlockedError handling. SpawnSubtask and RecordProgress are implemented and tested, ready for the MCP transport in Phase 2 where the channel becomes fully load-bearing. Store gains CreateEvent so the channel can emit agent_message events. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-24feat(storage): emit subtask_spawned events on the parent streamClaude
CreateTask is now transactional and, when a task has a parent_task_id, records a subtask_spawned event (actor agent) on the parent's stream so the parent's history shows the children it dispatched. Top-level task creation (no parent) emits nothing. This closes the one agent-originated signal that flows during a run today (subtasks POSTed to the API per the planning preamble) but previously left no trace in the event stream. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-23feat(storage): dual-write question/summary/interaction flows to eventsClaude
The legacy task columns (question_json, summary, interactions_json) now also emit events in the same transaction as the column update: - UpdateTaskQuestion(non-empty) -> clarification_request (agent); the empty-string clear-on-answer emits nothing - UpdateTaskSummary -> summary (agent) - AppendTaskInteraction -> clarification_answer (user) This populates the event stream from the existing Q&A and summary paths without removing the old columns yet, setting up their eventual deletion in the cleanup phase. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-23feat(storage): emit state_change events on task transitionsClaude
UpdateTaskState now writes a state_change event in the same transaction as the row update, so the event stream and task state never diverge. Adds UpdateTaskStateBy(id, newState, actor) for explicit actor attribution; UpdateTaskState delegates with ActorSystem (correct for the executor, which drives the state machine). RejectTask and ResetTaskForRetry also emit state_change events attributed to the user. API accept and cancel handlers now attribute their transitions to the user via UpdateTaskStateBy. The executor's Store interface is unchanged, so no test doubles needed updating. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-22feat(event,storage): add events table substrate for OSS redesignClaude
New internal/event package defines the Event type with canonical Kind and Actor constants. New events table is appended via additive migration (id, task_id, seq, ts, kind, actor, payload_json) with seq as per-task monotonic, assigned by the storage layer inside a transaction. CreateEvent + ListEvents support the future migration of question_json, interactions_json, summary, and elaboration_input off the tasks table into a unified, replayable event stream. No existing code paths are modified yet; this is pure substrate for Phase 1. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-22docs: add OSS replacement redesign planClaude
Captures the locked architectural decisions from the chatbot-driven redesign discussion: SQLite as source of truth with external trackers as input feeds, events table replacing ad-hoc task columns, per-runner structured agent channel, three-runner roster with classifier, scoped auth, multi-dimensional budget, per-runner sovereign resume. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
2026-05-20fix: use git tracking branch instead of hardcoded origin/master in ↵Peter Stone
teardownSandbox Replace explicit 'git pull --rebase origin master' with 'git pull --rebase' (no remote/branch), which uses the upstream configured by git clone. This correctly handles repos where the default branch is 'main', 'trunk', or any other name — not just 'master'. Also apply gitSafe flags (-c safe.directory=* -c commit.gpgsign=false etc) consistently to the status, push, pull, and log commands in teardownSandbox that were missing them. The variable-shadowing bug noted in CLAUDE.md was already resolved in the github/main merge (both setupSandbox calls used '=' not ':='); strike it from the docs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20feat: add [runners] config section to toggle each runner on/offPeter Stone
Adds RunnersConfig{Claude,Gemini,Local,Container *bool} to Config, parsed from a [runners] TOML section. Each field uses the *bool pointer pattern (nil = enabled by default, false = disabled) so existing installs require no config changes. serve.go / run.go now gate each runner registration on the corresponding Enabled() method. The pool's runners map remains the authoritative routing table — absent entries are already skipped by pickAgent and fail-fast in getRunner, so no executor changes are needed. Example: [runners] gemini = false # disable cloud gemini runner local = true # explicit (same as default) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20fix: auto-load default config file and relax repository_url requirementPeter Stone
root.go: PersistentPreRunE was only reading the config file when --config was explicitly passed. This left LocalModel.Endpoint empty so the local runner was never registered and the Classifier overrode agent.type:local. Now always tries ~/.claudomator/config.toml; silently ignores ENOENT. validator.go: repository_url was unconditionally required but is irrelevant for LocalRunner tasks (pure chat completions). The executor already handles an empty RepositoryURL via the Project registry lookup at dispatch time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13merge: integrate github/main — LocalRunner, real GeminiRunner, llm clientPeter Stone
Merges 12 commits from github/main (formerly master) that were developed independently. Key additions: - LocalRunner: OpenAI-compatible local LLM execution (Ollama, LM Studio) - Real GeminiRunner with full sandbox parity to ClaudeRunner - llm.Client for enriching CI failures and elaboration via local model - retry.ParseRetryAfter moved to shared package - tokens_in/tokens_out columns in executions table Conflict resolutions: - Kept local main's VAPID/push, stories, projects, agent events schema - Merged both sets of Config fields (local + LocalModel from github/main) - Unified activePerAgent accounting (decActiveAgent helper) - Removed duplicate helpers from claude.go (now in helpers.go) - Fixed double-decrement bug in handleRunResult vs decActiveAgent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12Merge pull request #4 from thepeterstone/claude/gemini-sandboxPeter Stone
Gemini runner: full sandbox-flow parity with Claude
2026-05-12feat(executor): bring GeminiRunner to sandbox-flow parity with ClaudeClaude
All coding tasks now follow the same flow regardless of runner: when project_dir is set, the agent runs in a temp clone, not in the user's working tree. On success, edits are autocommitted and pushed back to origin/master and the sandbox is removed. On failure or BLOCKED, the sandbox is preserved and its path surfaces in the error / BlockedError so the user can inspect partial work or resume in place. Before this commit, GeminiRunner.Run set cmd.Dir to project_dir directly, so an agent run could leave half-done edits in the user's working tree with no rollback. ClaudeRunner has had the full sandbox flow for a while; this commit closes the gap. Reused the existing package-level helpers from claude.go verbatim: setupSandbox, teardownSandbox, sandboxCloneSource, gitSafe, plus the resume/stale-sandbox/blocked-error patterns. No new shared abstraction needed — same package. LocalRunner intentionally not changed. The OpenAI chat path has no tool use, so the agent can't edit files; sandbox would be theater. Tests (6 new): - Run_ProjectDir_RunsInSandbox: cwd captured by fake binary is a sandbox path, not project_dir. - Run_BlockedError_IncludesSandboxDir: when question.json appears, BlockedError.SandboxDir is set and the dir exists. - Run_ExecError_PreservesSandbox: failing exit wraps error with "(sandbox preserved at <path>)" and the path exists on disk. - Run_ResumeUsesStoredSandboxDir: ResumeSessionID + SandboxDir → runs in that dir without re-cloning. - Run_StaleSandboxDir_ClonesAfresh: resume pointing at missing dir falls back to a fresh clone from project_dir. - Run_NoProjectDir_SkipsSandbox: tasks without project_dir don't trigger sandbox setup. https://claude.ai/code/session_017Edeq947TpSm1vQTxMhi1J
2026-05-07test(executor): verify explicit Claude commits are captured in execRecordClaude
Adds TestTeardownSandbox_CapturesExplicitCommits to cover the case where the agent explicitly commits changes (no autocommit needed). Previously only the autocommit path was tested; this confirms teardownSandbox populates Commits for any commits ahead of origin. https://claude.ai/code/session_01G4dT9JBWFFb8xGcSHenzRS
2026-05-03fix: atomic execution creation + RUNNING state transitionPeter Stone
Add CreateExecutionAndSetRunning to storage.DB and Store interface, replacing the two sequential CreateExecution/UpdateTaskState calls in executor.go. Eliminates the crash window where a task stays PENDING with an orphaned RUNNING execution record. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03chore: add next-steps.md session handoff summaryClaude
https://claude.ai/code/session_01W8P6wwDYF7dpETXWVXCz5L
2026-05-03docs: rewrite CLAUDE.md with accurate architecture, known bugs, and design ↵Claude
debt; add improvement plan - Fix YAML key in Task format (agent: not claude:) - Document complete state machine including BLOCKED and READY states - Document sandbox lifecycle and git clone failure root cause (sandboxDir shadowing) - Document all packages including notify, deployment, version, classifier, preamble - Call out GeminiRunner as a non-functional stub - Document design debt: unused priority/retry fields, double changestats extraction, hardcoded master branch, context.Background() in resume, non-atomic exec creation - Add docs/IMPROVEMENT_PLAN.md with 20 prioritized issues ranked by effort and impact https://claude.ai/code/session_01W8P6wwDYF7dpETXWVXCz5L