feat(explorer): rebuild the transaction and address asset panels - #135
Open
bitcoinuniverseadmin wants to merge 5 commits into
Open
bitcoinuniverseadmin wants to merge 5 commits into
bitcoinuniverseadmin wants to merge 5 commits into
Conversation
WP01 to WP06 of the asset summary UI plan, whose eight source markers are resolved and removed. The backend repairs these depend on land alongside. A shared asset-summary directory holds what the two panels must not render two different ways: the quantity presenter, the layout mixins, and the helper that makes their real templates renderable under the unit runner. The presenter never constructs a Number from a quantity, keeps an exact decimal, true digits of an unknown scale and an unknown quantity as three separate cases, and gives a long value an explicitly marked short form while copy always yields the exact string. A very small nonzero amount becomes an exponent rather than zero. The decoder validates the coverage enum instead of casting it, rejects duplicate protocol coverage and coverage from another context, requires the asset and coverage arrays rather than reading an absent one as an empty inventory, and refuses a stated total that conclusive coverage does not support or that disagrees with the identities listed. Effect evidence and the reading's checkpoint are preserved, so "not accepted" can be told from "not yet ruled on" and an absent checkpoint stays absent. The transaction component builds one display model per response instead of mapping inside template bindings, seeds the optional protocol registry so combineLatest cannot hold a proven amount behind a display name, states each coverage gap with its own state and reason instead of counting things being waited on, offers a retry only where one could change the answer, honours the payload's cooldown, and keys a failed logo by its artwork revision. Both templates keep a real table with a row header and put flex inside the header cell rather than on it, bound their numeric columns through an explicit colgroup, and stack by the panel's own width through a container query rather than by the viewport. Each row has a 44px disclosure carrying the full identity, the exact values and its own protocol effects, and every copy control reports what actually happened. The address panel keys holdings by the whole identity, carries divisibility and ruleset, resolves a divisibility conflict to unknown, separates a known quantity from the scope it was read over, states a checked and a total denominator, distinguishes a proven empty address from one that could not be fully checked, and routes its outpoint links through the active network. A bare /outpoint route resolves against the root network, so on signet those links pointed at mainnet. Two visual-QA fixtures were missing rather than wrong. The router falls back to a prefix match, so the summary endpoint was served the detailed flow payload and every transaction and address screenshot reviewed the panel's error state instead of the panel; the outpoint batch answered every request with an empty list. Frontend: 2491 unit tests pass, including twelve rendered-template cases for the transaction panel and twelve for the address panel. Lint reports no errors, the production build compiles both templates, and the text gate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… fixture The screenshot gate reported fourteen console errors on the transaction and address routes, and the errors said only "ERROR yt": a production build's minified constructor name, logged because msg.text() abbreviates a logged object rather than printing it. The gate now resolves the argument handles in the page, so a recorded error carries its real message. With that visible, the cause was not the pages. The application registers a service worker, and a request a service worker answers never reaches context.route, so the worker returned its cached app shell for API requests and the page parsed index.html as JSON. Every fixture this run declares was being bypassed for any request the worker answered, which means these screenshots were not driven by the fixtures they claim to assert. capture.mjs has always passed serviceWorkers: 'block'; this run did not. Three fixtures were also missing outright and fell through to the static server: the asset summary endpoint, which the router's prefix fallback answered with the detailed flow payload, so every transaction and address screenshot reviewed the asset panel's error state rather than the panel; the mining pools windows other than 1w; and the historical price the address page reads. The outpoint batch answered every request with an empty list, so the address holdings panel was only ever reviewed with nothing to show. The gate now reports GO: 24 screenshots, no automatic failures, no accessibility failures, no console errors, no unmatched fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… returned
`live-evidence/mainnet-summary-20260919.json` is the verbatim body of
GET /api/v1/universe/transactions/{txid}/assets from the production explorer on
2026-09-19, for a confirmed mainnet transaction. It is read-only public data,
pinned because a fixture written by the same hand as the decoder proves less than
the real thing.
It pins two things. The deployed producer does not send the per-asset evidence or
effect evidence the rebuilt decoder reads, so the decoder has to accept their
absence or the first deploy breaks the live site; that is now asserted. And the
deployed producer states a checkpoint height of zero beside the real tip block
hash, and reports all thirty one roster protocols as having no reader at all
including the six that two configured adapters do read. Both are the defects the
backend branch repairs, and both are asserted here so that shipping the repaired
producer fails this test loudly instead of passing on stale expectations.
The panel rendering that payload says Partial and "0 assets found so far", never
that there are none, states every unread protocol as a limit rather than as
something being waited on, and offers no retry for a gap no retry could clear.
Also adds a fixture-parity spec: the visual-QA summary fixture must decode under
the same decoder the application uses, and the detailed flow payload must keep
failing to, since that is what the router's prefix fallback was serving.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Dogecoin and Zcash transaction routes read the asset summary and had no fixture for it, so the request failed closed. It went unnoticed because other missing fixtures on the same pages raised JSON parse failures first and the pages never rendered far enough to ask. Repairing those, in the previous commit, is what surfaced this. Both are deliberately inconclusive: one protocol answered and the rest of the chain's roster has no transaction reader, so a total would contradict its own coverage and the decoder rejects that. The Zcash one states no divisibility, so the panel renders the true digits labelled as smallest units rather than a scaled figure, which is the case that was previously impossible to review. The fixture-parity spec now covers them, and asserts that neither carries the fabricated zero-height checkpoint the deployed producer sends. Co-Authored-By: Claude Opus 5 <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.
Implements WP01 to WP06 of the asset summary UI plan pinned at
prep/asset-summary-ui-20260919. All eight frontend source markers are resolved and removed. The backend repairs these depend on are in bitcoinuniverseio/backend-apis#204.What changed
A shared
asset-summarydirectory holds what the transaction and address panels must not render two different ways: the quantity presenter, the layout mixins, and the helper that makes their real templates renderable under the unit runner. The presenter never constructs aNumberfrom a quantity, keeps an exact decimal, true digits of an unknown scale, and an unknown quantity as three separate cases, and marks a shortened headline as approximate while copy always yields the exact string. A very small nonzero amount becomes an exponent rather than zero.The decoder validates the coverage enum instead of casting it, rejects duplicate protocol coverage and coverage from another context, requires the asset and coverage arrays rather than reading an absent one as an empty inventory, and refuses a stated total that conclusive coverage does not support or that disagrees with the identities listed. Effect evidence and the reading's checkpoint are preserved, so "not accepted" can be told from "not yet ruled on", and an absent checkpoint stays absent.
The transaction component builds one display model per response instead of mapping inside template bindings, seeds the optional protocol registry so
combineLatestcannot hold a proven amount behind a display name, states each coverage gap with its own state and reason, offers a retry only where one could change the answer, honours the payload's cooldown, and keys a failed logo by its artwork revision.Both templates keep a real table with a row header and put flex inside the header cell rather than on it, bound their numeric columns through an explicit colgroup, and stack by the panel's own width through a container query rather than by the viewport. Each row has a 44px disclosure carrying the full identity, the exact values and its own protocol effects.
The address panel keys holdings by the whole identity, carries divisibility and ruleset, resolves a divisibility conflict to unknown, separates a known quantity from the scope it was read over, states a checked and a total denominator, distinguishes a proven empty address from one that could not be fully checked, and routes its outpoint links through the active network. A bare
/outpointroute resolves against the root network, so on signet those links pointed at mainnet.Visual QA harness
The gate reported fourteen console errors on exactly these two routes, saying only
ERROR yt. The gate now resolves console argument handles, which showed the cause: the app registers a service worker, and a request a service worker answers never reachescontext.route, so the worker served its cached app shell for API requests and the page parsedindex.htmlas JSON. Every fixture the run declares was being bypassed.capture.mjshas always passedserviceWorkers: 'block'; this run did not.Three fixtures were also missing and fell through to the static server: the asset summary endpoint, which the router's prefix fallback answered with the detailed flow payload, so every transaction and address screenshot reviewed the panel's error state rather than the panel; the mining pools windows other than
1w; and the historical price the address page reads. The outpoint batch answered every request with an empty list.Verification
npx vitest run: 216 files, 2491 tests pass, including 12 rendered-template cases for the transaction panel and 12 for the address panel.npm run lint: 0 errors.npm run build:universe: succeeds, which compiles both templates ahead of time.node scripts/universe/check-text.mjs: passes.npm run checkscreenshots:changed: GO. 24 screenshots, 0 automatic failures, 0 accessibility failures, 0 console errors, 0 unmatched fixtures.Live functional acceptance on a first-party test network and the public mainnet release are separate operational steps and are not claimed here.
🤖 Generated with Claude Code