diff --git a/.playwright-screens/ds2-grid.png b/.playwright-screens/ds2-grid.png deleted file mode 100644 index abc2c60..0000000 Binary files a/.playwright-screens/ds2-grid.png and /dev/null differ diff --git a/.playwright-screens/ds2-locked.png b/.playwright-screens/ds2-locked.png deleted file mode 100644 index 82e61d7..0000000 Binary files a/.playwright-screens/ds2-locked.png and /dev/null differ diff --git a/.playwright-screens/ds2-viewport.png b/.playwright-screens/ds2-viewport.png deleted file mode 100644 index c30cc8d..0000000 Binary files a/.playwright-screens/ds2-viewport.png and /dev/null differ diff --git a/.playwright-screens/ds3-instrument.png b/.playwright-screens/ds3-instrument.png deleted file mode 100644 index 6e0cc2b..0000000 Binary files a/.playwright-screens/ds3-instrument.png and /dev/null differ diff --git a/.playwright-screens/ds6-full-motion.png b/.playwright-screens/ds6-full-motion.png deleted file mode 100644 index ca3446f..0000000 Binary files a/.playwright-screens/ds6-full-motion.png and /dev/null differ diff --git a/.playwright-screens/ds6-reduced-motion.png b/.playwright-screens/ds6-reduced-motion.png deleted file mode 100644 index 098fa12..0000000 Binary files a/.playwright-screens/ds6-reduced-motion.png and /dev/null differ diff --git a/.playwright-screens/ds7-big.png b/.playwright-screens/ds7-big.png deleted file mode 100644 index 3d596dd..0000000 Binary files a/.playwright-screens/ds7-big.png and /dev/null differ diff --git a/.playwright-screens/ds7-small.png b/.playwright-screens/ds7-small.png deleted file mode 100644 index e9b6404..0000000 Binary files a/.playwright-screens/ds7-small.png and /dev/null differ diff --git a/.playwright-screens/ds7-stacked.png b/.playwright-screens/ds7-stacked.png deleted file mode 100644 index b06a74b..0000000 Binary files a/.playwright-screens/ds7-stacked.png and /dev/null differ diff --git a/.playwright-screens/ds8-syntax.png b/.playwright-screens/ds8-syntax.png deleted file mode 100644 index 6d8f1a3..0000000 Binary files a/.playwright-screens/ds8-syntax.png and /dev/null differ diff --git a/.playwright-screens/hero.png b/.playwright-screens/hero.png new file mode 100644 index 0000000..45dd0c6 Binary files /dev/null and b/.playwright-screens/hero.png differ diff --git a/.playwright-screens/strip-closeup.png b/.playwright-screens/strip-closeup.png deleted file mode 100644 index aa3ed69..0000000 Binary files a/.playwright-screens/strip-closeup.png and /dev/null differ diff --git a/.playwright-screens/v0.2-stepper.png b/.playwright-screens/v0.2-stepper.png deleted file mode 100644 index a7f4aaa..0000000 Binary files a/.playwright-screens/v0.2-stepper.png and /dev/null differ diff --git a/.playwright-screens/v10-final-lower.png b/.playwright-screens/v10-final-lower.png deleted file mode 100644 index 1e5ac1d..0000000 Binary files a/.playwright-screens/v10-final-lower.png and /dev/null differ diff --git a/.playwright-screens/v10-final-top.png b/.playwright-screens/v10-final-top.png deleted file mode 100644 index 0318b2d..0000000 Binary files a/.playwright-screens/v10-final-top.png and /dev/null differ diff --git a/.playwright-screens/v10-mid.png b/.playwright-screens/v10-mid.png deleted file mode 100644 index 6ee8a67..0000000 Binary files a/.playwright-screens/v10-mid.png and /dev/null differ diff --git a/.playwright-screens/v10-narrow.png b/.playwright-screens/v10-narrow.png deleted file mode 100644 index 34f51d8..0000000 Binary files a/.playwright-screens/v10-narrow.png and /dev/null differ diff --git a/.playwright-screens/v10-wide.png b/.playwright-screens/v10-wide.png deleted file mode 100644 index e4e8eeb..0000000 Binary files a/.playwright-screens/v10-wide.png and /dev/null differ diff --git a/.playwright-screens/variant-b.png b/.playwright-screens/variant-b.png deleted file mode 100644 index 9e696a3..0000000 Binary files a/.playwright-screens/variant-b.png and /dev/null differ diff --git a/AGENTS.md b/AGENTS.md index ae5986a..4d09fe8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -164,17 +164,21 @@ human-only clean-profile demo gate (see `.agents/agent-relay/adapter.json`). ## Architecture Overview -Static React/Vite app; all compilation is local (no application network -requests). Full details: `docs/architecture.md`, ADRs in `docs/adr/`, -honest cut-line in `docs/limitations-v0.1.md`. +Static React/Vite app; all compilation and execution is local (no +application network requests). Full details: `docs/architecture.md`, ADRs +in `docs/adr/`, honest cut-line in `docs/limitations-v1.0.md`. -- `packages/domain` — locked solc environment, immutable normalized models, UTF-8/UTF-16 index -- `packages/compiler` — web-worker solc (exact version or fatal), instruction/source-map decoding, `normalize()` +- `packages/domain` — locked solc environment, immutable normalized models, UTF-8/UTF-16 index, shared hex primitives +- `packages/compiler` — web-worker solc (exact version or fatal), settings-aware Standard JSON, instruction/source-map decoding, `normalize()` - `packages/layout` — solc storageLayout → exact bigint slot/cell model -- `packages/session` — share-URL schema v1 + bounded codec +- `packages/executor` — in-browser EVM worker (EthereumJS, Cancun), keyframe/delta trace, revert/event decode +- `packages/semantics` — Layer-3 resolver: keccak-chain grounding → exact/inferred/unresolved +- `packages/lifecycle` — generic worker-lifecycle client shared by `compiler` and `executor` +- `packages/session` — share-URL schema v1/v2 + bounded codec, `.slotscope.json` +- `packages/challenges` — challenge schema, runtime-derived answer validator, progress store - `packages/ui` — design tokens, focus ring, reduced-motion contract - `packages/fixtures` — public fixture corpus, goldens, manifests, Forge cross-check -- `apps/web` — composition: CodeMirror editor, assembly pane, storage grid, sharing +- `apps/web` — composition: CodeMirror editor, assembly pane, probe pane, cockpit bar, execute strip, sharing ## Conventions & Patterns diff --git a/CLAUDE.md b/CLAUDE.md index 545d31c..95dfedc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,17 +84,21 @@ human-only clean-profile demo gate (see `.agents/agent-relay/adapter.json`). ## Architecture Overview -Static React/Vite app; all compilation is local (no application network -requests). Full details: `docs/architecture.md`, ADRs in `docs/adr/`, -honest cut-line in `docs/limitations-v0.1.md`. +Static React/Vite app; all compilation and execution is local (no +application network requests). Full details: `docs/architecture.md`, ADRs +in `docs/adr/`, honest cut-line in `docs/limitations-v1.0.md`. -- `packages/domain` — locked solc environment, immutable normalized models, UTF-8/UTF-16 index -- `packages/compiler` — web-worker solc (exact version or fatal), instruction/source-map decoding, `normalize()` +- `packages/domain` — locked solc environment, immutable normalized models, UTF-8/UTF-16 index, shared hex primitives +- `packages/compiler` — web-worker solc (exact version or fatal), settings-aware Standard JSON, instruction/source-map decoding, `normalize()` - `packages/layout` — solc storageLayout → exact bigint slot/cell model -- `packages/session` — share-URL schema v1 + bounded codec +- `packages/executor` — in-browser EVM worker (EthereumJS, Cancun), keyframe/delta trace, revert/event decode +- `packages/semantics` — Layer-3 resolver: keccak-chain grounding → exact/inferred/unresolved +- `packages/lifecycle` — generic worker-lifecycle client shared by `compiler` and `executor` +- `packages/session` — share-URL schema v1/v2 + bounded codec, `.slotscope.json` +- `packages/challenges` — challenge schema, runtime-derived answer validator, progress store - `packages/ui` — design tokens, focus ring, reduced-motion contract - `packages/fixtures` — public fixture corpus, goldens, manifests, Forge cross-check -- `apps/web` — composition: CodeMirror editor, assembly pane, storage grid, sharing +- `apps/web` — composition: CodeMirror editor, assembly pane, probe pane, cockpit bar, execute strip, sharing ## Conventions & Patterns diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0596a58..9595471 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,8 +57,15 @@ See `docs/architecture.md` and the ADRs in `docs/adr/`. message says what changed and why. 3. Every commit must leave every gate green — there are no allowed-failure jobs and no retries for correctness or benchmark assertions. -4. Deployments are a deliberate act: `vercel deploy --prebuilt --prod` ships - the locally gate-verified artifact. Git pushes never deploy. +4. `master` is branch-protected: work happens on a branch, opens a PR, and + merges only once every required check (`quality`, `unit-fixtures`, + `build`, `browser`, `release-verify`) is green. No force-pushes, no + direct deletion, linear history only. +5. Deployment is native: Vercel is connected to `master` and redeploys + production automatically on every merge (Build Command `pnpm build`, + Output Directory `apps/web/dist`). A manual + `vercel deploy --prebuilt --prod` is only for verifying a build locally + before it lands. ## Design system diff --git a/README.md b/README.md index 8557311..e29cedd 100644 --- a/README.md +++ b/README.md @@ -5,45 +5,77 @@

SlotScope

EVM-1 · storage & bytecode probe
-A precision instrument for Solidity: compile one contract entirely in your -browser and inspect the exact truth of it — deployed bytecode related to -source in both directions, and the storage layout as byte-accurate 32-byte -slots.

+A precision instrument for Solidity: compile, deploy, and step through a +contract entirely in your browser, and see the exact truth of it — source +related to bytecode in both directions, every storage slot byte-accurate, +and every runtime access named honestly or not named at all.

▶ Live instrument

-![SlotScope — the instrument](.playwright-screens/ds7-big.png) +![SlotScope — the cockpit-bar workbench](.playwright-screens/hero.png) ## What it does +The full measure loop — compile, deploy, call, step the trace, read every +named storage access — works on one screen: source, assembly, and the +storage probe stay visible while the cockpit bar and execute strip drive +execution beneath them. + - **Compile locally, exactly.** One bundled solc - (`0.8.36+commit.8a079791.Emscripten.clang`, cancun, optimizer off, via-IR - off) runs in a web worker. No backend, no network requests, works offline - after load. A version mismatch is a fatal error, never a fallback. + (`0.8.36+commit.8a079791.Emscripten.clang`, cancun) runs in a web worker. + Optimizer (with runs) and via-IR are live toggles; the header echoes the + environment the latched result _actually_ compiled with, never the + request. No backend, no network requests, works offline after load. A + version mismatch is a fatal error, never a fallback. - **Source ↔ assembly, honestly.** Hover, keyboard-focus, or pin either side to see the exact compiler source range on the other — the complete set of instructions sharing a range, never a fake one-to-one mapping. Rows without a user source range say so. - **Storage as a memory map.** One row per slot, 32 byte cells under a - 31 → 0 ruler (byte 0 is the LSB, rightmost), cross-inheritance packing with + 31 → 0 ruler (byte 0 is the LSB, rightmost), a per-slot record listing + every field's name, byte range, and type, cross-inheritance packing with declaring-contract attribution from AST ids, packed structs, static arrays, - hatched padding, and honest "expansion unavailable" spans for anything the - compiler output can't confidently expand. -- **Run it locally.** Deploy into an in-browser EVM (EthereumJS, Cancun) and - call it through ABI forms, then scrub the trace: stack, memory, calldata, - storage overlays, step in/over/out, and the exact instruction plus source - range at every step. + hatched padding, and an honest explanation for anything the compiler + output can't confidently expand (a mapping's entries live at keccak-derived + runtime slots — deploy and call to see them named). +- **Initialized values, visible on deploy.** The constructor is an execution + like any other: deploying captures its trace exactly like a call, so every + initializer's actual byte value shows up in Runtime storage the moment + "Deploy locally" succeeds — labeled _from the constructor_, never parsed + from source. +- **Run it locally, one screen.** Deploy into an in-browser EVM (EthereumJS, + Cancun) and call it through ABI forms. The live storage strip stays pinned + above the trace detail tabs at every cursor position — one band per + touched slot, bytes written up to the cursor filled amber and byte-diffed + to the proven changed range. A full-width cockpit bar (steppers, scrubber, + cursor facts, outcome) keeps stepping in view without losing the source or + storage picture. - **Storage accesses get honest names.** `allowance[0xaa…][0xbb…]`, `mp[9].b`, `arr[2]` — resolved only when every hop is grounded in recorded keccak bytes and layout facts, with an auditable derivation chain (KeccakFlow), byte-level write diffs, string-form migration badges, and cold/warm Cancun - cost facts. Anything ungrounded says `unresolved`, never a guess. -- **Share as a URL.** The whole state — source, locked environment, - selection, one pin — travels in the `#s=` fragment (schema v1, bounded and - tamper-rejecting). The recipient recompiles locally; nothing is hosted. + cost facts. Resolution status is a border treatment and a mark + (`=`/`≈`/`?`), never a color alone. Anything ungrounded says `unresolved`, + never a guess. +- **Revert forensics and events.** A failed call decodes `Error(string)`, + `Panic(code)` with its meaning, or a declared custom error by name — + never a guess for an unknown selector — with the reverting step and a + jump-to-it. Emitted logs decode against the ABI when the shape matches + exactly, honest raw topics/data otherwise. +- **A curriculum, not just an inspector.** Six Layout/Derivation challenges + whose expected answers are _derived at runtime_ from the same compiler and + executor output the panes show — never an authored constant that could + drift — including one deliberately `Unresolved` case to resolve by hand. +- **Share as a URL or a file.** The whole state — source, compiler settings, + the deterministic call sequence, trace cursor, one pin — travels in the + `#s=` fragment (schema v2, bounded and tamper-rejecting) with live + size feedback against the 2,000-character budget; larger sessions round-trip + through `.slotscope.json`. The recipient's link recompiles, redeploys, and + replays the recorded calls locally; nothing is hosted. - **Keyboard-first, motion-optional.** Every interaction works without a - mouse; every animation collapses to 0 ms under reduced motion with - identical final state; axe-clean in both modes. + mouse (a Ctrl/⌘K command palette reaches the rest); every animation + collapses to 0 ms under reduced motion with identical final state; + axe-clean in both modes. ## Quickstart @@ -67,25 +99,30 @@ with zero retries. Evidence lives in [docs/releases](docs/releases/). ## Architecture -Static React/Vite workspace, seven exact-pinned packages: - -| Package | Role | -| ------------------- | --------------------------------------------------------------------- | -| `packages/domain` | Locked environment, immutable models, UTF-8↔UTF-16 index | -| `packages/compiler` | Worker (exact solc), instruction + source-map decoding, `normalize()` | -| `packages/layout` | solc storageLayout → exact bigint slot/cell model | -| `packages/session` | Share-URL schema v1, bounded codec | -| `packages/ui` | Instrument design tokens, focus + motion contract | -| `packages/fixtures` | Public fixture corpus, goldens, Forge cross-check | -| `apps/web` | Composition: editor, assembly, storage grid, sharing | +Static React/Vite workspace, ten exact-pinned packages: + +| Package | Role | +| --------------------- | ------------------------------------------------------------------------------------------------- | +| `packages/domain` | Locked environment, immutable models, UTF-8↔UTF-16 index, hex primitives | +| `packages/compiler` | Worker (exact solc), settings-aware Standard JSON, instruction/source-map decoding, `normalize()` | +| `packages/layout` | solc storageLayout → exact bigint slot/cell model | +| `packages/executor` | In-browser EVM worker (EthereumJS), keyframe/delta trace, revert/event decode | +| `packages/semantics` | Layer-3 resolver: keccak-chain grounding, exact/inferred/unresolved | +| `packages/session` | Share-URL schema v2 + bounded codec, `.slotscope.json` | +| `packages/challenges` | Challenge schema + runtime-derived answer validator, progress store | +| `packages/lifecycle` | Generic worker-lifecycle client shared by the compiler and executor | +| `packages/ui` | Instrument design tokens, focus + motion contract | +| `packages/fixtures` | Public fixture corpus, goldens, Forge cross-check | +| `apps/web` | Composition: editor, assembly, probe pane, cockpit bar, execute strip, sharing | Details: [docs/architecture.md](docs/architecture.md) · ADRs: [docs/adr](docs/adr/) · -honest cut-line: [docs/limitations-v0.1.md](docs/limitations-v0.1.md) · +honest cut-line: [docs/limitations-v1.0.md](docs/limitations-v1.0.md) · design system: [docs/design/instrument-identity-v0.15.md](docs/design/instrument-identity-v0.15.md) ## Roadmap v0.1 Compile Spine ✔ → v0.15 Instrument Identity ✔ → v0.2 Stepper ✔ → -v0.5 Semantics ✔ → v1.0 Launch ✔ (KPI test pending — see docs/releases/v1.0.md). +v0.5 Semantics ✔ → v1.0 Launch ✔ → v1.1 Cockpit workbench ✔ +(v1.0 KPI test pending — see [docs/releases/v1.0.md](docs/releases/v1.0.md)). History: [CHANGELOG.md](CHANGELOG.md). diff --git a/docs/architecture.md b/docs/architecture.md index bd1eaa0..5c31f24 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,51 +1,59 @@ -# SlotScope architecture (v0.1) +# SlotScope architecture -_See `docs/adr/` for recorded decisions and `docs/limitations-v0.1.md` for +_See `docs/adr/` for recorded decisions and `docs/limitations-v1.0.md` for the honest cut-line._ ## Shape -A static React/Vite application. Compilation and normalization happen -locally in the browser; the release makes **no** application network request -(asserted in e2e). The single future-facing seam is the versioned URL -session schema (ADR 0002). +A static React/Vite application. Compilation, execution, and normalization +all happen locally in the browser; the release makes **no** application +network request (asserted in e2e). The future-facing seam is the versioned +URL session schema (ADR 0002, extended to v2 by ADR 0004). ## Packages and dependency rules ```text -domain <── compiler - ▲ ▲ - │ │ -layout │ - ▲ │ - └──── web ──┤ - ▲ ▲ │ - │ │ │ - ui session - -fixtures imports domain/compiler and exercises layout/session in tests. -domain imports no product package. compiler, layout, session import no React. -web composes packages and owns no compiler/layout algorithms. -ui receives explicit view state and never invents Solidity semantics. +domain <── compiler <── executor <── semantics + ▲ ▲ ▲ ▲ + │ │ │ │ +layout │ lifecycle │ + ▲ │ ▲ │ + └────────── web ─────────┴──────────────┤ + ▲ ▲ ▲ ▲ │ + │ │ │ │ │ + ui session challenges fixtures ─────┘ + +domain imports no product package. lifecycle is the generic worker-client +base both compiler and executor subclass. fixtures imports domain/compiler +and exercises layout/session/executor in tests. compiler, layout, executor, +semantics, session, challenges import no React. web composes packages and +owns no compiler/layout/execution/resolution algorithms. ui receives +explicit view state and never invents Solidity semantics. ``` -| Package | Responsibility | -| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `@slotscope/domain` | Locked environment constants, immutable normalized models, `Utf8IndexMap` (solc UTF-8 byte offsets ↔ editor UTF-16 code units), canonical JSON, deep-freeze | -| `@slotscope/compiler` | Worker protocol/client/worker (ADR 0001), fixed Standard JSON input, instruction decoder, source-map decoder, `normalize()` | -| `@slotscope/layout` | `decodeStorageLayout`: solc storageLayout → exact bigint slot/cell model with honest collapses | -| `@slotscope/session` | Session schema v1 + bounded codec (ADR 0002) | -| `@slotscope/ui` | Design tokens, focus ring, motion contract (`tokens.css`) | -| `@slotscope/fixtures` | Public fixture corpus, goldens, manifests, verify script | -| `@slotscope/web` | Composition: editor, assembly pane, relation index, storage grid, sharing | +| Package | Responsibility | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `@slotscope/domain` | Locked environment constants, immutable normalized models, `Utf8IndexMap` (solc UTF-8 byte offsets ↔ editor UTF-16 code units), canonical JSON, deep-freeze, shared hex primitives | +| `@slotscope/compiler` | Worker protocol/client/worker (ADR 0001), settings-aware Standard JSON input, instruction decoder, source-map decoder, `normalize()` | +| `@slotscope/layout` | `decodeStorageLayout`: solc storageLayout → exact bigint slot/cell model with honest collapses | +| `@slotscope/executor` | In-browser EVM worker (EthereumJS, Cancun), keyframe/delta trace substrate (ADR 0003), keccak/storage observations, revert and event decode | +| `@slotscope/semantics` | Layer-3 resolver: pure keccak-chain grounding over the frozen trace observations → exact/inferred/unresolved | +| `@slotscope/lifecycle` | Generic worker-lifecycle client (generation/job ids, cancel-by-termination, crash recovery) that `compiler` and `executor` subclass | +| `@slotscope/session` | Share-URL schema v1/v2 + bounded codec, `.slotscope.json` (ADR 0002, ADR 0004) | +| `@slotscope/challenges` | Challenge schema, runtime-derived answer validator, local-first progress store | +| `@slotscope/ui` | Design tokens, focus ring, motion contract (`tokens.css`) | +| `@slotscope/fixtures` | Public fixture corpus, goldens, manifests, verify script | +| `@slotscope/web` | Composition: editor, assembly pane, probe pane, cockpit bar, execute strip, relation index, sharing | ## Data flow -Editor text → Compile (job id + locked environment) → worker (exact-version -verified soljson) → raw Standard JSON output → pure `normalize()` → -immutable `CompilationResult` → assembly view + relation index + storage -decode → UI state (selection + one pin) → session schema v1 → `#s=` URL. -Transient hover/focus/pin state never mutates the compiled model. +Editor text → Compile (job id + settings + locked environment) → worker +(exact-version verified soljson, settings echoed back and verified against +the request) → raw Standard JSON output → pure `normalize()` → immutable +`CompilationResult` → assembly view + relation index + storage decode → UI +state (selection + one pin) + the deterministic call sequence + trace cursor +→ session schema v2 → `#s=` URL or `.slotscope.json`. Transient hover/focus/ +pin state never mutates the compiled model. ## Execution and trace substrate (v0.2) diff --git a/vercel.json b/vercel.json index 45c873b..e19d4fb 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { "git": { - "deploymentEnabled": false + "deploymentEnabled": true } }