Skip to content

Print a board.yaml to start a board from - #338

Merged
takashi-matsuyama merged 5 commits into
mainfrom
feat/board-init
Oct 9, 2026
Merged

takashi-matsuyama merged 5 commits into
mainfrom
feat/board-init

Conversation

@takashi-matsuyama

Copy link
Copy Markdown
Member

Summary

Adds basou board init (experimental): it prints a board.yaml to start a board from, and writes nothing.

  • What it prints: a declaration of the newest board_version, in the language the manifest declares for the workspace's own repo:
    • a title from the workspace's name;
    • the six stages, each with a meaning (concept, spec, built, merged, open, live), with the words for Japanese in view-strings.ts;
    • one sample lane, replace-me, to cut again (a board needs one);
    • nothing to observe;
    • no component registered: the first measurement names every one it finds, and that fires the axis review that registers them;
    • an axis of version 1, reviewed every 60 days;
    • the effort from the day of the first session in this host's time zone. That is the default the board page uses with no board (today when there is no session).
  • When the host's zone has no name a board can declare (none, or an offset such as +00:00):
    • the time zone is left out, and a comment and stderr say to write it;
    • with no name at all, the start is today in UTC, and the comment says so.
  • How to save it: run init only from the top of the workspace's own repo, then
    mkdir -p board && f=$(mktemp board/.board.yaml.XXXXXX) && { basou board init > "$f" && ln "$f" board/board.yaml; s=$?; rm -f "$f"; rmdir board 2>/dev/null; [ "$s" -eq 0 ]; }
    • The file is made inside board/ by mktemp.
    • It is linked into place only where no board.yaml is; a link never replaces one.
    • The temporary file is removed either way, and so is board/ when nothing else is in it.
    • init's help and basou board guide both print this command (BOARD_INIT_SAVE in @basou/core). Neither suggests printing straight into board/board.yaml: the shell empties that file before init runs.
  • It exits 1, printing nothing, when:
    • a board is already declared (a link to nothing included);
    • the workspace's own repo is not declared private;
    • it is run from anywhere other than the top of that repo. From a member repo, a workspace view or a subdirectory, the save would have landed elsewhere, a public repo included.
  • The guide's steps for a workspace with no board now start from init.
  • @basou/core (Internal):
    • added: boardInitText, BOARD_INIT_SAVE, BOARD_INIT_LANE_ID, BOARD_INIT_REVIEW_DUE_DAYS, effortStartOf, isBoardTimeZone and boardInitStrings;
    • escaping: the guide and init now escape the remaining bidirectional and invisible characters in what they print.

basou board stays experimental. init writes nothing, so it touches no invariant of compatibility.md, and the README does not change either. docs/spec/cli-commands.md lists the subcommand.

Why

Starting a board meant writing board.yaml by hand and fixing it against basou board measure's errors. init gives a board that reads as printed, in the anchor's language. Its start date is the one the board page already uses.

Printing straight into board/board.yaml would empty an existing board before init could refuse, so the save goes through a file of its own.

Verification

  • CI steps, run locally with exit codes: pnpm -r build, pnpm typecheck, pnpm lint (the existing 4 warnings only), pnpm lint:lang, pnpm lint:paths, pnpm test:scripts, pnpm test:coverage: all 0.
    • The board and view tests also pass under TZ=America/New_York and Asia/Tokyo.
    • The init tests also pass under America/Los_Angeles and GMT.
  • Tests:
    • the printed board reads under the workspace's manifest, in English and Japanese;
    • names with quotes, $&, newlines and bidi characters read back as written;
    • an unnamed zone and an offset zone each leave the time zone out, with the right comment and stderr line;
    • the start is the first session's day, or today when the sessions cannot be read;
    • init refuses an existing board, a workspace not declared private, and a run from a subdirectory;
    • the save command runs in sh: it writes once, refuses the second time with the board untouched, and leaves nothing behind when init fails;
    • the guide refuses a board.yaml that is a link to nothing.
  • Mutations: 7 on the first version and more on the review's fixes, applied one at a time. All are caught.
  • By hand, in a scratch workspace with a Japanese anchor and the built CLI:
    • the guide printed with no board, its save block run (exit 0, then exit 1 with the board kept);
    • basou board measure then read the saved board.

Review

One round of convergent review (review only), with the decided scope given as fixed. 10 findings (2 medium, 8 low), all fixed in the fourth commit:

  • on hosts whose zone Node names as an offset, init printed a time_zone that measure refuses;
  • the save wrote relative to the current directory, so from a member repo, view or subdirectory it put the board elsewhere;
  • with no named zone, the start was today in UTC while its comment said the first session's day;
  • mktemp left files in the temporary directory, and mv could replace a board that appeared in between;
  • a CLI test hung on the host's time zone;
  • a board.yaml that is a link to nothing got three contradictory answers;
  • some bidi and invisible characters were printed raw;
  • the guide with no board did not name the basou that printed it;
  • a comment misnamed where the title comes from;
  • no test covered the stderr notice for unreadable sessions.

Not taken up:

  • the Japanese name of stage 04, which reads as "production" (kept: the same words as the existing board);
  • the 0600 mode a mktemp file carries.

Checklist

  • Branch is rebased on main.
  • One topic (init; later commits are its review's fixes).
  • Tests cover the change (happy path and failure modes).
  • pnpm lint, pnpm typecheck, and pnpm test are all green locally.
  • CHANGELOG.md ## Unreleased updated.
  • CLI stderr stays pathless (the new messages name no path).

🤖 Generated with Claude Code

takashi-matsuyama and others added 5 commits October 9, 2026 17:34
basou board init prints a declaration of the newest board_version, in
the anchor's language: a title from the workspace's name, the six stages
with a meaning each, one sample lane to cut again, nothing to observe,
no component registered, an axis of version 1, and the effort from the
day of the first session in this host's time zone (the same default the
board page measures with). It writes nothing; it stops, printing
nothing, where a board is already declared or the workspace's own repo
is not declared private.

Its help and the guide say to save it through a file of its own, moved
into place only where no board is: printing straight into
board/board.yaml would let the shell empty an existing board before init
could refuse. The guide's steps for a workspace with no board now start
from init.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The help said to print the board to a fixed name under the temporary
directory, which on a /tmp other users share is a path another user can
place first. mktemp makes a file of the user's own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Leave out a time zone a board cannot declare (an offset such as +00:00,
  which some hosts name their zone), and say on stderr and in a comment
  when the start is today in UTC because the zone has no name.
- Run only from the top of the workspace's own repo, where the save
  writes: from a member repo, a view or a subdirectory the board would
  land elsewhere, a public repo included.
- Save through a file of its own inside board/, linked into place only
  where no board.yaml is and removed either way, with board/ when nothing
  else is in it: nothing is left in the temporary directory, and a board
  that appears meanwhile is never replaced. The guide shares the command.
- The guide refuses a board.yaml that is a link to nothing, which init
  would not replace, and names the basou that printed it with no board
  declared too.
- Escape the remaining bidirectional and invisible characters in what
  init and the guide print, and fix a comment and a test that hung on
  the host's time zone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node 20, which CI runs, refuses an offset such as +00:00 as a time zone,
so a host named so has no zone at all there, while Node 24 takes it. The
test now expects what the running Node does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takashi-matsuyama
takashi-matsuyama merged commit 31341eb into main Oct 9, 2026
8 checks passed
@takashi-matsuyama
takashi-matsuyama deleted the feat/board-init branch October 9, 2026 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant