universe: publish the pricing token universe as a live-loadable document - #83
Open
kaisbaccour wants to merge 3 commits into
Open
kaisbaccour wants to merge 3 commits into
kaisbaccour wants to merge 3 commits into
Conversation
Listing a token today is seven config edits across six repos and six PAM-gated releases, and every one of them restarts the service. A pricing restart refuses every quote for cold_start_hold_secs (420s) and then needs an oracle bounce, so a listing costs real downtime in market hours. The universe -- which (chain, symbol) slots a plane serves -- is pure enumeration: no prices, no spreads, no keys. So it is the one part of that config that can move to a live-loaded channel without weakening a release gate. This adds the selection file, the renderer and the publish workflow. st0x.pricing loads the result; t0.devops #710 owns the bucket. universe.toml is a SELECTION, not a projection of token-lists/. The lists carry 166 rows across the three chains; staging pricing serves 50, and every gap is a decision the file now records: wtSGOV is off staging until the broker position is bought back, 4663 carries only the launch pair plus the wtSGOV parity probe until the volume cap and the external watchdog land, and the European names have no Alpaca upstream at all. wtRKLB runs the other way -- in the pricing config, absent from the lists -- so it gets an explicit [[override]] rather than being silently dropped. Seeded from st0x.pricing deploy/config/staging.toml at origin/master and diffed against it: 50 token slots and 47 assets, identical on every field. The first publish is a deliberate no-op. The renderer refuses rather than guesses. A symbol absent from the chain's token list with no override is a hard error (a typo like wtAAPPL cannot become a silent drop, which would take a live symbol off the plane), as is an unknown env, an unknown chain, a duplicate, and an empty selection. Output is canonical -- sorted, no timestamp, with a revision that is the sha256 of the content -- so re-rendering an unchanged selection is byte-identical and mints no generation. Per-commit provenance rides in the GCS object's custom metadata instead. CI judges the candidate INSIDE the live staging pricing image before publishing, restoring the guarantee --validate-config gives the released config today. That is the config-ahead-of-image hole (liquidity 2026-09-04, RAI-2266), so an image with no --validate-universe fails the run rather than warning: until the st0x.pricing loader is released to staging there is nothing to publish to. Staging only. The publisher is conditioned to the staging/ prefix, so this workflow cannot reach a production plane. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The step claimed it validated against "the image the staging plane is actually running". That is not what :latest resolves to -- the release flow pins digests, so the newest build and the serving digest can differ. Reading the serving digest needs run.viewer on t0-pricing-staging, which is not granted. So keep :latest, matching what config-check.yml already does, and name it for what it is: early warning. The loader re-validates on every load, and that is the actual gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two fixes to the publish workflow. It called --validate-universe with no --config while the validator still required one, so the job would have failed the first time it ran. That is an ordering fact rather than a typo: this repo renders the document, st0x.pricing owns the config, and the image receives its own from a deploy-time secret mount rather than from a layer, so neither the runner nor the container has a file to point at. st0x.pricing now takes the config as optional and reports which two checks it skipped without it (chain existence and the spread-aware de-listing warning); everything else still runs, and the loader re-validates in full on every load. And the header now names the merge order. The bucket and this workflow's identity come from t0.devops #710, a gated stack needing a human apply, so a push here before that lands fails at the auth step. Better to read that in the file than to diagnose it from a red run. 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.
Part 2 of 3. Bucket: T0Trade/t0.devops#710. Loader: st0x.pricing, next.
Why
Listing a token is seven config edits across six repos and six PAM-gated releases, and every one of them restarts the service. A pricing restart refuses every quote for
cold_start_hold_secs(420s today) and then needs an oracle bounce, so a listing costs real downtime in market hours.The universe — which
(chain, symbol)slots a plane serves — is pure enumeration: no prices, no spreads, no keys. So it is the one part of that config that can move to a live-loaded channel without weakening a release gate.Why it is safe
A symbol published here with no spread row in the pricing plane's own released config cannot be quoted at all: the model returns
NoSpreadProfile, and the service omits it from/snapshotand refuses it on the WS. Onboarding stays two deliberate steps:st0x.pricing deploy/config/<env>.toml, released the normal PAM-gated way → the plane quotes it.Step 1 is reversible in seconds and cannot misprice anything. That is the whole argument for letting this bypass the release gate, so it is written into the header of
universe.toml. Never add a price-affecting field here.A selection, not a projection
token-lists/carryEvery gap is a decision, and the file now records each one: wtSGOV is off staging until the broker position is bought back; 4663 carries only the launch pair plus the wtSGOV cross-chain parity probe, because nothing else is listed there until the volume cap and the external watchdog land; the European names (wtAIR.PA, wtBMW.DE, wtMC.PA) have no Alpaca upstream at all. wtRKLB runs the other way — in the pricing config, absent from the lists — so it gets an explicit
[[override]]rather than being silently dropped.The first publish is a no-op
Seeded from
st0x.pricing deploy/config/staging.tomlatorigin/master, then diffed field by field:The two derivations it relies on were checked across all 103 token rows and 95 asset rows of both planes, not sampled:
vault == addresseverywhere, andupstream == symbolminus thewtprefix everywhere.The renderer refuses rather than guesses
Tested: a typo (
wtAAPPL), an unknown env, an unknown chain, a duplicate symbol, and an empty selection are each a hard error. A missing address is never a silent drop — a silent drop would take a live symbol off the plane.Output is canonical (sorted, no timestamp,
revision= sha256 of the content), so re-rendering an unchanged selection is byte-identical and mints no generation. Per-commit provenance rides in the GCS object's custom metadata instead. Verified: two renderscmpclean.CI judges the candidate inside the live image
Today a pricing config is parsed by
--validate-configinside the exact image that will read it. A runtime-loaded document would bypass that, which is precisely the config-ahead-of-image hole (liquidity 2026-09-04, RAI-2266). So the workflow pulls the image the staging plane is running and validates against it.An image with no
--validate-universefails the run rather than warning: until the st0x.pricing loader is released to staging there is nothing to publish to. That also makes the merge order explicit — t0.devops#710, then the pricing loader, then this.Staging only. The publisher is conditioned to the
staging/prefix in t0.devops, so this workflow cannot reach a production plane.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.