Commit Graph
245 Commits
Author SHA1 Message Date
istos c017639165 board: 48 Work report filed (istos) 2026-08-01 09:02:04 +02:00
istos b4f1282ab4 tasks: card 52 — a redraw throws away where you were looking
renderBoard() wipes #board and rebuilds every column, so each .drop is a
new node with scrollTop 0 — and render() runs on every SSE frame, so the
board resets under you continuously while agents work. The log already
solves this for itself with S.logStick; the columns never got it.
2026-08-01 09:00:43 +02:00
istos 146751c77f board: 48 → in-progress (istos) 2026-08-01 08:48:11 +02:00
istos a7096c0280 board: 51 → to-do (istos) 2026-08-01 08:48:03 +02:00
istos 35a4f1f36c board: 50 → to-do (istos) 2026-08-01 08:47:55 +02:00
istos 19717a30bd board: 49 → to-do (istos) 2026-08-01 08:47:44 +02:00
istos 0eee8d87a3 board: 48 → to-do (istos) 2026-08-01 08:47:36 +02:00
istos 9f4b3cae7f board: 37 → done (istos) 2026-08-01 08:47:23 +02:00
Ronald AshriandGitHub 5d3f22717f Merge pull request #39 from 12vectors/task/37-sync-resolves-its-remote-and-says-when-it-cannot
37 — Sync resolves its remote like the rest of the board, and says so when it cannot
2026-08-01 08:47:15 +02:00
istos 88c625188e board: 28 → done (istos) 2026-08-01 08:47:11 +02:00
Ronald AshriandGitHub 9e0ae30f33 Merge pull request #38 from 12vectors/task/28-a-pinned-port-stays-pinned
28 — A pinned port stays pinned: fix the TIME_WAIT hop that rewrites .env
2026-08-01 08:47:07 +02:00
istos 0224f3a73e tasks: phases — 48 the model, 49 the runner, 50 the UI, 51 the action
A phase is a card with Type: Phase and a ## Cards section listing its
members in run order; members carry Depends on, which guards rather than
orders. It runs on its own branch, each member branched from the phase
tip so card two sees card one's work, merged back on green, one PR into
main at the end — the human gate moves from every card to the phase
boundary, and 'the board never merges' stays true of main.
2026-08-01 08:46:09 +02:00
istos de4e7dc762 board: 37 PR opened (istos) 2026-08-01 08:03:03 +02:00
istos 97263c3691 board: 28 PR opened (istos) 2026-08-01 08:02:59 +02:00
istos 11e2f61ac7 tasks: 47 — 44 is done, so say so
The card warned that archiving does not commit and suggested sequencing
behind card 44. That landed before 0.3-alpha: archive_task goes out
through _relocate, attributed and committed under the same gate a move
is. Nothing blocks 47.
2026-08-01 07:43:01 +02:00
istos 51196cd06c tasks: card 47 — an archive button on the card
The tray works but the gesture is a drag across the whole board. Puts
the same glyph on the card's footer row, on backlog/to-do/done only,
arming on the first click as the other two-step actions do.
2026-08-01 07:39:04 +02:00
istos 038d258cb2 board: 37 → review (istos) 2026-08-01 07:35:57 +02:00
istos d57d3036fd board: 37 Work report filed (istos) 2026-08-01 07:35:57 +02:00
istos 7a27201dff board: 28 → review (istos) 2026-08-01 07:35:25 +02:00
istos f4796485c0 board: 28 Work report filed (istos) 2026-08-01 07:35:24 +02:00
istosandClaude Opus 5 58b658389b sync: ride the resolved remote, and say when there is none
REMOTE/UPSTREAM were module constants, so a team-mode board whose remote
is named anything but origin synced nothing at all — silently, with a
healthy header. The remote is now resolved per use (config's answer, the
one PRs already honour) and threaded through fetch, publish, replay and
integrate; every message names whatever was resolved.

The silence is the other half. Where _converge()/push_now() returned a
bare 'no-origin' that both callers discarded, _remote() now _note()s at
stalled level first: no remote at all names both fixes (add one, or set
BOARD_GIT_REMOTE), and a BOARD_GIT_REMOTE naming a remote this checkout
does not have stalls naming it rather than quietly using another. It
clears with a recovery line like the offline path, and install() asks at
startup so the chip is there from first paint, not the second beat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 07:33:39 +02:00
istosandClaude Opus 5 8194a043da config: one resolver for which remote this board rides
github.remote() decided it for PRs; sync assumed 'origin'. The answer
moves to config, which already owns BOARD_GIT_REMOTE, resolved on demand
so config still shells out nothing at import. github.remote() becomes a
thin call to it, and git_remotes() lets a caller check whether the name
resolves to a remote that exists rather than falling back silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 07:33:32 +02:00
istosandClaude Opus 5 6829136f42 start: a pinned port stays pinned across a restart
The free-port probe bound without SO_REUSEADDR while the board's own
ThreadingHTTPServer sets it, so the socket a just-stopped board left in
TIME_WAIT read as "taken by something else": a routine stop/start walked
the board to the next port and wrote that over the user's BOARD_PORT pin.
The probe now binds exactly as the server does, which is the whole race.

Behind it, a held port gets a few seconds (BOARD_PORT_WAIT, 5s) to clear
before the walk, re-asking is_our_board each beat — a restart races its
own predecessor far more often than a stranger takes the port. Walking
off a pinned port still persists, since the hooks and agents read
BOARD_PORT and must reach the live board, but it now says so in full:
the right file (manager/local/.env, not manager/.env), old → new, and
how to reclaim the pin.

Tested end to end against a scratch host with a stub board.py, over real
sockets: a genuine TIME_WAIT remnant, a listener that lets go mid-wait,
a listener that does not, and our own board answering.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 07:32:54 +02:00
istos 57b3073496 board: 37 → in-progress (istos) 2026-08-01 07:23:10 +02:00
istos adec6a3c37 board: 28 → in-progress (istos) 2026-08-01 07:21:58 +02:00
istos 727354487b README: the docs site, and the board it documents
A link to bench.12vectors.com under the opening paragraph, and the board
screenshot beneath it — the fastest way to say what this is.

The image is an absolute raw.githubusercontent URL rather than a repo
path, for two reasons. README.md ships in the release artifact and
site/ deliberately does not, so a relative link would break in every
vendored install. And the site's own copy is served under an immutable
year-long cache, so linking that would pin readers to whatever
screenshot was current when they first loaded the page.

The three-layer-law paragraph gains a clause and loses one. That section
is sliced into /concepts/three-layer-law/, so "or read them at
bench.12vectors.com" rendered as a page on that site telling you to go
to that site. It now says AGENTS.md is the file the docs site is cut
from, which is true in both places — the rule this repo sets for its own
prose.
2026-07-31 17:42:52 +02:00
istos 4660d0cb34 0.3-alpha
And un-pin the landing page's version test: it named 0.2-alpha as the
string to replace in VERSION, so the first bump after it was written
failed the suite. It reads the file now, which is what the test is about
in the first place.
v0.3-alpha
2026-07-31 17:28:20 +02:00
istos b031711e60 board: 44 → done (istos) 2026-07-31 17:22:33 +02:00
istos 22215753c6 board: 38 → done (istos) 2026-07-31 17:22:33 +02:00
Ronald AshriandGitHub 385acae35a Merge pull request #36 from 12vectors/task/44-archiving-a-card-reaches-git
44 — Archiving a card reaches git, and so does every other write the board makes to a task file
2026-07-31 17:22:25 +02:00
Ronald AshriandGitHub 97f6343275 Merge pull request #37 from 12vectors/task/38-a-card-being-completed-looks-like-it
38 — A card being merged and cleaned up looks like it, and holds still
2026-07-31 17:22:16 +02:00
istos 97d63216b6 site: take the six doors off the landing page
The page is now three things: what bench is, what installing costs, and
why to bother. The header nav is the way into the docs.

Removing them takes their CSS with them (.doors/.door* in the base sheet
and in all three breakpoints) and two tests that existed only for them —
the six-are-distinct check here and TheDoorsOpenOntoArticles in
test_site_pages. The hero-buttons test survives its class, renamed to say
what it now guards: the landing page's only links into the docs.

One test needed retargeting rather than deleting.
test_it_fails_before_anything_is_written removed /concepts/team-mode/
from the manifest to prove a dead internal link stops the build before
anything is written — but the link it relied on was a door. It now aims
at /guides/install/, which the hero button still hard-links.

Side effect worth having: the two reasons in "Why bench?" that nearly
repeated doors 03 and 04 no longer sit above them.
2026-07-31 17:20:49 +02:00
istos 90626b6405 site: a Why bench? section, and a console wide enough to read
Two changes to the landing page and the reference layout.

The three problem one-liners become "Why bench?" — four reasons now,
led by the one no hosted tool can answer: bench lives in your codebase
and is built to be extended, so the workflow it runs is exactly yours.
It stays unboxed on purpose; the doors below are the page's one grid of
cards and repeating that shape would make the eye read it twice.

The reference pages split the room right of the nav three fifths prose,
two fifths console. At the old fixed 208px every BOARD_AGENT_MODEL_* key
was cut mid-word, which makes a console of settings useless for the one
thing it is there for. Articles keep the narrow gutter — a list of
headings needs no more.

The narrow-width overrides are repeated at .page-reference .shell
specificity in both media blocks. Without that the base override (0-2-0)
outranks the bare .shell inside the query (0-1-0), the third track
survives, and with the gutter hidden it becomes two fifths of empty grid
squeezing the prose on every phone.
2026-07-31 17:08:55 +02:00
istos 6413893f19 board: 38 PR opened (istos) 2026-07-31 17:00:22 +02:00
istos 6fca91eed0 board: 44 PR opened (istos) 2026-07-31 17:00:22 +02:00
istos eedec02ef7 board: 38 → review (istos) 2026-07-31 16:57:34 +02:00
istos 9f6838e9fa board: 44 → review (istos) 2026-07-31 16:57:31 +02:00
istos a1124ec991 tests: the claim, the refusal, and the release on every exit
Against a real repo with a real branch: a real merge, a real conflict,
the wrong-branch refusal, a crash, and a second complete arriving from
inside the first one's merge — which is refused, starts no second merge,
and does not release the run it lost to. Plus the registry's own rules
and the card-face invariants in board.html.
2026-07-31 16:57:17 +02:00
istos 5736126142 …and the card wears it, and holds still
The card takes the accent border and a breathing `completing` pill in
the slot the status pill already owns — the working vocabulary, because
that is what is happening — and carries the latest narrated step on its
activity line, so "parking the drive" and "cleaned up: worktree and
local branch removed" land on the card and not only in the ticker.

While it is claimed the card offers nothing: no hover actions, no drive
or command chips (both run against the worktree being removed), and not
draggable, so the sheet cannot be reopened behind the run. Suppressed,
not ignored on click — an action that looks available and does nothing
is the same lie in a different place.
2026-07-31 16:57:16 +02:00
istosandClaude Opus 5 b462b83a7f Archiving a card reaches git, like every other task-file write
An archive renamed the file on disk and stopped there, leaving an
uncommitted deletion of a tracked file — exactly what sync._clean()
refuses to run over, so one drag onto the tray stalled every later board
commit, kept the archive off the other boards, and left the card in a
single working tree. Appended agent reports had the same gap.

taskfiles now has one door out of a stage directory, _relocate(): it
registers the expectation, writes, moves, and commits under the same
COMMIT_MOVES gate a move uses — so archive, unarchive and move all commit
by construction rather than by each caller remembering. The messages read
like the others (`board: <n> -> archived (<name>)`, and the stage slug on
the way back) and carry the `board: ` prefix sync's piggyback guard
requires; routing through _commit means state.task_committed() fires, so
the event-driven push publishes it.

The second door is append_to_task(), which writes into a card where it
stands and commits via commit_edit(); agents._file_report() uses it
instead of touching config.TASKS itself, so a run's closing report is
recorded rather than left modified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:57:09 +02:00
istos 7df6e19d0d completing a card is a state the board holds
Merge & clean up is a minute of destructive work behind one click, and
nothing recorded that it was running: /api/task/complete took a second
request as readily as the first.

state.py grows COMPLETING — claimed before the first step, released in a
finally after the last one, on success, conflict and crash alike. The
steps are already narrated as board events against the file, so
record_board_event folds the latest summary into the claim rather than
asking complete_task to report twice. It is memory, not disk: a board
killed mid-completion leaves no card stuck busy.

/api/state carries the registry and every change publishes it, so the
board renders the busy card from the server's truth rather than from
what one tab happened to click.
2026-07-31 16:57:09 +02:00
istos 25f5565fbd site: one thing in the hero, and an install section of its own
The landing page was carrying two large visuals stacked — a full
terminal in the hero and the board beneath it — which is what made it
read as busy. Now the hero is one third words and two thirds board: the
claim this page makes is a board, so the board is what it shows first
and shows large.

The terminal moves into a 50/50 section below, beside the copy that
explains what installing costs: one script, port 26071, two questions,
and the settings file it writes. The six doors are unchanged.

Smaller changes that follow from it: the headline takes a narrower step
(--t-hero-narrow, 40px) because it no longer has half a row; it loses
its hard <br> and wraps on its own; the CTA drops the time claim now
that a whole section makes it; and the caption under the shot becomes
one plain line rather than a tagged row.
2026-07-31 16:55:35 +02:00
istos d3a43ef1ae install: detect the test command instead of asking for it
The third setup question wanted an answer about a repo the person may
have just cloned, thirty seconds in, before anything had explained why
the board needed one. The file that names a project's ecosystem usually
names its runner too, so it is read off the project: package.json → npm
test, Cargo.toml → cargo test, go.mod → go test ./..., a
pyproject/setup.py/tests/ → python3 -m unittest.

Nothing recognisable writes BOARD_AGENT_COMMANDS empty rather than the
example's Python default, which would be quietly wrong in most repos —
and a wrong prefix denies an agent exactly the way an empty one does, so
absent beats wrong here.

Two details the first attempt got wrong, both now covered:

- Detection runs *before* the questions. Placed after them, a Ctrl-D
  part-way through skipped it and left the example's default.
- "Keep what the project already said" reads the existing .env, not the
  example-seeded defaults dict — which already carries the example's
  answer, so every first run looked like it had one and detection never
  ran.

The landing page's terminal, README.md and AGENTS.md follow: two
questions now, and AGENTS.md documents the detection table.
2026-07-31 16:47:30 +02:00
istos 866c4370c6 board: 44 → in-progress (istos) 2026-07-31 16:47:30 +02:00
istos 88cb9e2e05 board: 38 → in-progress (istos) 2026-07-31 16:47:30 +02:00
istos 3b3c1086d1 board: 45 → done (istos) 2026-07-31 16:47:30 +02:00
istos 63426eb9cc board: 42 → done (istos) 2026-07-31 16:47:30 +02:00
Ronald AshriandGitHub 028635e3fc Merge pull request #35 from 12vectors/task/45-a-past-agent-session-is-not-you
45 — A past agent session is labelled "You", because "You" is what the board says when it does not know
2026-07-31 16:40:50 +02:00
Ronald AshriandGitHub 09cf11e73c Merge pull request #34 from 12vectors/task/42-the-report-keeps-its-headline
42 — A long closing report loses its head, which is where it says what happened
2026-07-31 16:40:40 +02:00
istos 0bae23e833 board: 45 PR opened (istos) 2026-07-31 16:35:49 +02:00