Commit Graph
100 Commits
Author SHA1 Message Date
istos 11e84d94c0 board: 20 → archived (istos) 2026-08-02 12:34:34 +02:00
istos f4104d605d board: 19 → archived (istos) 2026-08-02 12:34:31 +02:00
istos b421291f15 tasks: phase 61 — what an agent is told before it starts
Runs 55 (the prompt says the run is one turn), then 54 (start_agent
refuses a phase card), then 46 (a board with no runnable commands says
so). No card depends on another; 55 is prompt-only and goes first, 54
and 46 both edit board.html so they run adjacent.

Also the first exercise of the Phases view: 56-59 landed after phase 53
finished, so hiding, the lane, the move refusal and the sweep have not
yet been watched on a live run.
2026-08-02 12:34:29 +02:00
istos c012987490 board: 18 → archived (istos) 2026-08-02 12:34:28 +02:00
istos a32ee33653 board: 17 → archived (istos) 2026-08-02 12:34:24 +02:00
istos d97fc4f34d board: 16 → archived (istos) 2026-08-02 12:34:21 +02:00
istos b20ef0344f board: 10 → archived (istos) 2026-08-02 12:34:18 +02:00
istos 0b975ce502 board: 04 → archived (istos) 2026-08-02 12:34:11 +02:00
istos e488a73832 board: 01 → archived (istos) 2026-08-02 12:34:02 +02:00
istos 8a69df87de board: 59 → done (istos) 2026-08-02 12:31:28 +02:00
Ronald AshriandGitHub 0745e68883 Merge pull request #50 from 12vectors/task/59-finishing-a-phase-finishes-its-cards
59 — Finishing a phase finishes its cards, and clears up after them
2026-08-02 12:31:20 +02:00
istos f601a078d5 board: 59 PR review filed (istos) 2026-08-02 12:21:04 +02:00
istos 7b6ccc8f86 board: 59 PR opened (istos) 2026-08-02 10:08:58 +02:00
istos f162a79822 board: 59 → review (istos) 2026-08-02 10:08:49 +02:00
istos a7e9f6aae2 board: 59 Work report filed (istos) 2026-08-02 10:08:49 +02:00
istosandClaude Opus 5 b690060be2 Finishing a phase finishes its cards, and clears up after them
Merge & clean up on a phase card now ends the phase: after the merge into
main has actually succeeded, every member the phase merged moves to done/
and its workspace is cleared the way completing an ordinary card clears
its own — worktree removed, local branch deleted, branch on the remote
deleted.

Only what the phase merged. A member is swept when its card settled into
review/ or done/ and its branch is contained in the phase branch (or
there was never a branch to bring) — the same pair the runner reads a
member as merged by. One that halted, was held or was walked back keeps
its card, its worktree and its branch: there is work in them.

Nothing uncommitted is thrown away: a member's worktree comes out without
--force, and a dirty one is reported in the ticker and kept with its
branch rather than forced.

One ending, told once. taskfiles.move_together moves the cards in a
single commit naming all of them (`board: 47, 52 → done with phase 53`,
so it publishes in team mode like any other board commit), and the moves
are marked quiet so the watcher does not also scroll five identical move
lines behind the one line the ending gets.

The other endings are untouched: "just move the card" and archiving the
phase card move no member, because neither puts anything in main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 10:08:00 +02:00
istos 064ed1f505 board: 59 → in-progress (istos) 2026-08-02 09:33:18 +02:00
istos f0b6cbf01c board: 58 → done (istos) 2026-08-02 09:33:07 +02:00
Ronald AshriandGitHub 97ea130063 Merge pull request #49 from 12vectors/task/58-a-phase-card-will-not-move-while-its-work-runs
58 — A phase card will not move while its work is running
2026-08-02 09:32:59 +02:00
istos d08723c72f board: 58 PR review filed (istos) 2026-08-02 09:29:49 +02:00
istos 69b1b4938a board: 58 PR opened (istos) 2026-08-02 09:23:55 +02:00
istos 292d1f93c9 board: 58 → review (istos) 2026-08-02 09:23:48 +02:00
istos a7243f0d20 board: 58 Work report filed (istos) 2026-08-02 09:23:48 +02:00
istosandClaude Opus 5 8fbfc1085d A phase card does not move while its work runs
Dragging a phase card between stages while one of its members has an
agent in it is a move nobody can mean: the card lands somewhere its
branch, its worktree and its live agent are not. The board refuses it,
and names the way through rather than the wall — which member is
working, and that ‖ hold stops the phase and the agent it has in flight
while leaving the branch, the merges and every worktree as they were.

- phases.assert_not_working() is the refusal: it reads the one file
  first, so an ordinary card never reaches the question, then resolves
  the phase's list and asks what is actually running.
- agents.working_on() answers that from the processes themselves, not
  from the registry's status alone — the reaper flips that a moment
  after a run ends, and a rule that only refuses must not hold a card
  hostage to a run that has already died. stop_phase() now reads the
  same helper.
- httpd asks it on /api/move, /api/archive (archiving is a move) and
  /api/task/complete, so a stale page cannot get past it.
- The toast wraps, is bounded to the viewport and stays up for as long
  as its text takes to read: a refusal you cannot finish reading is the
  wall this was written against.

What still moves: a phase between members, a halted phase, a held one,
a member card, and every ordinary card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 09:23:23 +02:00
istos 208077073e board: 58 → in-progress (istos) 2026-08-02 09:07:04 +02:00
istos 00f2a91ccf board: 57 → done (istos) 2026-08-02 09:07:03 +02:00
Ronald AshriandGitHub 8b6ba0c67f Merge pull request #48 from 12vectors/task/57-the-phases-view-a-swimlane-each
57 — The Phases view: a swimlane each
2026-08-02 09:06:55 +02:00
istos 41d30097c3 board: 57 PR update filed (istos) 2026-08-02 09:06:24 +02:00
istosandClaude Opus 4.8 99fb248615 Phases view: a held run sorts above the phases nobody has started
laneRank() lumped a held (stopped) phase in with unstarted phases at
the same rank, so an actionable "stopped — held by …" lane could sort
below phases nobody has started. The snapshot already carries
snap.stopped, so give a held run its own rank just below running,
keeping it near the top where a person can resume it. Addresses the
Copilot review point on PR #48.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-02 09:06:10 +02:00
istos 6e5e212e9b board: 57 PR opened (istos) 2026-08-02 08:55:47 +02:00
istos 6d7894a901 board: 57 → review (istos) 2026-08-02 08:55:39 +02:00
istos 7024c45d46 board: 57 Work report filed (istos) 2026-08-02 08:55:39 +02:00
istosandClaude Opus 5 a3388ed2ea The Phases view: a swimlane each
Card 56 took a phase's members off the Board; this is where they went. A fourth view beside Board, Sessions and Focus, holding one lane per phase card — every phase there is, not only the running ones, because a phase whose cards are all merged and which is waiting on its own PR still owns them.

A lane is a head, five stage columns and the phase's log. The head carries the phase, its progress, the member in flight and its own controls — hold, the phase branch, the phase card — and nothing that ends a phase, which stays a board move on the phase card. Four columns are the board's stages; the fifth is 'Merged in', not done/, because a member merged into the phase branch is finished as far as the phase is concerned and is not in main yet. Which members the branch holds is the runner's last pass while a phase runs, and the phase log once there are no more passes, so a phase in review/ still draws the work where it landed. The cards are the cards: cardFor() builds them here exactly as it builds them for the Board, agent line, chips, position-in-the-run chip and hover actions intact. The phase log sits under the lane, the runner's decisions in the order it made them, read off the card's own '## Phase log' — the only thing that can tell "not reached yet" from "started and ended badly".

A halted lane says so at the top of itself, names the card it stopped on, and offers run again beneath the reason. That action left the Board's phase card for the same reason: clearing a halt should mean having read what caused it, so the card offers a way into the view instead, and hold, which needs nothing read. The crossing the other way is the switcher, which carries how many phases are running and an alarm mark while one has halted — on every view, alongside the toast and the ticker line that already fire.

Covered by tests/test_phases_view.py: collect() reads a throwaway tasks/ tree and the page's own lane rules run over that reading in node (ordering, placement, the last column, the log, the all-merged phase in review/), plus source-level wiring invariants for the DOM half. test_phase_watch, test_card_actions and test_scroll_kept updated where this moves what they pin. Full suite green: 939 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 08:55:17 +02:00
istos a4fdb4f34d board: 57 → in-progress (istos) 2026-08-02 08:29:14 +02:00
istos 0868a942e2 board: 56 → done (istos) 2026-08-02 08:29:08 +02:00
Ronald AshriandGitHub 2f0e7f410b Merge pull request #47 from 12vectors/task/56-phase-members-leave-the-main-board
56 — Phase members leave the main board
2026-08-02 08:29:04 +02:00
istos b9400263fb board: 56 PR opened (istos) 2026-08-02 07:57:24 +02:00
istos ecef8ca7f2 board: 56 → review (istos) 2026-08-02 07:57:16 +02:00
istos 07fae2b801 board: 56 Work report filed (istos) 2026-08-02 07:57:16 +02:00
istos 3365919ca4 Phase members leave the Board view
The Board view stops drawing a phase's members: the phase card stands for them there, and the columns go back to counting what you can see. Nothing is deleted, moved or marked — a member keeps its stage, its file, its agent and its actions.

heldByPhase()/columnCards()/columnNote() in board.html: the count is the length of the list drawn, and '+2 in phases' beside it says where the rest went, only where members are actually hidden. Membership is the only thing that hides a card, so removing it is the un-hiding: an archived phase card and an edited '## Cards' list stop deriving it, and a phase in done/ holds nothing. A card wearing phase drift is never hidden. The phase card carries the summary it now owes: a '1 of 2 merged' chip opening its own sheet, the member in flight on its activity line, and a halt worn in --alarm. Only the Board hides; Sessions, Focus, the header's live chip and the tab title still see an agent working on a hidden member.

Covered by tests/test_phase_members_hidden.py — collect() reads a throwaway tasks/ tree and the page's own rules run over that reading in node, plus source-level wiring invariants. Full suite green.
2026-08-02 07:56:59 +02:00
istos dee9bbb1a4 board: 56 → in-progress (istos) 2026-08-02 07:40:22 +02:00
istos bea169ee0b board: 60 → archived (istos) 2026-08-02 07:40:13 +02:00
istos e8732e93f9 board: 59 → to-do (istos) 2026-08-02 07:39:52 +02:00
istos fb43e1c414 board: 58 → to-do (istos) 2026-08-02 07:39:49 +02:00
istos 3042648643 board: 57 → to-do (istos) 2026-08-02 07:39:47 +02:00
istos 616f5afc17 board: 56 → to-do (istos) 2026-08-02 07:39:45 +02:00
istos 37b96eb88e board: 52 → done (istos) 2026-08-02 07:38:43 +02:00
istos 829e13ed3b board: 47 → done (istos) 2026-08-02 07:38:43 +02:00
istos 4c40e62532 board: 53 → done (istos) 2026-08-02 07:37:27 +02:00
Ronald AshriandGitHub 29438fbfc3 Merge pull request #46 from 12vectors/phase/53-the-board-stops-fighting-you
53 — Phase: the board stops fighting you
2026-08-02 07:37:19 +02:00
istos d978d6ad42 tasks: 59 also clears the workspaces a phase leaves behind
complete_task works from the stem of the card it is completing, so a
finished phase leaves every member's worktree, local branch and remote
branch in place — measured on 53: three worktrees at 3.8M each and two
member branches still on origin. The sweep now removes them, with the
existing rule intact for a member that never merged: there is work in
that worktree, so it stays.
2026-08-02 07:33:55 +02:00
istos 93f0304a2c tasks: card 60 — a phase stacks its pull requests
Today the runner merges each member into the phase branch as soon as CI
is green, which closes the member's PR and flattens the stack as it is
built: phase 53 handed back one +807 diff for two cards. Stop merging
during the run, chain each member's branch on the one before it, and let
the phase card sit on top. Carries one open question — how the stack
lands — which is a decision only the author can make.
2026-08-02 07:27:37 +02:00
istos 2b9199780f board: 53 PR opened (istos) 2026-08-02 07:20:19 +02:00
istos b4b1f73890 board: 53 → review (istos) 2026-08-02 07:18:55 +02:00
istos 7494f79cdc board: 53 phase log (istos) 2026-08-02 07:18:55 +02:00
istos e683387388 board: 53 phase log (istos) 2026-08-02 07:18:53 +02:00
istos e894b22c7c phase: merge task/47-an-archive-button-on-the-card into phase/53-the-board-stops-fighting-you 2026-08-02 07:18:51 +02:00
istos bedbb56537 phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-02 07:18:48 +02:00
istos dbaa7c9282 board: 53 phase log (istos) 2026-08-02 07:18:46 +02:00
istos 4ce8b11d9b board: 47 PR opened (istos) 2026-08-02 07:17:20 +02:00
istos a6291ec7ba board: 47 → review (istos) 2026-08-02 07:17:14 +02:00
istos c256d80efc tasks: phases get a room of their own — 56, 57, 58, 59
56 takes phase members off the Board view (counts describe what is
visible; membership is the only thing hiding them, so done, archived or
an edited list gives them back). 57 draws them instead: a Phases view,
one swimlane per phase, the phase log under it, run-again where the halt
is. 58 refuses to move a phase card while a member has a live agent —
'hold the phase first' — and covers archive, which is the path that
would really have hurt. 59 sweeps merged members to done/ when the phase
merges, so an ending reads as an ending.
2026-08-02 07:15:00 +02:00
istos b6091809fe tasks: card 55 — a headless run gets one turn, and nothing says so
Card 47's agent ended its turn with 'the suite is still running; I'll
wait for the monitor', which in a one-shot claude -p run is the end of
the run — 314 lines of finished, passing work staged and never
committed. The prompt never states that the run is a single turn or that
uncommitted work dies with it.
2026-08-01 19:57:50 +02:00
istos 661dd89af4 An archive chip on the card, where the card is
Every card in backlog/, to-do/ and done/ gets a ⌸ chip at the end of its
footer row — the tray's own glyph — arming on the first click and
archiving on the second, through the routes and the ARCHIVE_FROM guard
that already existed. In-progress and review cards do not get one: the
board offers what it will do.

Committed by hand after the run that wrote it ended mid-turn without
committing. The work is the agent's; the suite passes on it (875 tests)
and it is unchanged from what the run left staged.
2026-08-01 19:57:03 +02:00
istos 887fa43e36 board: 53 phase log (istos) 2026-08-01 17:28:20 +02:00
istos 91622d1ecf phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-01 17:28:18 +02:00
istos 89763d23f9 board: 47 Work report filed (istos) 2026-08-01 17:27:46 +02:00
istos bb35ca1985 phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-01 17:16:30 +02:00
istos 98ceb1bb17 board: 53 phase log (istos) 2026-08-01 17:15:58 +02:00
istos fc9b6fa14a board: 47 → in-progress (istos) 2026-08-01 17:15:58 +02:00
istos 3d29f0f4c0 board: 53 phase log (istos) 2026-08-01 17:15:58 +02:00
istos fa7b92110c board: 52 PR opened (istos) 2026-08-01 17:15:57 +02:00
istos 221151f9b0 phase: merge task/52-a-redraw-throws-away-where-you-were-looking into phase/53-the-board-stops-fighting-you 2026-08-01 17:15:56 +02:00
istos c1cd433a91 phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-01 17:15:53 +02:00
istos 3b01f5c7c5 board: 52 → review (istos) 2026-08-01 17:15:50 +02:00
istos 2350491209 board: 52 Work report filed (istos) 2026-08-01 17:15:50 +02:00
istos 419daef5e6 A redraw keeps where you were looking
renderBoard() starts with `board.innerHTML = ''` and rebuilds every
column, so each column's scrolling .drop was a brand-new node on every
pass — and a new node's scrollTop is 0. render() runs on every SSE frame
and a working agent emits an event per tool use, so a column being read
snapped back to the top several times a minute: worst exactly when there
is most to read. The same wipe threw away #board's horizontal position,
and the same demolition costs the session timeline, the sessions rail,
the Focus view, the drawer, and the activity log's place whenever it was
not stuck to the bottom.

Rather than a reconciling render — the real cure, and a far larger change
to the way the page works — the position is paid for separately: note
where each scroller was before the wipe, put it back once the new nodes
are in, both halves inside the same synchronous render so nothing
flashes. Keys are stable names rather than nodes (the stage slug for a
column, the session id for a timeline), because the node is what does not
survive. Restoring clamps, so a column whose cards moved on lands at its
new bottom instead of past it, and one now shorter than its own viewport
lands at the top instead of at a negative offset.

A `v:` prefix marks a key belonging to a view, and switching views drops
those: coming back is a fresh look, not a stale offset from a previous
visit. The activity log spans every view, so its key carries no prefix
and outlives the switch. The two behaviours that were already right are
left alone and now have tests holding them there — the log still follows
the newest line when it was stuck to the bottom (that reading runs after
the restore, so it wins), and the drawer still opens at the top when you
select a different card, which falls out of keying the drawer per
document rather than being fought for.

The helpers run for real under node, as tests/test_drawer_markdown.py
already does with md(); the source-level invariants beside them hold the
mark/restore pairs in order around each wipe, and one of them fails if a
new scrolling element is added to the CSS without a renderer keeping its
place.

    python3 -m unittest discover -s tests   → 854 tests, ok
2026-08-01 17:15:30 +02:00
istos bf1fb992b9 phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-01 17:01:58 +02:00
istos 21a819080a tasks: card 54 — a work agent must refuse a phase card
/api/phase/run guards itself (phases._phase_card refuses a non-phase);
/api/agent/start has no phase check at all, so a stale tab launched an
ordinary work agent on card 53 and it implemented both members itself.
The gate that was missing is on the older feature — the usual shape:
a new capability guards its own front door and leaves the neighbour's
open.
2026-08-01 17:01:44 +02:00
istos 1eaca0734b phase: merge origin/main into phase/53-the-board-stops-fighting-you 2026-08-01 17:00:51 +02:00
istos 438a1d8a04 board: 53 phase log (istos) 2026-08-01 17:00:44 +02:00
istos ea1f0def96 board: 52 → in-progress (istos) 2026-08-01 17:00:43 +02:00
istos bfdcdec000 board: 53 phase log (istos) 2026-08-01 17:00:41 +02:00
istos bef6a642f7 board: 53 → in-progress (istos) 2026-08-01 16:48:36 +02:00
istos 25311aa2bc board: 53 → to-do (istos) 2026-08-01 16:48:28 +02:00
istos 6744177c50 tasks: phase 53 — the board stops fighting you
Runs 52 (scroll position survives a redraw) then 47 (an archive chip on
the card). Neither depends on the other, so neither carries a Depends on
line — they are phased because they both edit board.html, and two agents
branched from main would collide in it. Sequencing gives the second one
the first one's work as its branch point.
2026-08-01 16:47:34 +02:00
istos cb29504ff0 board: 51 → done (istos) 2026-08-01 16:44:20 +02:00
Ronald AshriandGitHub 09ab2c988f Merge pull request #43 from 12vectors/task/51-add-a-card-to-a-phase-from-the-board
51 — Add a card to a phase without opening the file
2026-08-01 16:44:16 +02:00
istos 6d1ebdef43 board: 51 PR review filed (istos) 2026-08-01 16:39:06 +02:00
istos c8ff9cb356 board: 51 PR opened (istos) 2026-08-01 16:34:15 +02:00
istos 648566e8fb board: 51 → review (istos) 2026-08-01 11:23:41 +02:00
istos a1a9a6f921 board: 51 Work report filed (istos) 2026-08-01 11:23:40 +02:00
istosandClaude Opus 5 463b35bdaf A card joins a phase without the file being opened
Phases are meant to arrive whole — members listed, dependencies filled in, readable in a diff before any of it runs. This is the other case: the card you decide belongs after all.

⟶ phase sits on backlog/ and to-do/ cards that are not already in a phase and are not phase cards themselves, and opens a sheet naming the phase cards waiting in to-do/ with what each already holds. Picking one appends `- <n> — <title>` to the end of that phase's ## Cards — the way a person writes it, because the section is authored and read by hand and a machine-shaped line is how a format stops being pleasant.

Only to-do/. A phase in in-progress/ is running: its branch exists and its members are being worked in the order the list had when it started, so appending mid-flight is a different feature with different questions. Offer it and someone finds that out the hard way. No phase waiting there and the action is absent rather than present and empty.

Nothing else moves. One line into the phase card, nothing at all into the card added — membership runs one direction and joining a phase is not a commitment to start it. The append goes out through append_to_section, the same door the phase log uses, so it commits itself under BOARD_COMMIT_MOVES, reaches the other boards, and reads the phase card off the disk rather than off a render: two boards adding to one phase produce two lines, not a lost one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:21:27 +02:00
istos 24f118df48 board: 51 → in-progress (istos) 2026-08-01 11:09:21 +02:00
istos 26aa7cf929 board: 50 → done (istos) 2026-08-01 11:09:21 +02:00
Ronald AshriandGitHub 97c42913ae Merge pull request #42 from 12vectors/task/50-the-board-shows-a-phase-running
50 — The board shows a phase running, and shows it stopping
2026-08-01 11:09:13 +02:00
istos ea905bc5c4 board: 50 PR opened (istos) 2026-08-01 11:07:08 +02:00
istos ba3b050c40 board: 50 → review (istos) 2026-08-01 10:33:03 +02:00
istos 646db08b6e board: 50 Work report filed (istos) 2026-08-01 10:33:03 +02:00
istosandClaude Opus 5 c49f5bd014 The board shows a phase running, and shows it stopping
A phase card inherits the PR chip, CI, the review actions and the
merge-and-clean-up sheet by being a card. Three things it does not
inherit, and this is them.

The runner learns to stop. `stopped — held by <name>` joins `halted` and
`run started` in the phase log, and one reader (`run_state`) answers
where a phase is from the last line that says: running, halted, stopped
or never run. `stop_phase` writes that line — so the next beat stands
down — and holds the member agent in flight, because a phase that said
it had stopped while its agent kept working would be lying about the one
thing it was asked. Nothing is unwound: the branch, every card merged
into it and every worktree are left exactly as they were. A halted phase
can be held too, which is the other half of the halt's promise that it
holds until the phase is run again or stopped.

The halt is now told three times, as a dead run is: the state, the
ticker line it already had, and a toast — rare, actionable, and the
whole argument for starting a phase and walking away. Every advance is
narrated: the member that came up green, the merge, the next start.

The page: run phase (take over on someone else's) in the slot start work
occupies, hold while it runs, and a header chip beside the agents chip —
breathing accent with the phase, its progress and the card in flight,
alarm and holding when it halts, absent entirely when there is nothing
to say. One chip per phase, because two phases could in principle run at
once and a chip that showed one of them silently would be worse than
none. Opening a phase card lists its members in run order with each
one's stage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:32:32 +02:00