@ouronet/talos-registry 1.1.1 → 1.3.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 `7c2b70c6118d6db2`, generated against mainnet.
20
+ **This build:** 423 entrypoints, 428 previews, surface `9091592ba15520f4`, 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
 
@@ -166,3 +166,51 @@ four-segment pipe-joined string and an account is a glyph string, not a `k:` add
166
166
  **They are not submittable.** The accounts are not yours to sign for, and an amount of `1.0` is
167
167
  a placeholder. A preflight-fed parameter is `null` with a note naming the read — the registry
168
168
  will not invent a value it has just told you cannot be constructed.
169
+
170
+ ## The tooltip canon
171
+
172
+ `TOOLTIP-CANON.md` and `tooltipModel()` are the shared rules for rendering a pre-ZBOM tooltip —
173
+ what to show, in what order, and which of it may be prefilled. They are shipped as an
174
+ implementation rather than a document because a document drifts from the code it describes:
175
+
176
+ ```ts
177
+ import { tooltipModel, signatureRows } from "@ouronet/talos-registry";
178
+ const m = tooltipModel("TS01-C1.DPTF|C_Transfer", { id: '"OURO-8Nh-JO8JO4F5"' });
179
+ // m.slots -> EVERY execution parameter, in order, with type, value and flags
180
+ // m.preview -> the INFO_ call with ITS OWN parameter list
181
+ // m.kind -> "ouronet" | "native" (native renders gold, has no cost section)
182
+ ```
183
+
184
+ Six rules, each there because it was got wrong first — most recently a six-parameter transfer
185
+ whose tooltip showed five arguments, because it was rendering the preview's list under the
186
+ execution's heading. Read the canon before building one.
187
+
188
+ ### Where a ghost may be used — `ghost.use`
189
+
190
+ A ghost is not equally safe on every surface, and a single value cannot say so. Entries whose
191
+ usage is **restricted** carry a `use` block, keyed by parameter name:
192
+
193
+ ```jsonc
194
+ "ghost": {
195
+ "args": { "new-guard": { "readKeyset": "ks" } },
196
+ "use": { "new-guard": { "zbom": false, "display": "(read-keyset \"ks\")" } }
197
+ }
198
+ ```
199
+
200
+ | key | meaning |
201
+ |---|---|
202
+ | `zbom` | may this be **prefilled into an input** that can reach a signed transaction? |
203
+ | `tooltip` | may this be **rendered** where nothing is submitted? |
204
+ | `display` | render *this string* instead of the value |
205
+
206
+ **Both flags default to `true`, and the block appears only when something is restricted** — 6 of
207
+ 423 entrypoints today, every one of them guard-taking. A `use` block on all 2,182 slots would be
208
+ noise consumers learn to skip, and the one entry that matters would be invisible inside it.
209
+
210
+ The case that forced the split is `guard`. A guard **cannot be prefilled** — nobody may hand a
211
+ user a keyset and ask them to sign under it — so `zbom` is `false`. But a tooltip showing the
212
+ guard slot empty teaches the wrong call shape, so `tooltip` stays `true` and `display` renders
213
+ the Pact form the caller actually writes. That is a value which is **correct to show and wrong
214
+ to submit**, which is exactly what one tag cannot express.
215
+
216
+ **Read `use` before prefilling anything.** An absent parameter is unrestricted.
@@ -0,0 +1,215 @@
1
+ # The pre-ZBOM tooltip canon
2
+
3
+ A pre-ZBOM tooltip answers one question: **what will this button actually execute, and what will
4
+ it cost?** Hovering a launcher should tell you before you commit to opening anything.
5
+
6
+ Two applications render it — OuronetUI and the Codex — and they were converging on it
7
+ independently, each rediscovering the same traps a few weeks apart. **This document is not the
8
+ canon.** `src/tooltip.ts` is. A document drifts from the code it describes and nobody finds out
9
+ until a user sees a wrong number, so the rules are shipped as an implementation:
10
+
11
+ ```ts
12
+ import { tooltipModel, tooltipModels, signatureRows } from "@ouronet/talos-registry";
13
+
14
+ const m = tooltipModel("TS01-C1.DPTF|C_Transfer", { id: '"OURO-8Nh-JO8JO4F5"' });
15
+ // m.slots -> every execution parameter, in order, with type, value and flags
16
+ // m.preview -> the INFO_ call, with ITS OWN parameter list
17
+ // m.kind -> "ouronet" | "native"
18
+ ```
19
+
20
+ This file explains **why** each rule exists. Every one is here because it was got wrong first.
21
+
22
+ ---
23
+
24
+ ## Rule 1 — show every execution parameter
25
+
26
+ All of them, always, in declared order.
27
+
28
+ `DPTF|C_Transfer` takes six arguments and its tooltip displayed five, because it was rendering
29
+ the **preview's** list:
30
+
31
+ ```
32
+ execution (patron executor executee id transfer-amount method) 6
33
+ preview (patron id sender receiver transfer-amount) 5
34
+ ```
35
+
36
+ Both lists were true. Showing one under a heading that named the other was not. A tooltip that
37
+ lists five rows for a six-parameter function is not a summary — it is a wrong answer, and it
38
+ teaches a call shape that will not work.
39
+
40
+ `tooltipModel().slots` is always exactly the entrypoint's own parameters. A test asserts this for
41
+ **all 423**, not for the one that was reported.
42
+
43
+ ## Rule 2 — the arguments belong to the execution, the cost belongs to the preview
44
+
45
+ **410 of the 423 entrypoints have a preview whose parameter list differs from their own.** Only 13
46
+ match. Different names, different order, different arity.
47
+
48
+ So the two cannot share one numbered list, and the preview's values cannot be labelled with the
49
+ execution's names. That mislabelling was a real defect: it rendered `executor = <the pool id>`,
50
+ `swpair = true`, `toggle = MISSING` for a call that was entirely correct.
51
+
52
+ Render the execution's arguments as the numbered list. Mention the preview separately — its name,
53
+ its own parameter list, and the cost it returns.
54
+
55
+ **Bind by NAME, never by position.** Positional merging is silent: both values are strings, so an
56
+ executor's account lands in a token-id slot, type-checks, and prices a different question.
57
+ `tooltipModel(key, values)` merges by name and cannot do otherwise.
58
+
59
+ ## Rule 3 — types go on their own row
60
+
61
+ ```
62
+ (patron executor executee id transfer-amount method)
63
+ (string string string string decimal bool)
64
+ ```
65
+
66
+ not
67
+
68
+ ```
69
+ (patron:string executor:string executee:string id:string transfer-amount:decimal method:bool)
70
+ ```
71
+
72
+ The inline form roughly doubles the width of the widest line in the panel, and the panel is
73
+ already the widest thing on screen when it opens. A parallel row costs one line.
74
+
75
+ `signatureRows(m)` returns `{ names, types }` as equal-length arrays for exactly this.
76
+
77
+ ## Rule 4 — native is not Ouronet, and the difference is narrower than it looks
78
+
79
+ A `coin.*` call is StoaChain's own root-namespace contract. It is **not** in this registry and
80
+ never will be: the registry is generated from `ouronet-ns`.
81
+
82
+ What is actually different:
83
+
84
+ | | Ouronet | native |
85
+ |---|---|---|
86
+ | INFO_ preview | yes | **no** — nothing prices a `coin` call |
87
+ | IGNIS | collected | **none** |
88
+ | accounts | Ouronet glyph strings | **Kadena-shaped** (`k:` `c:` `u:` `w:`) |
89
+ | gas sponsorship | gas station pays | **gas station pays** — the same |
90
+
91
+ **That last row is the one to get right.** An earlier version of this claimed a native call was
92
+ not sponsored and that the signing account paid its own STOA gas. Both false: every native modal
93
+ carries `GAS_PAYER` on the gas-station key beside its own capability, and says so in its own
94
+ header comment. The claim had been *inferred* from "this is not an Ouronet operation", which says
95
+ nothing about who pays the host chain — and it was wrong in the direction that matters, telling a
96
+ user they were about to spend when they were not.
97
+
98
+ **Render native with a gold perimeter.** The gold is semantic, not decorative: it means *nothing
99
+ here prices this call*. And say what is missing rather than leaving the cost panel empty — an
100
+ empty panel reads as "the price failed to load".
101
+
102
+ Suggested footer, which is what OuronetUI ships:
103
+
104
+ ```
105
+ no IGNIS — this is not an Ouronet operation
106
+ a StoaChain `coin` call, so nothing prices it. Gas is still sponsored.
107
+ ```
108
+
109
+ Signatures for the native calls are in `NATIVE_SIGNATURES`, transcribed from the deployed
110
+ contract with line numbers. Use them rather than typing your own.
111
+
112
+ ## Rule 5 — a ghost is not data
113
+
114
+ Every parameter has a worked example. The shapes are real — read from mainnet — so you can see
115
+ that a swpair is a four-segment pipe-joined string and an account is a glyph string, not a `k:`
116
+ address. **They are not submittable**, and three cases need distinguishing:
117
+
118
+ - **`isPlaceholder`** — the generic `"example"`. 225 slots across 147 entrypoints, every one an
119
+ entity id for something that **does not exist on chain** (`fvt-id`, `pool-id`, `score-id`,
120
+ `anchor-id`). Mainnet holds zero anchors and zero scores today. A plausible fake id would return
121
+ a confident price for something nobody owns, which is worse than an obvious gap. **Render it so
122
+ it reads as a placeholder** — grey it, italicise it, anything but plain.
123
+ - **`isPreflightFed`** — the argument is the *output of a read*, not user input. The registry
124
+ refuses to invent one and so must you. `execution.preflight` names the read.
125
+ - **`prefillable: false`** — a guard. See rule 6.
126
+
127
+ **Do not fire the preview read when any argument is a placeholder.** The answer is a foregone
128
+ refusal, and a read per hover is latency the user pays for a known-no. `m.shouldRead` is already
129
+ false in that case.
130
+
131
+ ## Rule 6 — a guard may be shown and must not be prefilled
132
+
133
+ Six entrypoints take a `guard`, and five are account operations:
134
+
135
+ ```
136
+ DALOS|C_DeploySmartAccount · DALOS|C_DeployStandardAccount · DALOS|C_RotateGovernor
137
+ DALOS|C_RotateGuard · CODEX|C_RotateCodexGuard · AQP-DSA|C_SetOracleAuth
138
+ ```
139
+
140
+ A ghost carries two use tags, both defaulting true, emitted only where something is restricted:
141
+
142
+ ```jsonc
143
+ "use": { "new-guard": { "zbom": false, "display": "(read-keyset \"ks\")" } }
144
+ ```
145
+
146
+ - **`zbom: false`** — never prefill it into an input that can reach a signed transaction. You do
147
+ not hand someone a keyset and ask them to sign under it.
148
+ - **`display`** — render *this* instead of the value. The submittable form and the readable form
149
+ differ: `{"readKeyset": "ks"}` is a transaction-data key NAME, and `(read-keyset "ks")` is what
150
+ a caller actually writes.
151
+
152
+ A value that is **correct to show and wrong to submit** is precisely what one tag cannot express,
153
+ which is why there are two. `tooltipModel` applies both for you.
154
+
155
+ ---
156
+
157
+ ## Placement and behaviour
158
+
159
+ Not expressible in the model, so they are stated here and they are not optional.
160
+
161
+ **On the LAUNCHER, never on the ZBOM's execute button.** Owner ruling. Inside a ZBOM the
162
+ information is already on the page — the input zone lists every parameter with its declared type,
163
+ the INFO panel shows the cost — and a tooltip there draws *over* the modal it annotates. The
164
+ tooltip exists to tell you what a button will do **before** you commit to opening it.
165
+
166
+ **Anchor the edge NEAREST the button.** Placed above, pin the bottom and grow upward; placed
167
+ below, pin the top and grow down. Either way the edge beside the button never moves. Pinning the
168
+ top in both orientations — from an assumed height — makes every content change move the bottom
169
+ edge, so a cycling panel grows and shrinks *away* from the button, which is the direction nobody
170
+ is looking.
171
+
172
+ **Cycle at 10 seconds** when a button fronts several executions. One window, a depletion bar, a
173
+ dot per variant. The transaction toaster's ~3s is the wrong model to copy: a toast is glanced at,
174
+ this panel is read — an entrypoint, a parameter list, six arguments and a live cost. Restart at
175
+ the first variant on every hover; a panel resuming mid-cycle describes an operation you did not
176
+ just point at. Cycle in the order the ZBOM presents, so the first thing hovered is the first thing
177
+ met.
178
+
179
+ **Use pointer events.** `onMouseLeave` fires when the cursor crosses onto a child in some engines;
180
+ `pointerleave` does not.
181
+
182
+ **A flickering tooltip is usually a REMOUNT.** If a button component is declared *inside* another
183
+ component's body, every render creates a new component type and React unmounts the whole subtree,
184
+ destroying the open state. Hoist to module scope.
185
+
186
+ **Key the preview effect on the rendered CALL STRING**, not on a values object. An inline literal
187
+ gives a new object identity every render; keying on it re-fires the read, which sets state, which
188
+ re-renders — forever.
189
+
190
+ **Fail open.** An unknown key renders the children alone. A tooltip is an affordance; taking a
191
+ page down because a hint has no data is a worse bug than the missing hint.
192
+
193
+ **Elide long values in the middle, keeping head AND tail.** An Ouronet account is 162 glyphs;
194
+ three in one call makes a tooltip taller than the card it annotates. `"OURO-8Nh…JO4F5"` identifies
195
+ a value where a truncated prefix does not. Keep the quotes *outside* the elision — a reader has to
196
+ be able to tell a Pact string from a bare decimal — and put the full value on `title`.
197
+
198
+ ## Enforce it, do not remember it
199
+
200
+ The rule is about a **class**: every button that opens a ZBOM. Classes are where hand-checking
201
+ fails. OuronetUI's guard walks every page, finds every ZBOM-opening launcher and fails on a naked
202
+ one — it was written after the Define buttons were missed once, and it had to be widened after
203
+ three whole toolbars shipped bare while it read a single file and claimed the class.
204
+
205
+ Two things such a guard must get right: model **both** coverage forms (a textual wrap, or an
206
+ `entrypoint=` on a component that wraps internally), and exclude `onClose` handlers, which close a
207
+ ZBOM rather than open one.
208
+
209
+ ---
210
+
211
+ ## Sources
212
+
213
+ - `src/tooltip.ts` — the canon itself; `tests/tooltip.test.ts` enforces all six rules
214
+ - 410-of-423 preview divergence, 225 placeholder slots: recomputed from the bundled registry
215
+ - The sponsorship correction: owner, 2026-09-27