Skip to content

Let a board declare what to observe and what each stage is judged by - #336

Merged
takashi-matsuyama merged 3 commits into
mainfrom
feat/board-declaration-v2
Oct 9, 2026
Merged

takashi-matsuyama merged 3 commits into
mainfrom
feat/board-declaration-v2

Conversation

@takashi-matsuyama

@takashi-matsuyama takashi-matsuyama commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

A board's board.yaml may now be board_version: 2, which declares what a judge observes outside basou (observe) and what to look at when judging each stage (stages.<id>.look and .notes). basou makes none of these observations; it reads them, holds each value to a safe shape, checks a record's observations against them, and keeps them in the record.

  • observe: a list of observations, each with the key its record holds it by, of seven kinds:
    • npm_version (package), the published version of an npm package;
    • github_release, github_open_issues, github_open_prs (repo as owner/name);
    • github_ci (repo, workflow as a file name, branch, by default main), the conclusion of the workflow's latest run;
    • page_version (url), the version a web page shows;
    • manual (how, in words), one only a person can make.
  • Shapes: each value is checked when the board is read, since a judge will put it into a command as one single-quoted shell word: a lowercase npm package name (scoped or not, at most 214 characters, no leading -), owner/name (an owner may hold _, as a managed user's handle does), a workflow file name ending in .yml/.yaml with no leading -, a branch name with no leading -, .., // or trailing / or ., and an https:// URL in printable ASCII with no quotes, backslashes, < or >, and a host written right after https:// with no @ before it (no user name or password, not even empty ones). A manual observation's how is words for a person, never a command word. Keys are ids and may not repeat.
  • Stages: look and notes are optional lists of non-empty strings.
  • Version 1 is read as before. A version 1 file that has observe (at the top) or look / notes (in a stage) is refused, and told on a line of its own that version 2 reads them, apart from keys no version reads. An unknown version names both versions this basou reads.
  • Records: a board that declares observations is recorded with exactly those (one not made as null with its error); one left out, or one the board does not declare, is refused and nothing is written. A board with no observations leaves the names free, as before.
  • record_version: 2: the declaration's snapshot now holds observe and the stages' look and notes. Every new record is version 2; version 1 records are still read (previous record, axis review) and drawn by basou view.

basou board stays experimental (no change to compatibility.md or the README). @basou/core exports BOARD_VERSIONS, BOARD_RECORD_VERSIONS, BOARD_OBSERVE_KINDS, BOARD_DEFAULT_CI_BRANCH and the types BoardObserve and BoardObserveKind (Internal).

Why

Today each workspace that wants a judged board also carries a skill of its own: a script that knows what to observe outside basou (npm, GitHub, a public site) and notes on what each stage is judged by. Declaring both in board.yaml is the first step to a board that needs no per-workspace skill: the next change prints the judging steps from the declaration. Checking the observations a record gives against the declaration keeps a closed exit from dropping off the board unsaid, and a mistyped key from losing the previous record's value.

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, view and orient tests pass under TZ=UTC, America/New_York and Asia/Tokyo.
  • New tests: every kind accepted with its default; version 2 without observe; version 1 still read, and refused with the hint when it has the new keys; unknown kind, missing and foreign keys, duplicate keys; each refused shape (packages, repositories, workflows, branches, URLs); empty look; the record's exact set of observations (missing, undeclared, left out entirely, empty and __proto__ names not reported twice, free names with none declared); the record's snapshot; version 2 records read after version 1 ones and drawn; the CLI refusing and then writing a version 2 record.
  • Mutations: 21 applied one at a time (the record's checks and snapshot, the readers' versions, the version 1 path and hint, duplicate keys, each shape rule, the default branch). The first run missed one (uppercase npm names), which the second commit's test catches. After the review, 13 more (the rules the review showed untested, the authority checks, the hint's placement, the non-object input): all caught.
  • By hand with the built CLI in a scratch workspace: measure of a version 2 board reports board_version: 2; record refuses a missing observation and writes nothing, then writes a version 2 record holding the declaration's observations and the stage's look and notes. A version 1 record written by the released 0.67.0 followed by a version 2 record from this build: record prints the diff against it, and basou view's /api/board draws both, with the previous value of an observation not made and the cells that moved.

Review

One round of convergent review (review only), with the decided scope given as fixed. The reviewer read the diff and the readers, ran the board, view and CLI suites, probed about 70 declarations and 12 record inputs through the built package, ran measure, record and view in a scratch workspace, and applied 17 mutations to a copy. No high finding; 2 medium and 5 low, all fixed in the third commit:

  • The comment on observation values promised more than the check (a URL holding $(...) or ; is safe only single-quoted): the comment now states that contract; & stays allowed for query strings.
  • Seven shape rules had no test (branch // and trailing ., the URL's quote characters, a password with no user name, the length limits, the workflow's case): tested now.
  • The version 1 hint was attached to unrelated unknown keys: version 2's keys are reported on a line of their own.
  • Stale comments and record --help (which now names the rule on observed).
  • https:///x and https://@x were taken (the URL parser drops what makes them wrong), and the host check could not fire: the host is now read as written.
  • An owner with _ was refused.
  • An input that is not an object also listed every declared observation as missing.

Not taken up, as design questions rather than defects: whether the digest should cover observe, look and notes (it does not cover stage meanings either), the numeric delta board_version: 1 -> 2 in a diff, an npm name rule stricter than npm's, and control characters in how, look and notes (escaped wherever they are printed).

Checklist

  • Branch is rebased on main.
  • One commit per topic (the second commit adds a test case a mutation showed was missing).
  • 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 keys, not paths).

🤖 Generated with Claude Code

takashi-matsuyama and others added 3 commits October 9, 2026 01:51
…ged by

A board.yaml of board_version 2 may list, under observe, what a judge
observes outside basou, each under the key its record holds it by, of
seven kinds (npm_version, github_release, github_open_issues,
github_open_prs, github_ci, page_version, manual), and may give each
stage look and notes. basou makes none of these observations and sends
nothing. Each value is held to a shape a command can be built from: a
lowercase npm package name, owner/name, a workflow file name, a branch
name that cannot start an option, an https:// URL with no credentials,
quotes or whitespace.

A version 1 board is read as before; one that has these keys is told
that version 2 reads them. A board that declares observations is
recorded with exactly those, one not made as null with its error, so a
closed exit is never dropped unsaid and a mistyped key never loses the
previous record's value.

Records move to record_version 2, which holds the observations and the
stages' look and notes in the declaration's snapshot. Version 1 records
are still read, compared with and drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Say what an observation's value is safe as: one single-quoted shell
  word; a manual observation's how is never a word of a command.
- Read a page's host as written: refuse https:///x, and any '@' before
  the host (https://@x, https://:pw@x), which the URL parser drops.
- Take '_' in a GitHub owner, as a managed user's handle has.
- Tell a version 1 file the keys version 2 reads on a line of their own,
  apart from keys no version reads.
- Check the observations of a record input only when it is an object.
- Test every shape rule the change states (branch '//' and trailing '.',
  the URL's quote characters, length limits, the workflow's case), and
  bring the comments and record's help up to date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takashi-matsuyama
takashi-matsuyama merged commit e3fd3fb into main Oct 9, 2026
8 of 9 checks passed
@takashi-matsuyama
takashi-matsuyama deleted the feat/board-declaration-v2 branch October 9, 2026 00:17
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