sememe 0.4.21 → 0.4.22
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 +112 -2
- package/package.json +1 -1
- package/src/adopt.mjs +8 -7
- package/src/codec.mjs +50 -1
- package/src/dialect.mjs +36 -1
- package/src/discriminators.mjs +22 -7
- package/src/rawle.mjs +13 -9
- package/src/store-read.mjs +9 -6
package/README.md
CHANGED
|
@@ -132,7 +132,7 @@ sememe is a local, content-addressed schema store with a verification contract:
|
|
|
132
132
|
## Install (as a consumer, not a contributor)
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
npm install github:auroraxo/sememe#v0.4.
|
|
135
|
+
npm install github:auroraxo/sememe#v0.4.22 # tagged release; no npm account needed
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
```js
|
|
@@ -151,7 +151,7 @@ The **library** refuses the same way: importing `sememe/pull` or `sememe/gap` wi
|
|
|
151
151
|
**From an install (not a clone), the CLI is `npx sememe …`** — npm exec resolves the package's own `bin` from `node_modules`; the bare command `sememe` is not on your `PATH` after a plain `npm install`, and `node bin/sememe.mjs` works only inside a clone of the repository (both measured 2026-11-05 from a real tarball install). The consumer journey from the install, verbatim:
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
npm install github:auroraxo/sememe#v0.4.
|
|
154
|
+
npm install github:auroraxo/sememe#v0.4.22 @solana/web3.js # acquisition needs the peer
|
|
155
155
|
npx sememe pull whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc --store ./store
|
|
156
156
|
npx sememe decode whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc 2b04ed0b1ac91e62a40d0100000000000100000000000000af331ba8327fbb35b1c4feff00000000010001010000000601 --store ./store
|
|
157
157
|
```
|
|
@@ -2570,6 +2570,116 @@ way shank was. What this fix guarantees is that until that happens, the
|
|
|
2570
2570
|
interface is refused by name at every boundary and never crashes the codec
|
|
2571
2571
|
or pretends to be decodable.
|
|
2572
2572
|
|
|
2573
|
+
## The product SPEAKS the dialect it named: the raw-LE engine is wired (2026-11-12)
|
|
2574
|
+
|
|
2575
|
+
PULS X named the refusal ("naming a dialect is not speaking it") and PULS Y
|
|
2576
|
+
built the speaker — an engine for exactly the grammar the diabloarb file
|
|
2577
|
+
DECLARES (`encoding: "little-endian; u8/u16/u32/u64 unsigned, i64 signed; no
|
|
2578
|
+
padding; no length prefixes except the u8 counters named in each header"`):
|
|
2579
|
+
u8-tag dispatch, dict `types` with declared fixed sizes, counter-prefixed
|
|
2580
|
+
vectors (`Base[n_bases]`, `u8[n_hops]`), one byte-decided optional trailing
|
|
2581
|
+
field. 102/102 committed payloads decoded byte-exactly, and the product
|
|
2582
|
+
boundary stayed untouched — deliberately: capability first, the wiring with
|
|
2583
|
+
the pins it rewrites.
|
|
2584
|
+
|
|
2585
|
+
**This pulse is the wiring** (the card's own declared next step):
|
|
2586
|
+
|
|
2587
|
+
- `src/codec.mjs` routes every decode/encode of a file OUTSIDE the Anchor
|
|
2588
|
+
shape but INSIDE the engine grammar through `src/rawle.mjs`, before the
|
|
2589
|
+
shape gate. The result carries the codec's own fields (`discriminator` is
|
|
2590
|
+
the dispatch prefix in hex; `dialect: 'raw-le'`), and every engine refusal
|
|
2591
|
+
is translated into `CodecError` — one refusal family at the product
|
|
2592
|
+
boundary, never a `RawLeError` leak, never a crash.
|
|
2593
|
+
- `src/dialect.mjs` splits the question "can anything read this file?" from
|
|
2594
|
+
"can the Anchor codec read it?": `unreadableDialectReason`/`assertReadableDialect`
|
|
2595
|
+
pass an engine-readable file and keep the exact named refusal (code
|
|
2596
|
+
`unsupported-idl-dialect`) for everything else; `idlDialect` reports
|
|
2597
|
+
`'raw-le'` for a wired dialect.
|
|
2598
|
+
- The store gates (`src/adopt.mjs` write path, `src/store-read.mjs` read
|
|
2599
|
+
path) use the readable-dialect guard: a raw-LE object adopts under its
|
|
2600
|
+
own program id, reads back, and the CLI loop `sememe adopt` → `sememe
|
|
2601
|
+
decode` works end-to-end offline (measured 2/2). Mislabelling under a
|
|
2602
|
+
foreign program id stays refused via `program_id` — the wiring opens the
|
|
2603
|
+
dialect gate, not the re-labelling gate.
|
|
2604
|
+
- The derivation (`src/discriminators.mjs`) passes the file WITHOUT filling
|
|
2605
|
+
any Anchor sighash table (the PULS X fill defect stays dead — measured
|
|
2606
|
+
filled {instructions: 0, events: 0}); the union (`src/union.mjs`)
|
|
2607
|
+
decodes through the engine like through any source.
|
|
2608
|
+
|
|
2609
|
+
Measured two-tree, byte-identical probe
|
|
2610
|
+
([`research/rawle-product-2026-11-12.json`](research/rawle-product-2026-11-12.json),
|
|
2611
|
+
pinned in `test/rawle-product-measurement.test.mjs` with a full
|
|
2612
|
+
gh-gated re-derivation): prefix tree = the PULS Y engine commit `05e6643`
|
|
2613
|
+
(engine present, product refuses — 0/102, store loop closed), this tree =
|
|
2614
|
+
102/102 decoded AND 102/102 byte-exact re-encoded at `decodeInstructionData`,
|
|
2615
|
+
5/5 product-level refusals as named `CodecError` faces (unknown tag,
|
|
2616
|
+
truncated payload, trailing bytes, unknown instruction on encode, missing
|
|
2617
|
+
argument on encode), derivation passes with NO table, adopt/read/union open,
|
|
2618
|
+
CLI store loop 2/2, Whirlpool control unchanged, a file that defeats BOTH
|
|
2619
|
+
engines keeps the exact PULS X named refusal at codec AND union. 24
|
|
2620
|
+
falsification counts, all zero. The PULS X measurement
|
|
2621
|
+
([`research/dialect-named-refusal-2026-11-12.json`](research/dialect-named-refusal-2026-11-12.json))
|
|
2622
|
+
was re-derived in its invariant form: its delta now reads crash → decodes
|
|
2623
|
+
through the engine, fill {4,0} → {0,0} — the no-crash, no-fill, no-mislabel
|
|
2624
|
+
invariants pinned on every payload. The PULS Y artifact stays the frozen
|
|
2625
|
+
pre-wiring baseline; its tool now exits 3 NO VERDICT in a wired tree BY
|
|
2626
|
+
DESIGN (stated in the tool header).
|
|
2627
|
+
|
|
2628
|
+
**Tests: 1 new pin file** (`test/rawle-product-measurement.test.mjs`, 12
|
|
2629
|
+
tests) plus the bent faces in `test/dialect-named-refusal.test.mjs` (D1b),
|
|
2630
|
+
`test/rawle-decoder.test.mjs` (G1) and
|
|
2631
|
+
`test/dialect-named-refusal-measurement.test.mjs`. Suite 912 → **933: 929
|
|
2632
|
+
pass / 4 named network skips / 0 fail**. Product code changed; the pin is
|
|
2633
|
+
the next release.
|
|
2634
|
+
|
|
2635
|
+
## The wiring SHIPS in v0.4.22 — and the ecosystem number it moves is +102 for the user who adopts, not the shipped store (2026-11-12)
|
|
2636
|
+
|
|
2637
|
+
This release carries the raw-LE wiring above to the install channel: the
|
|
2638
|
+
pinned tag now contains `src/rawle.mjs` and every wired boundary, so
|
|
2639
|
+
`npm install github:auroraxo/sememe#v0.4.22` degrades nobody (Whirlpool,
|
|
2640
|
+
shank, name-only anchor controls pin unchanged) and answers the diabloarb
|
|
2641
|
+
dialect where v0.4.21 refused it by name.
|
|
2642
|
+
|
|
2643
|
+
The wiring also prompted the card's declared coverage re-measurement, and
|
|
2644
|
+
the honest answer is TWO-HALVED (`tools/measure-diablo-adoption-coverage.mjs`
|
|
2645
|
+
→ `research/diablo-adoption-coverage-2026-11-12.json`, pinned by
|
|
2646
|
+
`test/diablo-adoption-coverage.test.mjs`, 9 tests, gh-gated re-derivation
|
|
2647
|
+
included):
|
|
2648
|
+
|
|
2649
|
+
- **At the shipped store the headline does NOT move.** Re-running the
|
|
2650
|
+
committed 2026-11-11 fixture through the coverage tool on this tree
|
|
2651
|
+
reproduces the committed artifact exactly: **88.5%, DiabLokxis 0 decoded
|
|
2652
|
+
/ 102 no-interface.** The wiring changed the codec, not the store, and
|
|
2653
|
+
the diabloarb copy cannot ship inside the store: `cryptodiablo/diabloarb`
|
|
2654
|
+
carries **no license** at the pin `f5005ac4` — live re-checked inside the
|
|
2655
|
+
pin test (GitHub license API 404 at the pin AND at HEAD, repo field
|
|
2656
|
+
null), not narrated. The bytes stay out of the repository and the
|
|
2657
|
+
package; `committedStoreModified: false` (readdir + per-file hash digest
|
|
2658
|
+
before/after).
|
|
2659
|
+
- **For the user who adopts, the 102 are real.** ONE `adopt` of the
|
|
2660
|
+
hash-gated first-party copy (canonical fingerprint
|
|
2661
|
+
`d6d4e8aa…` verified before anything runs) through the product's own
|
|
2662
|
+
write path, into a throwaway copy of the committed store, and the SAME
|
|
2663
|
+
report tool answers **102/102 decoded, 0 refused, 102 byte-exact**:
|
|
2664
|
+
headline **88.5% → 89.0%** of measured traffic, outside the watchlist
|
|
2665
|
+
**62.4% → 64.4%**. Every one of the 102 rides the **adopted** evidence
|
|
2666
|
+
column (`decodedVia.adopted +102`; `pulled` moves by exactly zero) — the
|
|
2667
|
+
same class split the pfeeU, manifest and archer measurements keep, so an
|
|
2668
|
+
adoption can never silently inflate the on-chain-pull number.
|
|
2669
|
+
|
|
2670
|
+
So "102 jetzt zählbar" was half true: countable **yes** — as adoption-path
|
|
2671
|
+
coverage, with a name and a byte-exact check — and **not** as a shipped
|
|
2672
|
+
headline. The acquisition gap at that program is a LICENSE gap, not a
|
|
2673
|
+
decoder gap: the one thing between the user and those 102 payloads is a
|
|
2674
|
+
licensed first-party copy, which is exactly what `sememe adopt` exists to
|
|
2675
|
+
carry while origin stays declared.
|
|
2676
|
+
|
|
2677
|
+
Suite after this release: **954 tests / 950 pass / 4 named network skips /
|
|
2678
|
+
0 fail** (945 → 954: the 9 new pin tests). CI green on `main`
|
|
2679
|
+
(43287274405) and on the published tag v0.4.22 (43287274209); the release
|
|
2680
|
+
surface re-measured against that tag is clean (newest = latest = pinned,
|
|
2681
|
+
0 defects — `research/release-surface-2026-11-12b.json`).
|
|
2682
|
+
|
|
2573
2683
|
## The remaining 35 publish-nothing programs, classified against foreign sources (2026-11-04)
|
|
2574
2684
|
|
|
2575
2685
|
With Lighthouse decoded under shank, exactly **35 programs (87 payloads)** remained
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sememe",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.22",
|
|
4
4
|
"description": "Local, fingerprint-verified Solana program interfaces: pull an IDL once, then decode instructions, accounts and events offline. Zero dependencies on the decode path. Ships derived wire tables for System, ComputeBudget, SPL Token, Token-2022 (incl. its token-metadata fallback), Raydium AMM v4 and the SPL Associated Token Account, pinned to upstream Rust.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/adopt.mjs
CHANGED
|
@@ -39,7 +39,7 @@ import path from 'node:path';
|
|
|
39
39
|
import { canonicalJson } from './canonical.mjs';
|
|
40
40
|
import { writeOnce } from './store-write.mjs';
|
|
41
41
|
import { originDigestOf, receiptDigestOf } from './receipt-digest.mjs';
|
|
42
|
-
import { assertIdlShape, declaredProgram } from './dialect.mjs';
|
|
42
|
+
import { assertIdlShape, assertReadableDialect, declaredProgram } from './dialect.mjs';
|
|
43
43
|
|
|
44
44
|
const sha256 = (data) => createHash('sha256').update(data).digest('hex');
|
|
45
45
|
|
|
@@ -117,12 +117,13 @@ export async function adopt(programInput, filePath, storeDir, { source = 'publis
|
|
|
117
117
|
}
|
|
118
118
|
throw Error(`${filePath} declares program ${declared}, not ${program} — refusing to re-label a foreign interface`);
|
|
119
119
|
}
|
|
120
|
-
// Gate 2 — the dialect gate
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
120
|
+
// Gate 2 — the dialect gate at the store's WRITE path: a dialect NO
|
|
121
|
+
// engine of the product can read is refused HERE by name, before
|
|
122
|
+
// adoption can store an undecodable interface. SINCE THE PULS-Z WIRING
|
|
123
|
+
// a raw-LE file passes (the codec serves it from src/rawle.mjs),
|
|
124
|
+
// everything else keeps the named refusal (measured 2026-11-12:
|
|
125
|
+
// diabloarb's dict `types` was the face of that refusal).
|
|
126
|
+
assertReadableDialect(idl, (reason) => Error(`${filePath}: ${reason}`));
|
|
126
127
|
|
|
127
128
|
const json = Buffer.from(canonicalJson(idl) + '\n');
|
|
128
129
|
const fingerprint = sha256(json);
|
package/src/codec.mjs
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
import { encodeBase58, decodeBase58 } from './base58.mjs';
|
|
22
22
|
import { isShankIdl, isShankDiscriminant, shankDiscriminatorBytes, SHANK_LEB128_TYPES } from './shank.mjs';
|
|
23
|
-
import { assertIdlShape } from './dialect.mjs';
|
|
23
|
+
import { assertIdlShape, unsupportedIdlReason } from './dialect.mjs';
|
|
24
|
+
import { rawLeDialectReason, decodeRawLe, encodeRawLe, RawLeError } from './rawle.mjs';
|
|
24
25
|
|
|
25
26
|
const MAX_DEPTH = 32;
|
|
26
27
|
// Instruction data is bounded by transaction size; a hostile length prefix
|
|
@@ -925,6 +926,43 @@ export function decodeInstructionData(idl, data, { allowTrailing, trailing, abse
|
|
|
925
926
|
throw new CodecError(`unknown absent policy ${JSON.stringify(absent)}`, null);
|
|
926
927
|
}
|
|
927
928
|
if (!(data instanceof Uint8Array)) throw new CodecError('instruction data must be a Uint8Array/Buffer', null);
|
|
929
|
+
// The raw little-endian dialect is WIRED at the product boundary (12.11.
|
|
930
|
+
// PULS Z). Until PULS X the diabloarb interface crashed the codec unnamed
|
|
931
|
+
// (TypeError over the dict `types`, 102/102 payloads); until this wiring
|
|
932
|
+
// the same interface was refused by name — honest, but naming a dialect is
|
|
933
|
+
// not speaking it (PULS Y: the engine decoded 102/102 byte-exactly, and
|
|
934
|
+
// the card's own next step was this adapter). The branch runs BEFORE the
|
|
935
|
+
// shape gate: a raw-LE IDL is exactly the shape the dialect gate names as
|
|
936
|
+
// unsupported, so the engine must answer first. When the bytes defeat the
|
|
937
|
+
// declared grammar the RawLeError is translated into the codec's error
|
|
938
|
+
// family — one refusal family at the product boundary, never a RawLeError
|
|
939
|
+
// leaking past it. The message text stays the engine's own words.
|
|
940
|
+
if (unsupportedIdlReason(idl) && rawLeDialectReason(idl) === null) {
|
|
941
|
+
try {
|
|
942
|
+
const raw = decodeRawLe(idl, data);
|
|
943
|
+
const { kind, instruction, tag, args, absent, consumed, total, trailing: rawTrailing } = raw;
|
|
944
|
+
const bytes = data instanceof Uint8Array ? data : Buffer.from(data);
|
|
945
|
+
return {
|
|
946
|
+
kind: kind ?? 'instruction',
|
|
947
|
+
instruction,
|
|
948
|
+
tag,
|
|
949
|
+
// The dispatch prefix in hex, like the codec's own `discriminator`
|
|
950
|
+
// field — here the prefix is the u8 tag the engine dispatched on,
|
|
951
|
+
// not an Anchor sighash. Consumers that render the prefix (CLI,
|
|
952
|
+
// src/decode.mjs) therefore see the real wire bytes.
|
|
953
|
+
discriminator: Buffer.from(data.subarray(0, 1)).toString('hex'),
|
|
954
|
+
args,
|
|
955
|
+
absent: absent ?? [],
|
|
956
|
+
consumed,
|
|
957
|
+
total,
|
|
958
|
+
trailing: rawTrailing ?? { count: Math.max(0, bytes.length - consumed), hex: '' },
|
|
959
|
+
dialect: 'raw-le',
|
|
960
|
+
};
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (!(error instanceof RawLeError)) throw error;
|
|
963
|
+
throw new CodecError(error.message, error.path);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
928
966
|
const ctx = context(idl);
|
|
929
967
|
const index = instructionIndex(idl);
|
|
930
968
|
const prefixLength = Math.min(8, data.length);
|
|
@@ -998,6 +1036,17 @@ export function decodeInstructionData(idl, data, { allowTrailing, trailing, abse
|
|
|
998
1036
|
* layout, and byte-exactness is the only evidence this function offers.
|
|
999
1037
|
*/
|
|
1000
1038
|
export function encodeInstructionData(idl, name, args, { absent } = {}) {
|
|
1039
|
+
// Same dialect branch as decodeInstructionData: a raw-LE interface is
|
|
1040
|
+
// served by its engine, byte for byte, and every refusal comes back in
|
|
1041
|
+
// the codec's own error family.
|
|
1042
|
+
if (unsupportedIdlReason(idl) && rawLeDialectReason(idl) === null) {
|
|
1043
|
+
try {
|
|
1044
|
+
return encodeRawLe(idl, name, args, { absent });
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
if (!(error instanceof RawLeError)) throw error;
|
|
1047
|
+
throw new CodecError(error.message, error.path);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1001
1050
|
const ctx = context(idl);
|
|
1002
1051
|
const instruction = instructionByName(idl, name);
|
|
1003
1052
|
const bytes = discriminatorBytes(instruction) ?? (() => {
|
package/src/dialect.mjs
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
// shank take the same paths as before, byte for byte.
|
|
44
44
|
|
|
45
45
|
import { isShankDiscriminant } from './shank.mjs';
|
|
46
|
+
import { rawLeDialectReason } from './rawle.mjs';
|
|
46
47
|
|
|
47
48
|
const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
48
49
|
|
|
@@ -107,7 +108,12 @@ export function unsupportedIdlReason(idl) {
|
|
|
107
108
|
* lists.
|
|
108
109
|
*/
|
|
109
110
|
export function idlDialect(idl) {
|
|
110
|
-
|
|
111
|
+
// A raw-LE file is SUPPORTED now (PULS Z wiring) but is not one of the
|
|
112
|
+
// three Anchor-family dialects this classifier enumerates; it reports
|
|
113
|
+
// its own name so callers can tell a wired dialect from a refused one.
|
|
114
|
+
if (unsupportedIdlReason(idl)) {
|
|
115
|
+
return rawLeDialectReason(idl) === null ? 'raw-le' : 'unsupported';
|
|
116
|
+
}
|
|
111
117
|
const instructions = idl.instructions;
|
|
112
118
|
if (instructions.length === 0) return 'anchor';
|
|
113
119
|
for (const instruction of instructions) {
|
|
@@ -143,3 +149,32 @@ export function assertIdlShape(idl, makeError) {
|
|
|
143
149
|
}
|
|
144
150
|
throw error;
|
|
145
151
|
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The refusal reason ONLY when no engine in the product can read the file.
|
|
155
|
+
* Since 12.11. PULS Z the raw little-endian engine (src/rawle.mjs) is wired
|
|
156
|
+
* into the codec: an IDL the Anchor codec refuses by SHAPE but the engine
|
|
157
|
+
* claims grammar for is decodable, so the store write gate, the store read
|
|
158
|
+
* gate and the discriminator derivation must not refuse it. Every other
|
|
159
|
+
* unsupported shape keeps the exact named refusal of `unsupportedIdlReason`.
|
|
160
|
+
* The codec's own dispatch (src/codec.mjs) asks the same two questions in
|
|
161
|
+
* the same order: is it outside the Anchor shape, and is it inside the
|
|
162
|
+
* engine grammar?
|
|
163
|
+
*/
|
|
164
|
+
export function unreadableDialectReason(idl) {
|
|
165
|
+
const reason = unsupportedIdlReason(idl);
|
|
166
|
+
if (!reason) return null;
|
|
167
|
+
return rawLeDialectReason(idl) === null ? null : reason;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Like assertIdlShape, but raw-LE-readable files pass — the engine speaks
|
|
171
|
+
* them now (PULS Z). Same named refusal and same error code otherwise. */
|
|
172
|
+
export function assertReadableDialect(idl, makeError) {
|
|
173
|
+
const reason = unreadableDialectReason(idl);
|
|
174
|
+
if (!reason) return;
|
|
175
|
+
const error = makeError(reason);
|
|
176
|
+
if (error && typeof error === 'object') {
|
|
177
|
+
error.code = UNSUPPORTED_DIALECT_CODE;
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
package/src/discriminators.mjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import { createHash } from 'node:crypto';
|
|
30
30
|
import { isShankDiscriminant } from './shank.mjs';
|
|
31
|
-
import {
|
|
31
|
+
import { unreadableDialectReason, idlDialect } from './dialect.mjs';
|
|
32
32
|
|
|
33
33
|
const sha256 = (text) => createHash('sha256').update(text).digest();
|
|
34
34
|
|
|
@@ -119,14 +119,29 @@ export function verifyDerivedDiscriminators(idl) {
|
|
|
119
119
|
* @returns {{ idl: object, filled: { instructions: number, events: number }, conflicts: object[] }}
|
|
120
120
|
*/
|
|
121
121
|
export function withDerivedDiscriminators(idl) {
|
|
122
|
-
// A dialect the
|
|
123
|
-
// 2026-11-12 on the diabloarb interface: the derivation
|
|
124
|
-
// tag-dispatch instructions with Anchor sighashes that
|
|
125
|
-
// table that looks authoritative and decodes nothing.
|
|
126
|
-
// (see src/dialect.mjs) and identical to the
|
|
127
|
-
|
|
122
|
+
// A dialect NO engine in the product can read gets NO derived table at
|
|
123
|
+
// all. Measured 2026-11-12 on the diabloarb interface: the derivation
|
|
124
|
+
// filled all four tag-dispatch instructions with Anchor sighashes that
|
|
125
|
+
// match no wire — a table that looks authoritative and decodes nothing.
|
|
126
|
+
// The refusal is named (see src/dialect.mjs) and identical to the
|
|
127
|
+
// codec's own. SINCE THE PULS-Z WIRING the raw little-endian engine is
|
|
128
|
+
// a product engine: a raw-LE interface carries its own tag dispatch (a
|
|
129
|
+
// u8 `tag`, not a sighash) and passes this gate to the codec, which
|
|
130
|
+
// serves it from src/rawle.mjs — no Anchor table is derived for it and
|
|
131
|
+
// none is needed.
|
|
132
|
+
const refusal = unreadableDialectReason(idl);
|
|
128
133
|
if (refusal) throw new Error(refusal);
|
|
129
134
|
const copy = structuredClone(idl);
|
|
135
|
+
// A raw-LE file (PULS Z wiring) dispatches on the u8 tag its own file
|
|
136
|
+
// declares — Anchor sighash derivation for it is the measured PULS X
|
|
137
|
+
// defect one gate change later: filled {instructions: 4, events: 0}, a
|
|
138
|
+
// table that looks authoritative and matches no wire byte. The honest
|
|
139
|
+
// table for this dialect is NO derived table, and the codec serves the
|
|
140
|
+
// file from src/rawle.mjs. The file still passes the gate (an engine
|
|
141
|
+
// reads it); only the Anchor filling is withheld.
|
|
142
|
+
if (idlDialect(idl) === 'raw-le') {
|
|
143
|
+
return { idl: copy, filled: { instructions: 0, events: 0 }, conflicts: [] };
|
|
144
|
+
}
|
|
130
145
|
let instructions = 0;
|
|
131
146
|
let events = 0;
|
|
132
147
|
const conflicts = [];
|
package/src/rawle.mjs
CHANGED
|
@@ -42,15 +42,19 @@
|
|
|
42
42
|
// A remainder that is neither exactly the option size nor zero is a
|
|
43
43
|
// named refusal (trailing bytes the layout does not explain).
|
|
44
44
|
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
45
|
+
// WIRED INTO THE PRODUCT SINCE PULS Z (2026-11-12): src/codec.mjs routes
|
|
46
|
+
// every decode/encode of a file OUTSIDE the Anchor shape but INSIDE this
|
|
47
|
+
// grammar through this engine — instruction decode/encode, the union
|
|
48
|
+
// probe, store adopt/read, and the discriminator derivation gate (an
|
|
49
|
+
// engine-readable file passes it; no Anchor table is derived for it).
|
|
50
|
+
// RawLeError is translated into CodecError at the boundary, so the
|
|
51
|
+
// product keeps ONE refusal family. Everything else — every anchor/shank/
|
|
52
|
+
// name-only file — takes the exact same codec paths as before, byte for
|
|
53
|
+
// byte, and files no engine can read keep PULS X's named refusal.
|
|
54
|
+
// The face this wiring turns: diabloarb's 102 sample payloads, refused
|
|
55
|
+
// by name until now, decode byte-exactly through the product entry. The
|
|
56
|
+
// pins it bends are the PULS X refusal faces for THIS interface, and the
|
|
57
|
+
// measurement that proves them is tools/measure-rawle-product.mjs.
|
|
54
58
|
//
|
|
55
59
|
// Byte-exactness note: decoded integers come back as Number (<= 4 bytes)
|
|
56
60
|
// or BigInt (8 bytes), exactly like src/codec.mjs's own convention, and
|
package/src/store-read.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { canonicalJson } from './canonical.mjs';
|
|
|
33
33
|
import { compareReceiptNames } from './receipt-order.mjs';
|
|
34
34
|
import { classifyReceiptFiles, isReceiptFile } from './receipt-name.mjs';
|
|
35
35
|
import { receiptIntegrity } from './receipt-digest.mjs';
|
|
36
|
-
import { assertIdlShape, declaredProgram } from './dialect.mjs';
|
|
36
|
+
import { assertIdlShape, assertReadableDialect, declaredProgram } from './dialect.mjs';
|
|
37
37
|
|
|
38
38
|
export { canonicalJson } from './canonical.mjs';
|
|
39
39
|
|
|
@@ -80,11 +80,14 @@ export async function readVerified(storeDir, program, receiptName) {
|
|
|
80
80
|
}
|
|
81
81
|
if (!Array.isArray(idl.instructions)) throw Error(`stored object for ${program} has no instructions`);
|
|
82
82
|
// Same two checks as the write path (src/adopt.mjs), applied on every
|
|
83
|
-
// read: a dialect the
|
|
84
|
-
// decode can crash unnamed, and a declared program that
|
|
85
|
-
// receipt's program is a re-labelling no matter which
|
|
86
|
-
// declaration (`address`, `metadata.address` or
|
|
87
|
-
|
|
83
|
+
// read: a dialect NO engine of the product can read is refused by NAME
|
|
84
|
+
// before any decode can crash unnamed, and a declared program that
|
|
85
|
+
// differs from the receipt's program is a re-labelling no matter which
|
|
86
|
+
// field carries the declaration (`address`, `metadata.address` or
|
|
87
|
+
// `program_id`). Since the PULS-Z wiring a stored raw-LE object reads
|
|
88
|
+
// through the engine (src/rawle.mjs) — a store that refused it by name
|
|
89
|
+
// now decodes instead, which is the point of the wiring.
|
|
90
|
+
assertReadableDialect(idl, (reason) => {
|
|
88
91
|
const error = Error(`stored object ${fingerprint} for ${program}: ${reason}`);
|
|
89
92
|
error.code = 'unsupported-idl-dialect';
|
|
90
93
|
return error;
|