Skip to content

Show keccak-derived slots as derivations under their reserved row - #6

Open
halaprix wants to merge 2 commits into
masterfrom
feat/derived-slots
Open

Show keccak-derived slots as derivations under their reserved row#6
halaprix wants to merge 2 commits into
masterfrom
feat/derived-slots

Conversation

@halaprix

Copy link
Copy Markdown
Owner

Implements docs/design/derived-slots-v1.4.md. Closes slotscope-xu8 (.1, .2).

What you see

slotscope-zvd gave every reserved slot a row but could not show what actually
lives in a mapping — those entries sit near 2^256, nowhere near the declared
sequence. They now appear as child rows under the head they descend from.

The address is the derivation, not the number. The rail reads
keccak(0xaa…, 1), or keccak(0xbb…, keccak(0xaa…, 2)) for a nested hop, or
keccak(3)+2 for an array element. That is the slot rail — the address column
everywhere else in this instrument (line numbers, PCs, slot numbers) — so a
derived entry's address is written the way the EVM computes it. The raw 32-byte
slot is a hover detail.

Entries that were only read are included and marked read-only. Nothing in the
product surfaced those before, and "read but never written" is worth teaching.

How a child finds its parent

From Layer-1 facts alone, never by parsing a display label. KeccakObservation
carries the exact preimage, so:

  • a 64-byte input splits into key ‖ baseSlot — both halves exact;
  • a 32-byte input is an array or data head, and elements sit at keccak(base) + delta;
  • a nested hop hashes key ‖ innerHash, so the tail is followed until it is a
    declared slot.

Parent linkage is therefore slot arithmetic, not name matching. A head nested
inside a struct attaches with no special case, and the derivedCountByBase
name-matching gap noted in the v1.2 plan disappears rather than being patched.

buildRows stays compiler-only — the gapless invariant is about declared slots,
and a derived slot is not one — so children attach at the view layer.

Decisions worth keeping

  • The child set spans the whole run, not stepIndex <= cursor. Roving focus is
    index-based, so a row appearing mid-scrub would move focus under the reader. The
    cursor changes values, never row positions.
  • Orphans are visible. A touched slot whose derivation cannot be reconstructed
    gets its own honest heading instead of being dropped.
  • RuntimeStorage becomes a write log in observation order with step numbers,
    keeping the status pill, migration badge and constructor-origin heading. The grid
    is the map; two maps of the same state, ordered differently, only made a reader
    decide which to believe. Its runtime-group e2e assertion is replaced
    deliberately.
  • Roving focus now indexes a flat list of navigable rows, so arrow keys reach a
    child exactly like a declared slot.

Verification

Gate Result
format:check, lint, typecheck clean
test:unit 241/241 — new derivedRows suite runs real compiled contracts through the EVM, not hand-built layouts
test:e2e 62/62, including axe on a grid with derived children
fixtures:verify no drift
release:verify passed
build:check 225 KB main entry
benchmark:v0.1 p95 250 ms action-to-paint, 732 ms cold start — PASS

🤖 Generated with Claude Code

slotscope-zvd gave every reserved slot a row but could not show what lives in a
mapping, because those entries sit near 2^256, nowhere near the declared
sequence. They now appear as child rows under the head they descend from.

The address is written the way the EVM computes it — keccak(0xaa…, 1), or
keccak(0xbb…, keccak(0xaa…, 2)) for a nested hop, or keccak(3)+2 for an array
element — and that string goes in the slot rail, which is the address column
everywhere else in this instrument. The raw 32-byte slot is a hover detail. The
derivation is the identity, because it is the thing worth learning.

Every child is reconstructed from Layer-1 facts alone: KeccakObservation carries
the exact preimage, so a 64-byte input splits into key and base, a 32-byte input
is an array or data head, and a nested hop is followed until its tail is a
declared slot. Parent linkage is therefore slot arithmetic, not name matching —
a head nested inside a struct attaches with no special case, and the
derivedCountByBase name-matching gap from v1.2 disappears rather than being
patched.

buildRows stays compiler-only: the gapless invariant is about DECLARED slots, and
a derived slot is not one. Children attach at the view layer.

Decisions worth keeping:
- The child set spans the whole run rather than stopping at the cursor. Roving
  focus is index-based, so a row appearing mid-scrub would move focus under the
  reader; the cursor changes values, never row positions.
- Read-only entries are included and marked. Nothing surfaced them before, and
  "read but never written" is worth teaching.
- A touched slot whose derivation cannot be reconstructed becomes a visible
  orphan, never a silent drop.
- RuntimeStorage becomes a write log in observation order. The grid is the map;
  two maps of the same state only made a reader pick one to believe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
slotscope Ready Ready Preview Jul 30, 2026 3:02pm

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant