<feed xmlns='http://www.w3.org/2005/Atom'>
<title>claudomator.git/web/test, 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-06T09:54:23+00:00</updated>
<entry>
<title>feat(web): add ensureTaskLogStream and taskLogStreams for task card log streaming (task 6)</title>
<updated>2026-07-06T09:54:23+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T09:54:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=2f83075719bbca0eb3682c61dbf4fe397894baff'/>
<id>urn:sha1:2f83075719bbca0eb3682c61dbf4fe397894baff</id>
<content type='text'>
Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(web): add cardContentSignature to skip log-tail in render dedup (task 5)</title>
<updated>2026-07-06T09:46:45+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T09:46:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=4344e4d49a94d5d636f4667812b556566ef71849'/>
<id>urn:sha1:4344e4d49a94d5d636f4667812b556566ef71849</id>
<content type='text'>
Prevents renderTasksIntoContainer from tearing down and rebuilding a task
card on every poll tick just because its .task-log-tail has accumulated
streamed log lines. Exports cardContentSignature which walks the card
element tree and returns a stable fingerprint that ignores .task-log-tail
and .running-log subtrees. Also preserves the existing log-tail node
across a real content-change swap so live EventSource streams survive.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(web): correct elapsed timer class/location, log-tail coverage, and tests (task 4 reviewer findings)</title>
<updated>2026-07-06T05:26:24+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T05:26:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=d2cbe9aa07afdc0aac33c959803d6a6aaef69b8e'/>
<id>urn:sha1:d2cbe9aa07afdc0aac33c959803d6a6aaef69b8e</id>
<content type='text'>
- Move elapsed timer span from card header to after meta block; add
  task-elapsed class alongside running-elapsed (Finding 1)
- Replace PENDING/QUEUED-only log-tail div with full if/else: placeholder
  div.task-log-tail-placeholder for queue states, div.task-log-tail with
  dataset.logTarget for all other states (Finding 2)
- Delete web/test/task-card-di.test.mjs; append correct tests to
  web/test/tasks-board.test.mjs covering log-tail and elapsed timer
  contracts (Finding 3)
- Thread doc parameter through createEditForm, renderSubtaskRollup, and
  renderQuestionFooter so createTaskCard is fully testable without a
  browser DOM

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(web): extend createTaskCard with DI doc param, log-tail placeholder, elapsed timer (task 4)</title>
<updated>2026-07-06T05:12:47+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T05:12:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=d9db12a25a42d00e0f64ac87e10ac0b612b841a8'/>
<id>urn:sha1:d9db12a25a42d00e0f64ac87e10ac0b612b841a8</id>
<content type='text'>
- Export createTaskCard and add `doc = document` parameter; replace every
  document.createElement call inside the function with doc.createElement,
  enabling unit testing without a real DOM.
- RUNNING tasks: append a span.running-elapsed (data-started-at, elapsed
  text) to the card header, consistent with renderRunningView so
  updateRunningElapsed() picks it up automatically.
- PENDING/QUEUED tasks: append div.task-log-tail &gt; p.task-log-placeholder
  ("Waiting to start…") — the queue column has no execution yet, so a
  placeholder is shown instead of an empty log box.
- New web/test/task-card-di.test.mjs covers DI doc plumbing, placeholder
  presence/absence, and elapsed-timer presence/absence across states.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(web/test): update tab-persistence default-tab assertions from 'queue' to 'stories'</title>
<updated>2026-07-06T04:40:24+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T04:40:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=694390df915f4fcbefd00d509bce4d33bed43868'/>
<id>urn:sha1:694390df915f4fcbefd00d509bce4d33bed43868</id>
<content type='text'>
Two test assertions in tab-persistence.test.mjs were checking that the default
tab is 'queue', but commit cc6b323 changed the default to 'stories' without
updating this test. Updates both the assertion values and the test description
strings to match current behaviour.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(web): reviewer findings on Tasks board column model (task 1)</title>
<updated>2026-07-06T01:38:50+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T01:38:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0ca84d28c6d358591582b5d61ef66ed0e266e260'/>
<id>urn:sha1:0ca84d28c6d358591582b5d61ef66ed0e266e260</id>
<content type='text'>
Finding 1 (Important): replace inline per-column sort comparators in
groupTasksByColumn with calls to the existing sortTasksByDate(tasks, descend)
helper. The old inline code fell back to epoch 0 for missing created_at,
sorting dateless tasks FIRST in ascending columns — diverging from the rest of
the codebase's convention (sortTasksByDate puts them LAST). Add two new tests
asserting that a task with no created_at sorts LAST in both an ascending
column (queue) and a descending column (interrupted).

Finding 2 (Important): revert web/test/tab-persistence.test.mjs — the two
'stories' assertions introduced in 54be094 were out of Task 1's scope and
belong to Task 2. Restore the original 'queue' assertions so commit messages
accurately reflect their contents; Task 2 will re-apply the fix intentionally.

Finding 3 (Minor): add the missing state-list uniqueness assertion to the
'every column key is unique' test: assert.equal(new Set(allStates).size,
allStates.length) so a state appearing in two columns is caught.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(web): add Tasks board column model (TASK_COLUMNS, columnForTaskState, groupTasksByColumn)</title>
<updated>2026-07-06T01:29:45+00:00</updated>
<author>
<name>Claudomator Agent</name>
<email>agent@claudomator.local</email>
</author>
<published>2026-07-06T01:29:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=54be094e65ffafe69e3490cbe9b5ae37d1fa927d'/>
<id>urn:sha1:54be094e65ffafe69e3490cbe9b5ae37d1fa927d</id>
<content type='text'>
Pure logic layer for the new Tasks tab Kanban board: a 5-column partition of
all 10 task states (Queue/Running/Ready/Interrupted/Done) with per-column sort
directions (Queue+Ready oldest-first, Running+Interrupted+Done newest-first).

Also fixes 2 pre-existing stale assertions in tab-persistence.test.mjs
(default tab is 'stories', not 'queue', since cc6b323).

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: task submission context cancellation, executions success-rate calc</title>
<updated>2026-07-05T23:51:58+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-05T23:51:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/claudomator.git/commit/?id=0e49f91c62ff9977c6a917a6f197adbd7876bbb9'/>
<id>urn:sha1:0e49f91c62ff9977c6a917a6f197adbd7876bbb9</id>
<content type='text'>
submitTask was using the inbound HTTP/MCP request context to submit work to
the executor pool, so every chatbot/REST-submitted task was cancelled the
moment the request returned rather than actually running. Use the server's
long-lived lifecycle context instead, matching the other Submit call sites.

Separately, computeExecutionStats compared execution state against a
lowercase 'completed' literal while the backend always emits uppercase
state values, so success rate silently read 0% for every execution. Also
treat READY as success alongside COMPLETED, since a top-level task's
execution lands at READY on success — COMPLETED requires a later, separate
accept step.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
</content>
</entry>
<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>
</feed>
