The CLI was rendering real lesson titles and "TBD" summaries from
unreleased modules. Now renderModuleDetail returns lessons: [] in JSON
mode and a single "available after unlock" line in human mode when a
module is locked, regardless of what the API returns.
This is a client-side mask; the API still emits the data on the wire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop MIN_MODULE from 1 to 0 so parseLessonRef and parseModuleRef accept
m0/m0l1 (the 10xdevs3 prework module). Lesson numbers remain positive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On Windows, configDir() reads APPDATA, not XDG_CONFIG_HOME. Tests that
only redirected XDG_CONFIG_HOME leaked config state across test files,
causing failures on windows-latest CI. Extract redirectConfigDir/
restoreConfigDir helper that sets both env vars, apply across all 10
test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement v1 signing verification (v1:<keyId>:<hash> canonical string) and
add loadEffectiveKeyset() with localhost-gated env var override for e2e testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement client-side Ed25519 signature verification (Phase 5B) to ensure
lesson bundles haven't been tampered with between the API and CLI.
REQUIRE_SIGNATURES is set to true (fail-closed). The ApiResult type now
exposes responseHeaders so fetchLesson can extract X-Bundle-Signature
and X-Bundle-Key-Id headers for verification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core student commands plus shared supporting modules:
- lesson-ref parser: m<N>l<N>, module range 1..5 per 10xdevs3 shape
- api-content: typed wrappers for catalog/modules/lesson/health
- writer.ts: phase 4 planning stub; phase 5 swaps in real writes
- format.ts: shared formatReleaseAt (long-form English dates, UTC
day-start snapping so "in N days" never drifts by one at midnight)
10x list accepts both '1' and 'm1' to match '10x get m1l1'. Drill-in
hints always include a concrete runnable example, picked from an
unlocked module when available so the suggested command works today.
10x doctor gains a hint field on every check result. Human report
uses Title-case labels (Auth/API/Config/Version/Claude); JSON keeps
stable lowercase names so machine consumers don't break.
Message style guide at the top of src/lib/output.ts codifies:
- never echo API machine codes ("module_locked", "not_found", ...)
as human copy — build the sentence from payload fields
- error = what happened; hint = runnable '10x ...' command
- timestamps always through formatReleaseAt() on stderr; JSON
consumers still get the raw ISO
- module hints show both accepted input forms so students discover
the alternative
55 new tests, 121 total, all green. typecheck + oxlint clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>