v0.16.0 — the polyglot learner's release
Tagged from main, 2026-08-04.
Three evidence-led audit loops against the served v0.15.0 build, then a round of
defects reported directly from the running app by UD. Everything below was
reproduced on the real surface before it was changed, and re-verified afterwards
at the same viewport, register, fixture and flow.
The headline: a language focus no longer empties the screen
Home is written in one language. Nothing written in another language has an
import route to it. On this repository, focusing Rust drew 0 of 5 boxes
on a 1396×509 canvas, and the guidance chip said "No Home is chosen" while the
header two rows above said "Home codemble.cli".
This affected 5 of the 7 languages shipped here, and every polyglot project
the tool exists to chart. Two independent causes:
| Tab | Cause |
|---|---|
| Architecture | The unrouted shelf folds boxes away to keep the connected core readable. useState runs its initialiser once: the component mounted unfocused (132 unreachable → collapsed), the focus then cut the set to 5, and the collapse stuck. Folding is only ever a trade made for a connected core; with no core to protect it simply blanks the drawing. |
| Workflow | The empty-state guard only caught a missing root. A focus that filters out every row of a tree whose root still exists fell straight through it and rendered an empty canvas — no message, no way back. |
Both tabs now name the cause and offer Show all languages. The Architecture
fix is a derived guard rather than a second piece of state, so it cannot go
stale the same way the first one did.
What the project is
The star chart was reachable from every level and opened onto a 130-row concept
inventory. It said nothing about the project it belonged to — and neither did
anything else in the app. Every surface answered a question about something
the learner had already found.
projectOverview answers the first question instead: how many files and
structures, which languages and how much of each, where it starts, the biggest
files, the ones called from the most places, how much is proved, what fell
outside the graph. Parser truth end to end — no API key, and it cannot disagree
with the galaxy or the map.
It is held to the app's own rules: a project with no Home reports none rather
than inventing one, and the ranking says "called from the most places" rather
than "most important", because centrality counts the distinct structures that
call in and that is all it can claim.
Panels that hid their own content
macOS draws no scrollbar until you scroll one, so an overflowing panel is
pixel-identical to a finished one. Measured at rest, before the fix:
| Surface | Depth | What was hidden |
|---|---|---|
| Study panel | 7.8 viewports at 1440×900, 19.9 at 320×640 | impact widget, connections, real source, lens notes — 9–10 headings |
| Checks panel | up to 2.1 viewports | 0 of 4 answer options at 1280×720, 375×720 and 320×640 |
| Star chart | 14.6 viewports | 130 concept rows |
| Find palette | 4.8 viewports | search results |
| Modules sidebar | 11.4 viewports | the module index |
All five take the Map's own scroll-shadow treatment, which the Map already
carried with a comment describing this exact failure. The last two matter most:
they are how a learner reaches a module the thinned galaxy does not show.
Rather than fix a sixth address later, check_panel_reach.mjs now sweeps —
it opens every surface a learner can open and asserts nothing scrolls without a
cue, naming the offender and its depth. Proven in both directions.
The quiz
At 320×640 the sticky submit was drawn across 64% of the question and no
answers were visible at all. The cause was two compounding decisions: position: sticky had been set on .check-primary, which is this app's shared
primary-button class (ten buttons across six components, only one inside a
scrolling panel), and a 191px sticky button inside a 627px option row floats
across content rather than covering it.
The bar sticks now, not the button, and the quiz opens at the question rather
than at its own masthead.
| Viewport | Options visible before | After | Question covered before → after |
|---|---|---|---|
| 1440×900 | 3 / 4 | 4 / 4 | 0% → 0% |
| 1280×720 | 0 / 4 | 4 / 4 | 0% → 0% |
| 375×720 | 0 / 4 | 4 / 4 | 0% → 0% |
| 320×640 | 0 / 4 | 3 / 4 | 64% → 0% |
The exits
- The study panel had no way to close it. Its only exit was "Back to the
module" in the header rail, while the checks panel — which opens in the same
slot — has carried a Close since it shipped. - There was no way back to Home. Home was named in the breadcrumb and
clickable nowhere. The only control carrying the word was Change Home,
which redefines which module Home is — so the one thing that looked like the
way back would have changed the learner's starting point instead.
Smaller, and each measured
- Text merged into text on the Workflow tab. A row drew its meta as a second
<text>offset bydx={label.length * 0.62}em.emresolves against the
meta's own 11px while the mono label advances at 13px — a systematic ~15%
undershoot, so every row printed "— possible call" through the tail of the
name it described. Two<tspan>s flow correctly; measured, every row carrying
meta now abuts at 0px. - Clearing progress left the Map lit. The server was right — both APIs
reported zero understood — while the diagram still drewcodemble.clias
understood in its own accessible name. Third instance of a shape this project
has recorded twice: an invalidation rule stated in a comment and applied at
only some of its callers. - The first-run choice did not look answerable. Both audience-gate options
computed toborder-width: 0; background: transparent. - Enter destroyed the tutorial.
showModal()focuses the first focusable
descendant, and Skip is first in source order. - Connection and impact rows read as one run-on string to a screen reader
and to the clipboard.
Withdrawn on measurement
Recorded so they are not re-investigated:
- The galaxy sky is not washed out.
--cm-skyis#131f4bby design since
the 2026-08-02 "too dark" decision, and the centre glow is the approved
nebula. AreadPixelssample that appeared to contradict the screenshot
returned all zeros because the drawing buffer is not preserved after
compositing — an invalid measurement, not evidence. - Focus after the coach marks is correct. It lands on
.map-stage[tabindex="-1"], the stage the coach has just described. - "Called by 0" beside "Two other files bring it in" are both right and
measure different relations; the wording that separates them is a recorded
decision.
Deliberately not done
Stated rather than implied. This release adds the project summary that was
missing and heads the concept inventory that follows it, but it is not the
full redesign of the star chart, and "Easy mode is still hard" is only partly
served by it. Both want their own pass with their own evidence.
Verification
- 428 pytest · Ruff clean · 17 frontend contract checks
check_space_budgetgreen at 8 widths plus 4 wide widths × 2 levelscheck_escape_surfacesgreen — 84 assertions across 4 widthscheck_panel_reach— new gate, fails on every number quoted above before the
fix; its sweep proven to fail in both directions- Zero console errors or warnings across the full journey; every request 200
- Bundle rebuilt and committed