@ouronet/talos-registry 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ buildCall("TS01-C1.DPTF|C_Transfer", {
17
17
  Note `1` became `1.0`. Pact's decimal lexer rejects a bare integer in a decimal slot, and that
18
18
  is the least interesting thing this package stops you getting wrong.
19
19
 
20
- **This build:** 423 entrypoints, 428 previews, surface `9091592ba15520f4`, generated against mainnet.
20
+ **This build:** 423 entrypoints, 428 previews, surface `8107265a0be6edc3`, generated against mainnet.
21
21
  Every figure in this file is asserted by `tests/readme.test.ts` against the bundled snapshot, so
22
22
  a stale number fails the suite rather than misleading a reader.
23
23
 
@@ -178,10 +178,12 @@ import { tooltipModel, signatureRows } from "@ouronet/talos-registry";
178
178
  const m = tooltipModel("TS01-C1.DPTF|C_Transfer", { id: '"OURO-8Nh-JO8JO4F5"' });
179
179
  // m.slots -> EVERY execution parameter, in order, with type, value and flags
180
180
  // m.preview -> the INFO_ call with ITS OWN parameter list
181
- // m.kind -> "ouronet" | "stoa" | "kadena" (only ouronet has a cost section)
181
+ // m.kind -> "ouronet" | "stoa" | "kadena" (only ouronet has a cost section)
182
+ // m.chainColor -> the CANONICAL border colour for that chain
183
+ // m.consumer -> who is rendering, for the caller zone
182
184
  ```
183
185
 
184
- Seven rules, each there because it was got wrong first — most recently a six-parameter transfer
186
+ Ten rules, each there because it was got wrong first — most recently a six-parameter transfer
185
187
  whose tooltip showed five arguments, because it was rendering the preview's list under the
186
188
  execution's heading. Read the canon before building one.
187
189
 
package/TOOLTIP-CANON.md CHANGED
@@ -126,21 +126,61 @@ header comment. The claim had been *inferred* from "this is not an Ouronet opera
126
126
  nothing about who pays the host chain — and it was wrong in the direction that matters, telling a
127
127
  user they were about to spend when they were not.
128
128
 
129
- **Make the two visually distinct — the palette is yours.** The distinction is mandatory because
130
- the two differ in what they cost and what can be previewed; the specific colour is presentation,
131
- and this package does not get a vote on your design language. A rule that mandates a palette in
132
- someone else's application is a rule that gets ignored, and an ignored rule weakens the ones that
133
- matter.
129
+ ### The palette is canon — `CHAIN_PALETTE`
130
+
131
+ | chain | colour | |
132
+ |---|---|---|
133
+ | `ouronet` | `#3b82f6` | **blue** |
134
+ | `stoa` | `#ceac5f` | **gold** |
135
+ | `kadena` | `#22c55e` | **green** |
136
+
137
+ **This reverses the 1.4.0 position**, which said the palette was each app's business. The
138
+ reasoning then — that a package should not dictate colours inside someone else's design language —
139
+ was right about *decoration* and wrong about *this*. The colour encodes **which chain your money
140
+ is on**. Two apps teaching different meanings for the same colour is worse than neither using
141
+ colour at all, because a user believes whichever they learned first, and these two apps share
142
+ users.
143
+
144
+ `m.chainColor` and `m.chainLabel` are on the model so a renderer cannot quietly pick a different
145
+ one, and a test asserts every chain has both — if `Chain` ever gains a member without a colour,
146
+ that fails rather than rendering `undefined`, which CSS ignores and which therefore looks like
147
+ "no border rule" instead of a bug.
148
+
149
+ Colours were taken from what was already in use rather than invented: gold is OuronetUI's STOA
150
+ accent, blue its established `#3b82f6`, green its success green — unused on any tooltip surface
151
+ and therefore free to carry this meaning.
134
152
 
135
- In practice:
153
+ **Make the distinction run in every direction.** We coloured `stoa` gold and left `ouronet` on the
154
+ panel's ordinary grey, so it only existed one way: a reader who never hovered a native button
155
+ learned no rule at all, and gold read as "something is odd about this one" rather than as a
156
+ category. Every kind gets a colour *and* a label.
136
157
 
137
- | | `stoa` | `ouronet` | `kadena` |
138
- |---|---|---|---|
139
- | OuronetUI | gold `#ceac5f` | blue `#3b82f6` | *(not surfaced)* |
140
- | Codex | *its own* | violet | **green** |
158
+ And **say what is missing** rather than leaving the cost panel empty — an empty panel reads as
159
+ "the price failed to load".
160
+
161
+ ## Rule 4b — say who is rendering
162
+
163
+ Two applications draw this tooltip, and a Codex panel can open over an OuronetUI page. That is
164
+ exactly the case where "whose tooltip is this" is hardest to infer from chrome and most useful to
165
+ state.
166
+
167
+ **Carry a caller zone naming the consumer.** One line, its own accent:
168
+
169
+ ```ts
170
+ tooltipModel(key, values, CONSUMERS.Codex) // m.consumer -> { name, accent }
171
+ ```
172
+
173
+ | consumer | accent | |
174
+ |---|---|---|
175
+ | `OuronetUI` | `#d2d3d4` | neutral |
176
+ | `Codex` | `#8b5cf6` | **violet** |
177
+
178
+ **Two axes, deliberately.** The border says *what you are looking at*; the caller zone says *who
179
+ is showing it to you*. A consumer accent is therefore never a chain colour, and a test asserts the
180
+ two sets do not intersect — the moment they do, the two meanings merge and both become unreadable.
141
181
 
142
- Whatever you choose, make it carry meaning rather than decoration, and **say what is missing**
143
- rather than leaving the cost panel empty — an empty panel reads as "the price failed to load".
182
+ `name` matches the `consumerName` each app already uses for its codex settings, so the workspace
183
+ has one spelling of "OuronetUI" rather than two.
144
184
 
145
185
  Suggested footer, which is what OuronetUI ships:
146
186
 
@@ -152,11 +192,6 @@ a StoaChain `coin` call, so nothing prices it. Gas is still sponsored.
152
192
  Signatures are in `STOA_SIGNATURES` and `KADENA_SIGNATURES`, transcribed from the deployed
153
193
  contracts with line numbers. Use them rather than typing your own.
154
194
 
155
- **Make the distinction run in every direction.** We coloured `stoa` gold and left `ouronet` on the
156
- panel's ordinary grey — so the distinction existed one way only, and a reader who never hovered a
157
- native button learned no rule at all. Gold then reads as "something is odd about this one" rather
158
- than as a category. Give every kind a colour and a label, not just the exceptional ones.
159
-
160
195
  ## Rule 5 — a ghost is not data
161
196
 
162
197
  Every parameter has a worked example. The shapes are real — read from mainnet — so you can see
@@ -202,6 +237,81 @@ which is why there are two. `tooltipModel` applies both for you.
202
237
 
203
238
  ---
204
239
 
240
+ ## Rule 7 — fill every parameter you can, and prove it
241
+
242
+ A tooltip that leaves a fillable slot showing the registry's example is not "partially
243
+ implemented". It is **wrong**, and it is wrong in the way that does not announce itself: the
244
+ examples are real ids read from mainnet, so the panel looks like data and the preview succeeds
245
+ against somebody else's entity.
246
+
247
+ Four separate bugs came from this, all on buttons that looked fine:
248
+
249
+ | what showed | what it meant |
250
+ |---|---|
251
+ | `ats = "Auryndex-O136CBn22ncY"` on a **SilverStoa** button | a genuine fee, for the wrong pool |
252
+ | `patron` = the example account | `INFO_ATS\|ColdRecovery` read an empty ledger and reported a table error |
253
+ | `rt = <no live example>` | the page knew the token and never passed it |
254
+ | `ats1`/`ats2` = `<no live example>` | on a button whose whole question is "from where, to where" |
255
+
256
+ ### Fill BY ROLE, not by name
257
+
258
+ Pact types cannot help: `patron`, `id`, `ats` and `swpair` are all `string`, and a value of the
259
+ wrong kind in any of them type-checks, renders plausibly and prices the wrong thing.
260
+
261
+ ```ts
262
+ paramRole("ats") // "ats-pool"
263
+ paramRole("rt") // "token-id"
264
+ paramRole("patron") // "ouronet-account"
265
+ paramsByRole() // the whole vocabulary, grouped
266
+ ```
267
+
268
+ Roles are **derived from the shape of each ghost**, which was read from mainnet — an Ouronet
269
+ account is a glyph string, a pool is `Name-O136CBn22ncY`, a swap pair starts `W|`, a hibernated
270
+ DPOF starts `H|`. Not a hand-written list of 332 names: hand-classifying the vocabulary is the
271
+ same act that caused the bugs, a human deciding `ats` looks like a token because both are strings.
272
+
273
+ **A name whose ghost is the generic `"example"` gets no role**, and nothing should be inferred
274
+ about it. Those stay visible placeholders. As of this writing 35 names classify and the rest do
275
+ not, which is an honest answer rather than a gap to be filled with plausible-looking values.
276
+
277
+ ### The check
278
+
279
+ ```ts
280
+ unfilledFillable(model) // slots with a known role and no supplied value
281
+ ```
282
+
283
+ **A conformant consumer asserts this is empty for every button it renders.** That is the
284
+ difference between "we fixed the tooltip someone complained about" and "this class cannot recur".
285
+
286
+ Note what it deliberately does *not* report: placeholders (honestly unresolved) and preflight-fed
287
+ slots (must not be invented). It answers *what did you leave on the table*, not *what is missing*.
288
+
289
+ ### One fill map, not one per page
290
+
291
+ If two value-builders exist, they will diverge, and the divergence will be a whole category. In
292
+ OuronetUI the dashboard's builder had an account list from day one and the token pages' had none
293
+ at all — which is exactly why Cold Recovery errored on three pages and nowhere else. It surfaced
294
+ only because one preview out of two happened to read a table.
295
+
296
+ Derive the fill map from `paramsByRole()` once, and give every page the same one.
297
+
298
+ ## Rule 8 — a refusal is an answer; render it as one
299
+
300
+ `No value found in table ouronet-ns.ATS_ATS|Ledger for key: SilverStoa…` is not a crash. It is the
301
+ chain saying *this subject has no row here* — often the most useful thing the tooltip could tell
302
+ you, and it looks like a bug.
303
+
304
+ Classify before rendering:
305
+
306
+ - **missing row** (`No value found in table … for key …`) → say what it means: *"no position in
307
+ this pool yet"*. The operation is real; the subject simply has nothing to act on.
308
+ - **placeholder argument** → name the argument, do not show the refusal. The read was never going
309
+ to succeed and that is not the contract's fault.
310
+ - **anything else** → show it. An unclassified failure is worth seeing raw.
311
+
312
+ And do not fire a read that cannot succeed: `m.shouldRead` is already false when an argument is a
313
+ placeholder.
314
+
205
315
  ## Placement and behaviour
206
316
 
207
317
  Not expressible in the model, so they are stated here and they are not optional.