<feed xmlns='http://www.w3.org/2005/Atom'>
<title>claudomator.git/internal/executor/container_test.go, branch fix/dockersandbox-gitpush-host-security</title>
<subtitle>claudomator — task automation server
</subtitle>
<id>https://git.terst.org/claudomator.git/atom?h=fix%2Fdockersandbox-gitpush-host-security</id>
<link rel='self' href='https://git.terst.org/claudomator.git/atom?h=fix%2Fdockersandbox-gitpush-host-security'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/'/>
<updated>2026-07-04T05:05:36+00:00</updated>
<entry>
<title>feat(story,role): add retro ceremony -- closes the self-improvement loop (Phase 8)</title>
<updated>2026-07-04T05:05:36+00:00</updated>
<author>
<name>Claude Sonnet 5</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-07-04T05:05:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=8cb291ad7cdb317ff80947278ee055b1a4925b41'/>
<id>urn:sha1:8cb291ad7cdb317ff80947278ee055b1a4925b41</id>
<content type='text'>
The final mechanism the versioned role-config model (Phase 5) was built for:
when a story reaches DONE, StoryOrchestrator spawns a retro-role task that
reflects on the story's full history and proposes draft role_configs
versions for a human to review and activate via the existing (unchanged)
POST /api/roles/{role}/activate.

- AgentChannel gains a 6th method, ProposeRoleConfig(ctx, role.RoleConfig)
  (version, err), following ProposeEpic's precedent (Phase 7c): a structured
  tool call, not summary-parsing. storeChannel.ProposeRoleConfig calls the
  same Store.CreateRoleConfig the human-facing POST /api/roles/{role}/versions
  endpoint already uses (proposed_by: "retro"), landing a new draft row
  without touching whatever's currently active. Wired through both
  transports exactly like ProposeEpic: internal/agentloop/tools.go (native
  loop) and internal/executor/agentmcp.go (MCP).
- StoryOrchestrator.Tick now routes a story at status DONE to a new
  processRetro stage instead of processStory -- a sibling stage, not a
  continuation, since the Builder-&gt;Evaluators-&gt;Arbitration chain is long
  settled by then. processRetro only *reads* that settled pipeline
  (read-only findEvaluators/findArbitration counterparts to
  ensureEvaluators/ensureArbitration -- it never spawns/mutates
  Builder-pipeline tasks) to locate the Arbitration task the retro task
  depends on, then spawns (idempotently -- checks for an existing
  retro-role dependent first) one retro-role task with instructions
  assembled from the story's spec/acceptance-criteria, full task tree, per-
  task cost/escalation history, active role_configs per role encountered,
  and the story's own event stream (evaluator verdicts, arbitration
  decision).
- event.KindRetroCaptured (attached to the story's ID, matching
  KindEvalVerdict/KindArbitrationDecided's convention) fires once the retro
  task completes (auto-accepted like every other pipeline task), aggregating
  every event.KindRoleConfigProposed the retro task recorded (one per
  propose_role_config call) into {task_id, proposals: [{role, version}],
  summary} -- the summary is the "capturing lessons" half of this ceremony,
  the proposals are the versioned-config half.
- Human activation is completely untouched: drafts land through the
  identical CreateRoleConfig/config_json path Phase 5's endpoints already
  handle, confirmed via existing role-endpoint tests passing unmodified.

go build/vet/test -race -count=1 all pass, full suite (20 packages) -- one
run hit a known, pre-existing, intermittent flake under full-suite load
(unrelated to this phase's files) that did not reproduce on two immediate
reruns, both in isolation and full-suite.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs
</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>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>executor: deduplicate worker slots and fix git command execution</title>
<updated>2026-06-04T07:40:46+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-06-04T07:40:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=fa3ed5220a28f326d443726233c37e1a7df0ced5'/>
<id>urn:sha1:fa3ed5220a28f326d443726233c37e1a7df0ced5</id>
<content type='text'>
</content>
</entry>
<entry>
<title>merge: integrate oss branch — budget gating, MCP back-channel, events, loopback-only bind</title>
<updated>2026-06-03T23:01:39+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-06-03T23:01:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=68cf1e24dd2ca2612b72babc4b35280cd3512f92'/>
<id>urn:sha1:68cf1e24dd2ca2612b72babc4b35280cd3512f92</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: clone from local mirror and sync from GitHub push events</title>
<updated>2026-06-03T01:23:08+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-06-03T01:23:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=476a3136a9f55e151ae689cd098795ec865e7850'/>
<id>urn:sha1:476a3136a9f55e151ae689cd098795ec865e7850</id>
<content type='text'>
- 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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor(executor): retire the file-based question/summary fallback (Phase 8)</title>
<updated>2026-05-27T05:42:31+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-27T05:42:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=1deee869e5cd0895e45016192dd70c0ed6c68cf3'/>
<id>urn:sha1:1deee869e5cd0895e45016192dd70c0ed6c68cf3</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>fix(executor): set IS_SANDBOX=1 so claude honors bypassPermissions under root</title>
<updated>2026-05-26T09:28:57+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-26T09:28:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=561915c5182c3fb39cd6a8b6613c489b35b7c1bf'/>
<id>urn:sha1:561915c5182c3fb39cd6a8b6613c489b35b7c1bf</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>feat(executor): wire the agent MCP back-channel for gemini containers (Phase 4)</title>
<updated>2026-05-25T19:02:19+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-25T19:02:19+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=65cd7ea65d9c6fe0fad39bb2c5cac70d61153444'/>
<id>urn:sha1:65cd7ea65d9c6fe0fad39bb2c5cac70d61153444</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>feat(executor): MCP-oriented planning preamble, applied by ContainerRunner (Phase 2)</title>
<updated>2026-05-24T17:38:53+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-24T17:38:53+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=473d80224880dd718cb2612fd49b987cd6097b2c'/>
<id>urn:sha1:473d80224880dd718cb2612fd49b987cd6097b2c</id>
<content type='text'>
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
</content>
</entry>
</feed>
