Graph: the y-axis minimum belongs to the plot, not the time axis - #70
Merged
Conversation
Closes #69. With both `axis` and `timeAxis`, the minimum was written at the bottom row -- which the time axis had already claimed. The two labels ended up side by side with nothing between them, so a chart rendered "$0" and "08-10" as "$008-10". It is visible today in the CoinPay frame on hqtui.com/apps. The minimum marks the bottom of the *plot*, and a time axis takes that row away, so it belongs one row higher whenever one is present. Decided before the labels are written rather than after, because by then the surface has already been subdivided. Fixed in the TypeScript reference and in every port that reimplements the widget: Rust, Go, Python, Zig and C++. The COBOL port renders through the TypeScript library and uses neither option, so it inherits the fix rather than needing one. ## Why no fixture caught it Because none covered the combination. `graph-axis` and `graph-timeaxis` each existed alone, and `timeAxis` appears nowhere in the demo either, so regenerating produced no drift at all. A `graph-axis-timeaxis` scene is added, which is what now holds the five ports to this. The conformance harnesses earned their keep on the way: each refused the new fixture outright rather than silently skipping it, which is the whole point of matching scenes by name. 238 pass under Bun and Node; Rust, Go, Python, Zig and C++ suites green, each comparing 54 widget scenes over 5584 cells. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
ralyodio
added a commit
that referenced
this pull request
Sep 8, 2026
…ministic (#71) * Recapture the CoinPay frame with the axis fix #70 moved the y-axis minimum off the time-axis row. The published screenshot still showed the old collision, since the gallery serves committed images rather than rendering live. "$0" now sits on the plot's bottom row and "08-10" has the time axis to itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy * Recapture nixamp and tsbb, now that both frames are deterministic Regenerating the whole gallery showed these two changing on every run, which I had claimed would not happen without checking. Both were real defects in the fixtures rather than in the capture: nixamp anoisesrc defaults to seed=-1, so the analyser was measuring different noise each time. Seeded upstream. tsbb the view renders ages through relative(), which compares against Date.now(). Pinning the fixture timestamps to a fixed date made the rendered labels drift by an hour every hour. Anchored to capture time upstream instead. Two consecutive captures now produce byte-identical files for all nine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #69.
The bug
With both
axisandtimeAxis, the y-axis minimum was written atheight - 1— the row the time axis had already claimed. The two labels landed side by side with nothing between them:Visible today in the CoinPay frame on hqtui.com/apps.
The minimum marks the bottom of the plot, and a time axis takes that row away, so it belongs one row higher whenever one is present. Decided before the labels are written rather than after, because by then the surface has already been subdivided and the row is no longer addressable.
Fixed everywhere it is implemented
Why no fixture caught it
Because none covered the combination.
graph-axisandgraph-timeaxiseach existed alone, andtimeAxisappears nowhere in the demo either — so regenerating the fixtures against the buggy code produced zero drift. The bug was invisible to the entire conformance system.A
graph-axis-timeaxisscene is added. That is what now holds the five ports to this, and it is the part of this change that matters beyond the one-line fix.The harnesses did earn their keep: each of the five refused the new fixture outright — "no Rust scene for widget fixture", "no Go scene", and so on — rather than silently skipping it. Matching scenes by name is what makes an unwired port loud instead of quiet.
Checks
graph-axis.test.ts. Reverting just the fix fails 2 of them, so they test the behaviour rather than describe it.bun testandnode --test(was 232).cargo test, Gogo test ./..., Pythonunittest, Zigzig build test, C++ctest(9/9) — all green, each comparing 54 widget scenes over 5584 cells.One test-writing note: my first assertion used
/^\s*0*08-10/, which matches the correct output too because0*allows none. It is/\d08-10/now — a digit directly against the label is the actual signature of the fusion.🤖 Generated with Claude Code
https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy