Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 671e275a97 | |||
| a1ef1fb555 | |||
| 9a154950eb | |||
| b57a1ea286 |
4 changed files with 208 additions and 14 deletions
75
CHANGELOG.md
75
CHANGELOG.md
|
|
@ -5,6 +5,81 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **README's selftest numbers were re-measured, not re-derived.** The badge said
|
||||
`selftest_checks-868` and the five `## Development` comments said
|
||||
220/360/73/99/40 = 792 - two different wrong sums on the same public surface,
|
||||
neither matching the other. The five suites were run under `/bin/bash` (3.2)
|
||||
on `b57a1ea` and reported `coord 257`, `board 393`, `route 73`, `orders 116`,
|
||||
`state-line-guard 54` = **893**, 0 failed in every summary. 0.33.0's entry
|
||||
below records 257+368+73+116+54 = 868 and was true when written; 0.33.1 added
|
||||
25 board checks (368 -> 393) without re-summing, and the comment block had been
|
||||
stale far longer. No code, no version bump: the number furthest from the meter
|
||||
rots first, and only the number moved.
|
||||
|
||||
## [0.33.1] - 2026-09-04
|
||||
|
||||
### Added
|
||||
|
||||
- **A git repo nested under a depth-1 repo now enters the board, on a STATE.md
|
||||
and nothing wider.** Discovery adds a depth-1 repo and stops, and the
|
||||
else-branch container scan - the only place children are ever looked at - is
|
||||
unreachable for an entry that is itself a repo. Measured before anything was
|
||||
written: **12 nested repos across the real tree, exactly 1 with a STATE.md**
|
||||
(`from-ai-to-chitta/content-sadhguru`, which had been running work and
|
||||
reporting to nobody). The other 11 are vendored or experimental checkouts and
|
||||
stay invisible on purpose - they do not even reach the `UTEN STATE.md` bucket.
|
||||
`add_nested_repos()` sits beside `add_dot_repos()` rather than widening the
|
||||
`*` loops, for the same reason ordre `20260818T124828Z` gave for dot repos:
|
||||
routing a depth-1 repo into the container branch would admit every nested
|
||||
checkout. One level only; depth 3 is pinned as NOT admitted. A dot-prefixed
|
||||
depth-1 repo gets the same nested scan, since nothing in the criterion
|
||||
distinguishes it.
|
||||
|
||||
A nested repo carries **two names**, and conflating them would have put a
|
||||
fabricated 0 in three columns. The board KEY is `<parent>/<child>`; the
|
||||
MAILBOX name is `basename(git toplevel)`, so `$COORD/<parent>/<child>/inbox`
|
||||
finds no directory and INN/ORDRE/FLY would read 0 for a repo that may have
|
||||
mail. The record loop carries `mbox` beside `name`, and `--voyage`'s order
|
||||
lookup takes `basename($vy_dir)`. Which directories are nested is RECORDED by
|
||||
discovery (`NESTED_LIST`), never re-derived from "is my parent a repo?".
|
||||
|
||||
- **The scan reports its own denominator.** `undersoekt: N katalog(er) depth 1,
|
||||
M polyrepo-container(e), K nestede repo (J med STATE.md tatt med).` The header
|
||||
count answered how many repos were found and nothing about how many were
|
||||
looked at, so a criterion excluding 11 of 12 nested repos was invisible on the
|
||||
surface built to show it. Real tree 2026-09-04: `43 / 5 / 12 (1 tatt med)`. A
|
||||
non-repo dot-directory counts in N and never in M - the line is a denominator,
|
||||
not a partition.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The denominator line's own wording broke an existing check: `polyrepo
|
||||
container itself is not listed as a repo` grepped the whole output for
|
||||
`polyrepo` and matched the new footer's `polyrepo-container(e)` - the same
|
||||
class as a grep reading a comment that EXPLAINS a pattern as an instance of
|
||||
it. Now anchored at column 1, which is what it always meant.
|
||||
|
||||
### Known gaps, stated rather than closed
|
||||
|
||||
- The mailbox-keyed JOINs (`$OWED`, `brief_orphans`, `--inbox-plan`) still key
|
||||
on the board name, so a nested repo owing a reply gets no debt tab and is
|
||||
listed under `UTENFOR REPO-SKANNEN`. Neither created nor worsened here: before
|
||||
this change the repo was absent from `RECORDS` entirely, so both readings were
|
||||
already exactly as wrong.
|
||||
- A 34-character key overflows the table's `%-32s` REPO column, shifting that
|
||||
row two characters right. Same class as `Fable 5.1/xhigh` in KOST; parsing is
|
||||
unaffected, and widening the column moves three `cut -c89-` selftest helpers.
|
||||
- A dot-prefixed NESTED repo is not looked for. The combination was neither
|
||||
measured nor ordered.
|
||||
|
||||
### Testing
|
||||
|
||||
- `board-selftest.sh` 368 -> 393 checks.
|
||||
|
||||
## [0.33.0] - 2026-09-04
|
||||
|
||||
### Added
|
||||
|
|
|
|||
32
CLAUDE.md
32
CLAUDE.md
|
|
@ -1464,6 +1464,36 @@ obligations in another repo.
|
|||
and re-measured after: 11 of 11 now match. Only the Install block was
|
||||
converted; converting the other 39 indented blocks was not ordered and
|
||||
would bury a two-line fix in a whole-file diff.
|
||||
- **README's selftest numbers are pinned by `tests/selftest.test.mjs`, and the
|
||||
check lives there rather than in one of the five bash suites for a measured
|
||||
reason.** The badge and the five `## Development` comments rotted twice in a
|
||||
row - 529 carried from 0.25.0, then a badge saying 868 beside comments summing
|
||||
to 792, two different wrong sums of the same fact on the same screen - because
|
||||
nothing compared them to anything. The order behind the fix
|
||||
(`20260905T053602Z-6743615726`) suggested hosting it in whichever suite
|
||||
already pins README/catalog invariants; measured before choosing, **no such
|
||||
suite exists** (`grep -ln README scripts/*selftest*.sh` returns
|
||||
`board-selftest.sh` alone, on two incidental hits - a prose comment and a
|
||||
`research/README.md` fixture). This wrapper is the only place where all five
|
||||
numbers exist at once in a run that already happens: `runSuite()` captures
|
||||
each suite's own summary line, so the truth source is the line the suite
|
||||
prints, never a re-count. A check inside one suite could see its own total but
|
||||
would have to RE-RUN the other four (**212s sequentially**, measured
|
||||
2026-09-05 under `/bin/bash` 3.2) to see theirs, and grepping `check` calls out
|
||||
of the scripts is both the second copy of the counting and a wrong one, since
|
||||
those calls sit inside loops. The badge is compared against the MEASURED sum,
|
||||
not against the five README comments - a badge agreeing with five stale
|
||||
comments is the 868-beside-792 shape one layer down - and a suite that stops
|
||||
printing a summary FAILS the check rather than being skipped, because an
|
||||
absent measurement must not read as a matching one. It adds no bash check, so
|
||||
the five counts and the 893 badge are unchanged by its arrival.
|
||||
Mutation-verified in both directions: `73 -> 74` on the route comment gives
|
||||
"README says route-selftest has 74 checks; it reported 73", `893 -> 894` on
|
||||
the badge gives "README's badge says 894 selftest checks; the five suites
|
||||
reported 893", and the restored file is 12/12 green. **Bounded gap, stated
|
||||
rather than closed:** `CLAUDE.md`'s own copies of the five counts (in this
|
||||
section, above) are NOT checked - a second surface carrying the same numbers,
|
||||
measured and left alone because widening the check to it was not ordered.
|
||||
- Conventional Commits: `type(scope): description`.
|
||||
|
||||
## Commands
|
||||
|
|
@ -1471,7 +1501,7 @@ obligations in another repo.
|
|||
- Test: `bash scripts/coord-selftest.sh`, `bash scripts/board-selftest.sh`,
|
||||
`bash scripts/route-selftest.sh`, `bash scripts/orders-selftest.sh` and
|
||||
`bash scripts/state-line-guard-selftest.sh` (or `npm test`, the Node wrapper
|
||||
around all five plus the hook tests)
|
||||
around all five plus the hook tests and the README-number check)
|
||||
- Order queue smoke test: `CLAUDE_COORD_DIR=$(mktemp -d) bash
|
||||
scripts/coord-order-send.sh --to smoke --from tester --subject s --message m`
|
||||
then `CLAUDE_COORD_DIR=<same> bash scripts/coord-order-inbox.sh --repo smoke`
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -14,7 +14,7 @@ Session A in repo X leaves a message for repo Y; the next session in repo Y gets
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ It makes **zero model calls**, which is the point rather than a detail. Under su
|
|||
|
||||
**`board.sh --voyage` reports the Voyage briefs in flight**, a sixth rendering of the same scan. `board` reads STATE lines, which say nothing about a brief, so a programme running Voyage across several repos had no shared surface: nobody could answer which briefs were running, in what phase, and who was waiting on whom. Detection is by **property, never by directory name** — a directory holding `brief.md` or `brief.md.draft` under any of the three planning locations the convention recognises (`.claude/projects/`, `docs/`, `features/<n>-<name>/`) — and it walks the **filesystem, never the git index**: a repo that gitignores `.claude/projects/` would otherwise report zero briefs while actually running one. The phase ladder measures *artifacts*, not sessions: a plan executed in a single session leaves no file behind, so `plan` is the last thing the filesystem can prove, and nothing here claims a session is alive. `brief_quality` is read out of the brief's frontmatter and an absent field reads `-`, never `complete`; a research directory that exists and holds nothing reads `0`, distinct from the `-` that means no research step was ever started. The table carries a matching `VOY` column beside `ORDRE` and `FLY` — the same class of durable filesystem fact, and never summed with them.
|
||||
|
||||
**`board.sh --plan` renders the day plan**, a *third* rendering of that same scan and the only one that takes a position: which repos to open a tab for today, in what order, and the command to start each. The order is the position, and there is no cutoff — nothing is hidden, and one deterministic score decides it: `40 ×` repos released transitively, `15 ×` unhandled inbox messages, plus small bonuses for live work and for a cheap `next-cost` row. Four hard buckets preceded it and could not express "this repo owes one message and releases two others" — which is how a blocked chain's root ended up ranked *below* the repos waiting on it. **Chain-root credit** follows `blocked-on` transitively to the first repo that is not itself blocked and credits only that root: opening a blocked repo releases nobody, since its own next step is by definition waiting. A cycle, or a `blocked-on` naming a repo the scan never produced, credits nobody rather than inventing a root — a plan that looks correct while sending you to the wrong repo is worse than one that says nothing. Repos owing mail still rank high *whatever their status*, and debt is deliberately **uncapped**: excluding `blocked` or `done` is a statement about a repo's own next step, which cannot be moved, while owing an answer is a different axis and answering is often what unblocks it. Repos with no board line come last and labelled — the table already prints a note about those, so a plan that dropped them silently would repeat exactly that defect. `why=` names the dominant term, so a block reads `unblocks:2` rather than the `inbox:N` every block used to repeat. Still zero model calls, still read-only, and still cross-checked against `coord-count.sh`.
|
||||
**`board.sh --plan` renders the day plan**, a *third* rendering of that same scan and the only one that takes a position: which repos to open a tab for today, in what order, and the command to start each. The order is the position, and there is no cutoff — nothing is hidden, and **five ordered groups** decide it, each a lookup over a field the scan already read rather than a weighted score: (1) **chain-root credit**, most repos released first; (2) **debt**, most-owed-first, whatever the status; (3) `planned`; (4) `in-progress`; (5) `?`/`MALFORMED` — undeclared, last and labelled. Within a group, ties break on a cheap Sonnet `next-cost` row, then oldest plan first. A 0.19.0 weighted score (`40 ×` repos released, `15 ×` unhandled inbox messages) briefly stood in this spot and *could* express "this repo owes one message and releases two others" as a single number — but re-tuning those two coefficients would have silently reordered a parser living in another repo, with no test here able to hold a ranking stable for a consumer it can't see; the operator replaced it with the group order in 0.20.0 for that reason. Groups 3 and 4 are `planned` above `in-progress`, inverted from every earlier version by the same decision: turning a decision into motion is the slow step, live work is already moving. **Chain-root credit** follows `blocked-on` transitively to the first repo that is not itself blocked and credits only that root: opening a blocked repo releases nobody, since its own next step is by definition waiting. A cycle, or a `blocked-on` naming a repo the scan never produced, credits nobody rather than inventing a root — a plan that looks correct while sending you to the wrong repo is worse than one that says nothing. Repos owing mail still rank high *whatever their status*, and debt is deliberately **uncapped**: excluding `blocked` or `done` is a statement about a repo's own next step, which cannot be moved, while owing an answer is a different axis and answering is often what unblocks it. Repos with no board line come last and labelled — the table already prints a note about those, so a plan that dropped them silently would repeat exactly that defect. `why=` names the group that placed the repo, so a block reads `unblocks:2` rather than the `inbox:N` every block used to repeat. Still zero model calls, still read-only, and still cross-checked against `coord-count.sh`.
|
||||
|
||||
**`--focus "<prose>"` narrows that plan to one subject, and reports what it held back.** It is the only cutoff this format has, which is why the report is a condition of the feature rather than a refinement of it: `--plan` documents that it hides nothing and labels what it cannot rank, so a silent filter would break a property already written down. The same run prints the subjects the prose resolved to, how many blocks were removed, how many `STATE.md` were searched, and — named, not counted — the repos that *mention* a resolved subject without declaring a marker line. That last class is enumerated because it is where the misses live: a repo can be a heavy participant and never have written a marker, and no string measurement finds it until the held-back population is listed. Each surviving block carries the declaration it survived on. Prose matching nothing prints the *full* plan and says so, since the phrase arrives verbatim from a person and a typo must not empty the day. The subject vocabulary is read from the scanned `STATE.md` themselves, so the "reads `STATE.md` and no other file" invariant is untouched.
|
||||
|
||||
|
|
@ -195,12 +195,12 @@ Note that raising the inbox's priority (Rule 7) deliberately does **not** widen
|
|||
|
||||
## Development
|
||||
|
||||
bash scripts/coord-selftest.sh # 220 checks against a throwaway mailbox
|
||||
bash scripts/board-selftest.sh # 360 checks against a throwaway repo tree
|
||||
bash scripts/coord-selftest.sh # 257 checks against a throwaway mailbox
|
||||
bash scripts/board-selftest.sh # 393 checks against a throwaway repo tree
|
||||
bash scripts/route-selftest.sh # 73 checks, incl. the route->board round trip
|
||||
bash scripts/orders-selftest.sh # 99 checks, incl. the 20-way barriered claim race
|
||||
bash scripts/state-line-guard-selftest.sh # 40 checks, incl. the Edit replace_all projection and the ratchet
|
||||
npm test # all five selftests plus the hook tests, via node --test
|
||||
bash scripts/orders-selftest.sh # 116 checks, incl. the 20-way barriered claim race
|
||||
bash scripts/state-line-guard-selftest.sh # 54 checks, incl. the Edit replace_all projection and the ratchet
|
||||
npm test # all five selftests, the hook tests, and the README-number check
|
||||
|
||||
TDD is the house rule: every behavior change lands with a failing selftest check first.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import { test } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { basename, dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
|
@ -12,8 +12,31 @@ import { fileURLToPath } from 'node:url';
|
|||
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const hook = join(root, 'hooks', 'scripts', 'session-start.mjs');
|
||||
|
||||
// Every bash suite already prints its own total on its last line, and this
|
||||
// wrapper already runs all five. Capturing that line here is what makes the
|
||||
// README's numbers testable without a SECOND copy of the counting: nothing
|
||||
// re-counts `check` calls (loops make that undecidable anyway) and nothing
|
||||
// re-runs a suite to read a number the run in progress is already printing.
|
||||
// The five suites cost 212s sequentially, measured 2026-09-05 under /bin/bash
|
||||
// 3.2 - the marginal cost of the README check is zero because it consumes a
|
||||
// run that happens regardless.
|
||||
const summaries = new Map();
|
||||
|
||||
function runSuite(name) {
|
||||
const script = join(root, 'scripts', `${name}-selftest.sh`);
|
||||
try {
|
||||
summaries.set(name, execFileSync('bash', [script], { encoding: 'utf8' }));
|
||||
} catch (err) {
|
||||
// Record what the suite managed to print before failing, then let the
|
||||
// failure through: a red suite must stay red here, and the README check
|
||||
// below still gets a number to compare rather than a silent absence.
|
||||
if (typeof err.stdout === 'string') summaries.set(name, err.stdout);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
test('coord bash selftest passes', () => {
|
||||
execFileSync('bash', [join(root, 'scripts', 'coord-selftest.sh')], { encoding: 'utf8' });
|
||||
runSuite('coord');
|
||||
});
|
||||
|
||||
// board.sh reads this plugin's mailbox for its INN column, so the board ships
|
||||
|
|
@ -22,7 +45,7 @@ test('coord bash selftest passes', () => {
|
|||
// through CLAUDE_PLUGIN_ROOT, so a board.sh that exists only in
|
||||
// ~/.claude/scripts/ would be missing on exactly the path production uses.
|
||||
test('board bash selftest passes', () => {
|
||||
execFileSync('bash', [join(root, 'scripts', 'board-selftest.sh')], { encoding: 'utf8' });
|
||||
runSuite('board');
|
||||
});
|
||||
|
||||
// route.sh is the WRITER for the next-cost field board.sh already reads, so its
|
||||
|
|
@ -31,7 +54,7 @@ test('board bash selftest passes', () => {
|
|||
// CLAUDE_PLUGIN_ROOT, and a calculator proven only elsewhere is unproven on the
|
||||
// one path production uses.
|
||||
test('route bash selftest passes', () => {
|
||||
execFileSync('bash', [join(root, 'scripts', 'route-selftest.sh')], { encoding: 'utf8' });
|
||||
runSuite('route');
|
||||
});
|
||||
|
||||
// pre-state-line-guard.mjs is a PreToolUse hook, so like session-start.mjs it
|
||||
|
|
@ -39,7 +62,7 @@ test('route bash selftest passes', () => {
|
|||
// CLAUDE_PLUGIN_ROOT, and a guard proven only elsewhere is unproven on the
|
||||
// path production actually runs.
|
||||
test('state-line-guard bash selftest passes', () => {
|
||||
execFileSync('bash', [join(root, 'scripts', 'state-line-guard-selftest.sh')], { encoding: 'utf8' });
|
||||
runSuite('state-line-guard');
|
||||
});
|
||||
|
||||
// The order queue is the second channel beside the mailbox, with the opposite
|
||||
|
|
@ -48,7 +71,7 @@ test('state-line-guard bash selftest passes', () => {
|
|||
// CLAUDE_PLUGIN_ROOT, so a queue proven only elsewhere is unproven where it
|
||||
// runs.
|
||||
test('orders bash selftest passes', () => {
|
||||
execFileSync('bash', [join(root, 'scripts', 'orders-selftest.sh')], { encoding: 'utf8' });
|
||||
runSuite('orders');
|
||||
});
|
||||
|
||||
// The engine refuses to invent an identity from the cwd, but the hook is the
|
||||
|
|
@ -188,3 +211,69 @@ test('hook keeps mail and orders in separate blocks, mail first', () => {
|
|||
assert.ok(ctx.includes('UNTRUSTED DATA'), 'the mail block lost its authorization framing');
|
||||
assert.ok(ctx.includes('OPERATOR-AUTHORIZED'), 'the order block lost its authorization framing');
|
||||
});
|
||||
|
||||
// --- README's selftest numbers must rot loudly ------------------------------
|
||||
//
|
||||
// The badge and the five `## Development` comments are the only public claim
|
||||
// about how much this engine is pinned by, and they are the number furthest
|
||||
// from the meter: they rotted twice in a row (529 from 0.25.0; then a badge
|
||||
// saying 868 beside comments summing to 792 - two different wrong sums of the
|
||||
// same fact, neither matching the other, on the same screen). Nothing caught
|
||||
// either, because nothing compared them to anything.
|
||||
//
|
||||
// It lives HERE rather than in one of the five bash suites, and the choice is
|
||||
// not arbitrary. The order's parenthetical suggested the suite that already
|
||||
// pins README/catalog invariants; measured before choosing, no such suite
|
||||
// exists - `grep -ln README scripts/*selftest*.sh` returns board-selftest.sh
|
||||
// alone, on two incidental hits (a prose comment and a `research/README.md`
|
||||
// fixture). Of the places that could host it, this wrapper is the only one
|
||||
// where all five numbers exist at once in a run that already happens: a check
|
||||
// inside a suite could see its own count but would have to RE-RUN the other
|
||||
// four (212s, measured 2026-09-05) to see theirs, and reading counters out of
|
||||
// the scripts is the second copy of the counting this check was asked not to
|
||||
// be. `check` calls sit inside loops, so a static count is not merely a second
|
||||
// copy - it is a wrong one.
|
||||
//
|
||||
// The truth source is each suite's own summary line, verbatim, and a suite
|
||||
// that stops printing one FAILS here rather than being skipped: an absent
|
||||
// measurement must not read as a matching one.
|
||||
function suiteTotal(name) {
|
||||
const out = summaries.get(name);
|
||||
assert.ok(out !== undefined,
|
||||
`${name}-selftest produced no captured output: its total was never measured, ` +
|
||||
'so the README comparison below would be resting on nothing');
|
||||
// Two summary grammars, both already in the tree: coord prints
|
||||
// `PASS=N FAIL=M`, the other four print `<name>-selftest: N passed, M failed`
|
||||
// and orders adds `, S skipped (of T checks)`. README documents the TOTAL
|
||||
// number of checks, so skipped ones count.
|
||||
let m = out.match(/^\S+-selftest: (\d+) passed, (\d+) failed(?:, (\d+) skipped)?/m);
|
||||
if (m) return Number(m[1]) + Number(m[2]) + Number(m[3] ?? 0);
|
||||
m = out.match(/^PASS=(\d+) FAIL=(\d+)/m);
|
||||
assert.ok(m, `${name}-selftest printed no summary line this parser recognises`);
|
||||
return Number(m[1]) + Number(m[2]);
|
||||
}
|
||||
|
||||
test('README states the selftest counts the suites actually reported', () => {
|
||||
const readme = readFileSync(join(root, 'README.md'), 'utf8');
|
||||
const suites = ['coord', 'board', 'route', 'orders', 'state-line-guard'];
|
||||
|
||||
let sum = 0;
|
||||
for (const name of suites) {
|
||||
const measured = suiteTotal(name);
|
||||
sum += measured;
|
||||
const line = readme.match(
|
||||
new RegExp(`^\\s*bash scripts/${name}-selftest\\.sh\\s+#\\s+(\\d+) checks`, 'm'));
|
||||
assert.ok(line,
|
||||
`README's ## Development block has no "N checks" comment for ${name}-selftest.sh`);
|
||||
assert.equal(Number(line[1]), measured,
|
||||
`README says ${name}-selftest has ${line[1]} checks; it reported ${measured}`);
|
||||
}
|
||||
|
||||
// The badge is the sum, and it is compared against the MEASURED total rather
|
||||
// than against the five README comments: a badge agreeing with five stale
|
||||
// comments is exactly the 868-beside-792 shape, one layer down.
|
||||
const badge = readme.match(/badge\/selftest_checks-(\d+)-/);
|
||||
assert.ok(badge, 'README has no selftest_checks badge to check');
|
||||
assert.equal(Number(badge[1]), sum,
|
||||
`README's badge says ${badge[1]} selftest checks; the five suites reported ${sum}`);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue