Measured (median of 9 spawns, this machine, 2026-09-11, valid cached session present)
| invocation |
median |
libby help |
26.1 ms |
libby where |
25.7 ms |
libby archive (no --all/--title) |
2596 ms — exits 2 with "Specify what to archive" only AFTER a full session bootstrap |
libby borrow/return/hold/unhold with a missing id sit on the same path.
Root cause
bin/libby.mjs:341-343 runs buildConfig + authenticate for every authed command before the archive branch rejects a missing target (bin/libby.mjs:447-455) and before borrow/return/hold/unhold validate their title id (bin/libby.mjs:351-356).
- Cached-session
authenticate performs a network round trip: verify() = GET /chip/sync (src/auth.mjs:60, src/auth.mjs:150-157) — that round trip is the whole ~2.5 s.
- The unknown-command gate (
bin/libby.mjs:243-244: "a typo must never mint chips or authenticate") already establishes the principle — arg validation belongs behind the same line.
Fix shape (smallest)
Hoist cheap arg validation above buildConfig/authenticate:
archive: require --title <id> or --all (else usage + exit 2)
borrow/return/hold/unhold: require <id> (else usage + exit 2)
Valid invocations behave identically — they still authenticate and sync.
Acceptance
- BEFORE/AFTER spawn table (median of 9, interleaved with
libby help): libby archive (no flags) and libby borrow (no id) within noise of libby help; zero network on the error path.
- Hermetic spawn pins in
test/cli-lazy.test.mjs proving the error path exits 2 with the usage message before any session/auth work (the pin must fail if validation is reordered back behind authenticate).
node --test test/cli-lazy.test.mjs green; full node --test green.
- No changes outside
bin/libby.mjs + test/cli-lazy.test.mjs (+ docs only if a usage pin text is documented there).
Related: #11 (init --help runs the wizard), #12 (evaluate skipping the cached-session verify round trip — the 2.5 s every authed invocation pays even when it does proceed).
Measured (median of 9 spawns, this machine, 2026-09-11, valid cached session present)
libby helplibby wherelibby archive(no --all/--title)libby borrow/return/hold/unholdwith a missing id sit on the same path.Root cause
bin/libby.mjs:341-343runsbuildConfig+authenticatefor every authed command before the archive branch rejects a missing target (bin/libby.mjs:447-455) and before borrow/return/hold/unhold validate their title id (bin/libby.mjs:351-356).authenticateperforms a network round trip:verify()=GET /chip/sync(src/auth.mjs:60,src/auth.mjs:150-157) — that round trip is the whole ~2.5 s.bin/libby.mjs:243-244: "a typo must never mint chips or authenticate") already establishes the principle — arg validation belongs behind the same line.Fix shape (smallest)
Hoist cheap arg validation above
buildConfig/authenticate:archive: require--title <id>or--all(else usage + exit 2)borrow/return/hold/unhold: require<id>(else usage + exit 2)Valid invocations behave identically — they still authenticate and sync.
Acceptance
libby help):libby archive(no flags) andlibby borrow(no id) within noise oflibby help; zero network on the error path.test/cli-lazy.test.mjsproving the error path exits 2 with the usage message before any session/auth work (the pin must fail if validation is reordered back behind authenticate).node --test test/cli-lazy.test.mjsgreen; fullnode --testgreen.bin/libby.mjs+test/cli-lazy.test.mjs(+ docs only if a usage pin text is documented there).Related: #11 (init --help runs the wizard), #12 (evaluate skipping the cached-session verify round trip — the 2.5 s every authed invocation pays even when it does proceed).