<feed xmlns='http://www.w3.org/2005/Atom'>
<title>claudomator.git/web, 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-04T08:50:46+00:00</updated>
<entry>
<title>feat(web,api): add Budget/Roles dashboard -- final phase of the harness redesign (Phase 9b)</title>
<updated>2026-07-04T08:50:46+00:00</updated>
<author>
<name>Claude Sonnet 5</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-07-04T08:50:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=f8ae821240f33d615a9e91cdfeb6c026b7970782'/>
<id>urn:sha1:f8ae821240f33d615a9e91cdfeb6c026b7970782</id>
<content type='text'>
Two new tabs, matching Phase 9a's conventions: Budget (per-provider spend
meters, escalation funnel, spend-over-time) and Roles (version history,
draft activation, readable escalation-ladder view) -- the human-facing
surface for the token-husbanding value proposition and the Phase 5/8
versioned role-config system.

New backend (minimal, additive, matching existing endpoint conventions --
unauthenticated like /api/budget and /api/roles/*):
- internal/storage/dashboard.go: QueryEscalationFunnel (executions grouped
  by escalation_rung + agent, count + cost) and QuerySpendTimeseries (cost
  per provider bucketed hourly/daily, mirroring QueryDashboardStats'
  existing bucketing expressions). Documented honestly: rung 0 is not
  exclusively "resolved locally" -- escalation_rung defaults to 0
  uniformly, so non-role-typed executions (which never climb a ladder)
  land there too, alongside role-typed tasks genuinely resolved at tier 0.
  A role-only variant would need a join against tasks.config_json with no
  queryable role column on executions -- intentionally out of scope.
- internal/api/dashboard.go: GET /api/escalation-funnel, GET
  /api/spend-timeseries (both ?window=5h|24h|7d|&lt;duration&gt;, default 24h).
- internal/storage.ListRoleNames + GET /api/roles: the "which roles exist"
  gap -- there was no way to discover role names before this, only to list
  versions for a role you already knew the name of.

Chart-form decisions (dataviz skill, invoked before writing chart code):
horizontal stacked bar for the escalation funnel (rung order already
encodes the funnel shape positionally; color only needed for per-provider
segments within each rung); multi-line for spend-over-time; a fixed-order
categorical palette from the skill's validated palette.md slots for
provider identity (re-validated against this app's dark surface, passing);
a separate status (good/warning/critical) palette for budget meters,
deliberately distinct from both --state-* and the provider palette. Caught
and fixed a real bug during visual QA: converging near-zero end-labels on
the spend chart were overlapping (an anti-pattern the skill explicitly
flags) -- fixed with a 14px minimum-gap check before direct-labeling an
endpoint, leaning on the legend/tooltip otherwise.

Verified with a real running server, real seeded data (65 executions across
rungs 0-2 with a realistic provider mix, 2 roles with active/draft/retired
role_configs versions written directly via internal/storage), and a real
headless-browser session (reusing Phase 9a's Chromium/proxy scaffinding):
confirmed correct rung totals/percentages, provider legends, a 3-line spend
chart, live window-selector re-render, correct active-version highlighting
on the role panel, and a real Activate click on a draft version -- verified
via both DOM re-render and a direct backend GET that it truly persisted.

go build/vet/test -race -count=1 all pass, full suite. node --test
web/test/*.mjs: 291/291 passing (16 new).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs
</content>
</entry>
<entry>
<title>feat(web): add Stories tab -- Kanban board, epic swimlanes, story-detail DAG (Phase 9a)</title>
<updated>2026-07-04T05:31:42+00:00</updated>
<author>
<name>Claude Sonnet 5</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-07-04T05:31:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=d105eca610b0e737c3313e4978d6a917b4f55d10'/>
<id>urn:sha1:d105eca610b0e737c3313e4978d6a917b4f55d10</id>
<content type='text'>
First UI work on top of the harness's 8 backend phases -- pure web/*
frontend, no backend changes, keeping the existing embedded vanilla-JS
convention (no build step, no framework).

- Kanban board: columns spanning the full story lifecycle (DISCOVERY/FRAMING
  -&gt; BACKLOG -&gt; PRIORITIZED -&gt; IN_PROGRESS -&gt; VALIDATING/REVIEW_READY -&gt; DONE),
  cards showing priority/acceptance-criteria count/eval-verdict progress.
  Drag-and-drop reorders priority within a column (persisted via a partial
  PUT /api/stories/{id}); cross-column drag is disabled outright (a
  dragover handler only preventDefault()s when the dragged card's own status
  already maps to that column) rather than allowed-then-snapped-back, since
  the latter would visibly misplace a card for several seconds until the
  next poll -- reads as a bug, not a feature.
- Epic swimlanes: stories grouped client-side by epic_id (incl. an
  "Unassigned" lane) with a DONE/total progress meter per epic, built from
  data the board already fetches (avoids N+1 GET /api/epics/{id}/stories
  calls for a result that's a strict subset of the already-loaded list).
- Story detail modal: metadata/spec/acceptance criteria, a BFS-layered plain-
  SVG DAG of the story's task-tree (rows = BFS depth from root_task_id over
  both parent_task_id and depends_on edges, solid vs. dashed strokes
  distinguishing the two edge kinds), an event timeline rendering
  eval_verdict/arbitration_decided/human_accepted/retro_captured/
  epic_proposed with human-readable text, and an Accept button wired to
  POST /api/stories/{id}/accept when status is REVIEW_READY.
- DAG node coloring reuses the app's existing --state-* CSS custom
  properties (the dataviz skill's "reserved status palette" pattern,
  already used elsewhere in the app) rather than introducing a new palette.
  Running the skill's own palette validator against that pre-existing
  9-color set fails its CVD/lightness checks (a pre-existing condition, out
  of scope to fix here since those colors are used elsewhere in the app) --
  mitigated per the skill's documented remedy for a failing palette: every
  node always carries a text label (name + role + state) and a full legend,
  so identity never depends on color alone; edge kind is encoded by stroke
  style, not color.

Verified with a real running server and a real headless-browser session
(Chromium via playwright-core, offline-cached), not just code review:
seeded real data (an epic, 11 stories across every lifecycle status, a
6-node Builder-&gt;4-Evaluator-&gt;Arbitration task tree, 4 story events) via the
live REST API plus two throwaway fixture scripts for the two write paths
the API doesn't expose (task depends_on, direct event writes), then
confirmed via screenshots/DOM assertions: correct column placement for every
status, working swimlane grouping/progress meter, correct 6-node/8-edge DAG
render, working event timeline, a real Accept-button API call flipping
REVIEW_READY-&gt;DONE, real drag-and-drop priority persistence (re-fetched to
confirm), and a genuine cross-column-drag no-op. Caught and fixed a real bug
during this process: DAG node-label truncation now measures
getComputedTextLength() and binary-searches the longest fit, instead of a
fixed character-count budget that collapsed every "Eval: evaluator_X" label
to "Eval:..." (only one space in the string).

go build ./... clean; node --test web/test/*.mjs -- 275 tests pass (19 new).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs
</content>
</entry>
<entry>
<title>feat: execution row click opens unified detail + log viewer</title>
<updated>2026-06-04T00:22:00+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-06-04T00:22:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0d537df08fea539022cbcffe3ea778a3c84e7fb7'/>
<id>urn:sha1:0d537df08fea539022cbcffe3ea778a3c84e7fb7</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(api,web): budget headroom endpoint + UI chips (Phase 6)</title>
<updated>2026-05-26T20:34:09+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-26T20:34:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=ab4b364954af08fa602388495ca425eaef0abf74'/>
<id>urn:sha1:ab4b364954af08fa602388495ca425eaef0abf74</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>refactor(web): remove the stories dashboard from the UI (Phase 3)</title>
<updated>2026-05-25T05:12:54+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-25T05:12:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=5669e2ca58b4a99616083eaff9f639ecce4ba809'/>
<id>urn:sha1:5669e2ca58b4a99616083eaff9f639ecce4ba809</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>refactor(web): remove the create-task modal from the UI (Phase 3)</title>
<updated>2026-05-25T05:09:10+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-25T05:09:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=42bb6432d3e53042723b43330c22eafbf553e10f'/>
<id>urn:sha1:42bb6432d3e53042723b43330c22eafbf553e10f</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>feat(api,web): event-stream timeline (Phase 3)</title>
<updated>2026-05-25T05:05:27+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-25T05:05:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=b44c8277465c3b4e03fe4de4dd93850413023b69'/>
<id>urn:sha1:b44c8277465c3b4e03fe4de4dd93850413023b69</id>
<content type='text'>
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
</content>
</entry>
<entry>
<title>merge: integrate github/main — LocalRunner, real GeminiRunner, llm client</title>
<updated>2026-05-13T04:02:20+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-05-13T04:02:20+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=68399a598924775a3ec22a39c2336ae497fb07f3'/>
<id>urn:sha1:68399a598924775a3ec22a39c2336ae497fb07f3</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>chore: close deferred work — real GeminiRunner, Local UI option, db.go cleanup</title>
<updated>2026-05-03T08:00:20+00:00</updated>
<author>
<name>Claude</name>
<email>noreply@anthropic.com</email>
</author>
<published>2026-05-03T08:00:20+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=e7b382bf177cbe518af3d86c3ee6c49344d225f4'/>
<id>urn:sha1:e7b382bf177cbe518af3d86c3ee6c49344d225f4</id>
<content type='text'>
Closes the three items left on the deferred queue after the post-epic
cleanup.

GeminiRunner.execOnce now actually executes the gemini binary instead
of writing hardcoded stream data. Mirrors ClaudeRunner.execOnce:
- exec.CommandContext with the same env vars (CLAUDOMATOR_API_URL etc.)
- process group SIGKILL on context cancel
- stdout piped through parseGeminiStream → stdoutFile
- stderr to file
- exit codes captured, stderr tail surfaced on failure

Test infrastructure bug uncovered in passing: testServerWithGeminiMockRunner's
mock script used double-quoted echo with literal triple-backticks, which
bash interpreted as command substitution. The script always produced
empty output. The bug was invisible until now because GeminiRunner
ignored the script entirely. Switched to a single-quoted heredoc.

Frontend: index.html dropdown gains a "Local" option. No JS branching
needed — the value flows through to agent.type verbatim and downstream
display reads the type string as-is.

storage/db.go: removed stale debug-comment scaffolding (the "TODO:
Replace with proper logger" block) that was tracking a dead
`fmt.Printf` call. The path it commented on is fine without logging —
unmarshal errors are returned wrapped.

Test status: `go test -race ./...` green across every package, zero
skips, zero excluded tests.

https://claude.ai/code/session_017Edeq947TpSm1vQTxMhi1J
</content>
</entry>
<entry>
<title>feat: Ship button on SHIPPABLE stories; checker report on READY task cards</title>
<updated>2026-04-04T09:39:03+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-04-04T09:39:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=40513ffddba01467193c3c3e19468c7090f06215'/>
<id>urn:sha1:40513ffddba01467193c3c3e19468c7090f06215</id>
<content type='text'>
Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
