| Age | Commit message (Collapse) | Author |
|
Phase 2 of "local OSS models as agents" plan. Adds a third elaboration
path that calls the local OpenAI-compatible LLM via the internal/llm
client, and reorders dispatch so the cheap path is tried first:
local → claude → gemini, with each next attempt only on hard failure
of the prior.
Wiring is opt-out, not opt-in: when [local_model].endpoint is set,
elaboration prefers local by default. Users with a slow or low-quality
local model can disable just elaboration via:
[local_model]
endpoint = "..."
prefer_for_elaborate = false
without giving up the runner or the classifier path.
Implementation:
- Server gains an optional *llm.Client field via SetLLM (matches the
existing SetNotifier/SetWorkspaceRoot setter pattern, no NewServer
signature break).
- elaborateWithLocal() reuses buildElaboratePrompt verbatim and asks
for response_format=json_object so we skip markdown-fence cleanup.
- handleElaborateTask reorders try chain; existing Claude-first
behavior is preserved exactly when SetLLM is not called.
- LocalModel.UseForElaborate() encapsulates the default-true gating
with a *bool so explicit-false survives TOML parse.
Tests:
- elaborateWithLocal: parses valid response, errors on nil client,
errors on bad JSON.
- handler: local preferred when wired; falls back to claude when
local fails; unchanged behavior when no LLM is configured.
- config: UseForElaborate gating across empty/default/explicit-true/
explicit-false cases.
Pre-existing test failures noted in docs/plans/local-oss-runner.md
(post-epic cleanup): TestGeminiLogs_ParsedCorrectly returns 404 for
gemini execution log fetch — predates this change.
Plan: docs/plans/local-oss-runner.md.
https://claude.ai/code/session_017Edeq947TpSm1vQTxMhi1J
|
|
Phase 1 of "local OSS models as agents" plan. Adds a third Runner
backed by any OpenAI-compatible HTTP server (Ollama, vLLM, LM Studio,
llama.cpp), and migrates the Gemini-CLI classifier to route through
the same client when configured.
Two-layer split: internal/llm.Client is the workhorse (HTTP, no Pool,
no DB) used directly by the classifier and any future internal helper
that needs cheap reasoning. internal/executor.LocalRunner is a thin
adapter implementing Runner for user-facing tasks. This avoids
Pool reentrancy/deadlock when sub-second internal calls fire from
inside Pool.execute().
Highlights:
- internal/retry: relocated runWithBackoff/IsRateLimitError/ParseRetryAfter
into a shared package reused by executor and llm.
- internal/llm: Chat (non-streaming) and ChatStream (SSE) over
/chat/completions with optional bearer auth, json_object response
format, retry on 429/503, Retry-After parsing.
- internal/executor/LocalRunner: streams deltas into stdout.log in the
same stream-json envelope ClaudeRunner emits, then writes one
consolidated assistant block plus a result terminator so existing
parsers (extractSummary, ParseChangestatFromOutput) work unchanged.
- internal/executor/Classifier: gains optional LLM field; uses
json_object response format (no markdown-fence cleanup needed).
Falls back to Gemini-CLI subprocess when LLM is nil.
- Pool.skipClassification: now skips only when the requested agent
type is registered, so unknown types still reach the load balancer.
- Storage: additive tokens_in/tokens_out ALTERs on executions; CLI
runners record cost_usd as before, LocalRunner records 0 + tokens.
- Config: [local_model] section (endpoint, model, timeout_seconds,
default_temperature, api_key). Empty endpoint = no LocalRunner
registered, classifier falls back to Gemini.
Pre-existing test issues fixed in passing:
- claude_test.go setupSandbox callsites updated to current signature.
- gemini_test.go TestParseGeminiStream skipped (asserts unimplemented
GeminiRunner stream-error parsing; tracked separately).
Plan: docs/plans/local-oss-runner.md.
https://claude.ai/code/session_017Edeq947TpSm1vQTxMhi1J
|
|
Fix 1 (server.go): Replace context.Background() with s.ctx in
handleAnswerQuestion, handleResumeTimedOutTask, and handleRunTask.
Add a ctx field to Server (defaulting to context.Background()) and
a SetContext method so the serve command can wire in the signal-
cancellable lifecycle context.
Fix 2 (serve.go): Call srv.SetContext(ctx) before StartHub so all
pool submissions use the server's root context (already cancelled
on SIGTERM/SIGINT). Pool.Shutdown and its wiring were already present.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Fix 1: Remove QuestionRegistry and related types (QuestionHandler, PendingQuestion)
from question.go -- nothing reads Pool.Questions or uses the registry. Remove
NewQuestionRegistry() call from NewPool and the Questions field from Pool.
Remove the now-superfluous registry tests; keep stream/parse helpers which are
still used by the claude runner.
Fix 2: Check scanner.Err() after the parseStream loop so I/O errors from the
scanner are not silently swallowed when streamErr is still nil.
Fix 3: Delete internal/api/changestats.go -- the parseChangestatFromFile and
parseChangestatFromOutput wrappers were only needed to support processResult(),
which no longer calls them; they are unreachable dead code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add AcceptanceCriteria field to elaboratedStoryTask struct, update
buildStoryElaboratePrompt schema and rules, pass the value through
handleApproveStory into task.Task, and add a test verifying the
field is persisted on approved story tasks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
cancellation killing deploy
|
|
auto-deploy
- checkStoryCompletion now guards against re-running on already-SHIPPABLE stories
and no longer auto-triggers triggerStoryDeploy on completion
- New Pool.ShipStory method validates SHIPPABLE state then fires triggerStoryDeploy
- POST /api/stories/{id}/ship route registered and handleShipStory handler added
- Two new tests: 202 for SHIPPABLE story, 409 for non-SHIPPABLE story
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
failure states
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Remove the standalone "All" tab; completed tasks (last 24h) now appear
at the bottom of the Ready tab under a "Completed (24h)" section header
- Move Stories tab to the first position in the nav bar
- Drop 'all' from badge count computation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
cascade-retry test race
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
consecutive failures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Adds modal-shell to SeedProjects() and documents how to find
and edit seed.go in .agent/design.md.
|
|
Uses time-bucketed picsum seed (5-min intervals) so all sites
show the same image simultaneously. Sets --bg-url CSS custom
property on body, consumed by .bg-body background-image rule.
|
|
When a story is approved with pre-created subtasks, parent tasks are
QUEUED but never run. Their subtasks complete, but:
- maybeUnblockParent only handled BLOCKED parents, not QUEUED ones
- checkStoryCompletion required ALL tasks (incl. subtasks) to be done
Fixes:
- maybeUnblockParent now also promotes QUEUED parents to READY when all
subtasks are COMPLETED
- checkStoryCompletion only checks top-level tasks (parent_task_id="")
- RecoverStaleBlocked now also scans QUEUED parents on startup and
triggers checkStoryCompletion if it promotes them
- Add QUEUED→READY to valid state transitions (subtask delegation path)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add workerWg to Pool; Shutdown() closes workCh and waits for all
in-flight execute/executeResume goroutines to finish
- Signal handler now shuts down HTTP first, then drains the pool
- ShutdownTimeout config field (toml: shutdown_timeout); default 3m
- Tests: WaitsForWorkers and TimesOut
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Story detail modal now fetches /api/stories/{id}/tasks and renders
top-level tasks as a numbered list with subtasks nested beneath, using
the same state emoji as the blocked/task views.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add ensureStoryBranch() that runs git ls-remote to check, then clones
into a temp dir to create and push the branch if missing. Called before
the task's own clone so checkout is guaranteed to succeed.
Removes the post-checkout fallback hack added in the previous commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
If git checkout of the story branch fails (branch never pushed to bare
repo), create it from HEAD and push to origin instead of hard-failing
the task.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
The `id NOT IN (SELECT task_id FROM executions)` guard prevented retried
tasks (FAILED→PENDING) from ever being picked up by start-next-task.
Only the rejection_comment check is needed to avoid re-running rejected tasks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
When /run is called on a CANCELLED/FAILED task that has deps in a terminal
failure state, automatically reset and resubmit those deps so the task
isn't immediately re-cancelled by the pool's dep check.
Also update reset-failed-tasks script to handle CANCELLED tasks and clean
up preserved sandbox workspaces.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
QUEUED→FAILED is not a valid state transition. When a dependency enters a
terminal failure state, cancel the waiting task instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
createStoryBranch was pushing to 'origin' which doesn't exist — branches
never landed in the bare repo so agents couldn't clone them. Now uses
the project's RemoteURL (bare repo path) directly for fetch and push.
Raise drain threshold from 2 to 3 consecutive failures to reduce false
positives from transient errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
With maxPerAgent=1, tasks with DependsOn were entering waitForDependencies
while holding the per-agent slot, preventing the dependency from ever running.
Fix: check deps before taking the slot. If not ready, requeue without holding
activePerAgent. Also accept StateReady (leaf tasks) as a satisfied dependency,
not just StateCompleted.
Add startedCh to pool and broadcast task_started WebSocket event when a task
transitions to RUNNING, so the UI immediately shows the running state during
the clone phase instead of waiting for completion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
AgentStatusInfo was missing drained field so UI couldn't show drain lock.
AgentEvent had no JSON tags so ev.agent/event/timestamp were undefined in
the stats timeline. UI now shows "Drain locked" card state with undrain CTA.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
handleApproveStory was creating tasks without RepositoryURL, causing
executions to fail with "task has no repository_url". Now looks up
the project's RemoteURL and sets it on all tasks and subtasks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
scripts
- handleApproveStory: set Project = input.ProjectID on tasks and subtasks so
the executor can resolve RepositoryURL from the project registry (was causing
"task has no repository_url" on every story task)
- elaborate.go: read .agent/worklog.md instead of SESSION_STATE.md for project
context injected into elaboration prompts
- deploy: explicitly chmod +x all scripts before restart (same root cause as
the binary execute-bit loss — chown -R was stripping it)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
chown -R in fix-permissions was stripping the execute bit on the binary,
causing the service to fail with 203/EXEC on next restart. Explicitly
chmod +x both bin paths as the final step before systemctl restart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Stories tab (📖) with story card list, status badges, project/branch meta
- New Story modal: project picker + goal textarea + AI elaboration → plan review → approve & queue
- Story detail modal: status, project, branch, created date
- Export storyStatusLabel() and renderStoryCard() with 16 unit tests
- CSS for story cards, status badges, and modals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
renderTaskPanel was rendering the summary twice — once with .task-summary
before Overview, and again with .task-summary-text after it. Remove the
second (post-Overview) duplicate.
Add `dataset: {}` to the test mock DOM so makeMetaItem's state badge path
doesn't crash during renderTaskPanel tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
- executor.go: merge story branch to main before deploy
- container.go: error messages reference git push origin main
- api/stories.go: create story branch from origin/main (drop master fallback)
- executor_test.go: test setup uses main branch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Wraps POST /api/tasks + run in a single script callable from /workspace/**
(auto-permitted). Used by the ct-create skill to avoid per-call auth prompts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
task queued
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- triggerStoryDeploy: fetch/checkout/merge --no-ff/push before running deploy script (ADR-007)
- executor_test: TestPool_StoryDeploy_MergesStoryBranch proves merge happens
- seed.go: add doot project with deploy script; wire claudomator deploy script
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
(ADR-007)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Add checkValidationResult which inspects the final task.State of a
completed validation task and updates the story to REVIEW_READY (pass)
or NEEDS_FIX (fail). Wire into handleRunResult so stories in
VALIDATING state are dispatched to checkValidationResult instead of
checkStoryCompletion, covering both success and FAILED terminal paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Add triggerStoryDeploy to Pool: fetches story's project, runs its
DeployScript via exec.CommandContext, and advances story to DEPLOYED on
success. Wire into checkStoryCompletion with go p.triggerStoryDeploy
after the SHIPPABLE transition. Covered by TestPool_StoryDeploy_RunsDeployScript.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|