Skip to content

Commit 33e5f2f

Browse files
burrows99claude
andauthored
refactor(debugger): exact event→line mapping + tracer-free concept view (#7)
A rigorous QA pass (per-pattern) found the source debugger's heuristic anchoring mapped events to the wrong line across every pattern, snapped breakpoints invisibly to unrelated lines, and leaked tracer identifiers (t *trace.Tracer, t.Pace, a broker t field) into the "concept" view. Re-architect so the concept and its instrumentation are genuinely separate: - trace stays the dedicated event library; pacing moves out to a tracer-free pace(ctx, d) helper that scales by the speed carried on the context, so concept code has no instrumentation. - Narration hooks are standalone statements sitting directly on the operation they describe; registry.go drops the hook lines and the residual tracer param/args and maps each event to the exact adjacent statement (above, or below for a Block) — an exact, non-heuristic map. Hook-only ifs are dropped so no dead {} remains. - Breakpoints are settable only on real stop lines and fire exactly there — no snapping, the dot never moves onto a brace/decl/dead line. - cmd/genconcepts materializes the pure concept into internal/concepts/<name>/ (one compilable package per pattern) via `go generate ./...`. - Fix mismodeling surfaced by QA: buffered blocks only when full, timeout's result channel cap, pub-sub broker kind (mutex, not cond), critical-section and cancel() now highlight their own line. Verified across all 27 patterns: 0 tracer leaks, 0 breakpoints on a brace/blank, 0 events off their stop line. Adds patterns/debug_test.go to lock the contract. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b72ac01 commit 33e5f2f

63 files changed

Lines changed: 2647 additions & 575 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,21 @@ The frontend proxies `/api` and `/healthz` to the backend, so you only open
120120
step-over*. Stepping advances one **concept line** at a time (several runtime
121121
events on one line collapse into a single step), so the highlight always
122122
moves in step with the picture.
123-
- **Breakpoints** — click the gutter next to any executable statement to set a
124-
breakpoint; playback halts when execution reaches it, and **Play** doubles as
125-
*continue to next breakpoint*. Only lines that actually run an event are
126-
breakpointable — you can never drop a breakpoint on a brace or a dead line.
127-
- **Clean concept view** — the tracer hooks (`t.Spawn`, `t.Send`, …) are
128-
stripped from the displayed source, and every event is mapped, via `go/ast`,
129-
onto the real statement it narrates. You read idiomatic Go; you debug idiomatic
130-
Go.
123+
- **Breakpoints** — click the gutter next to any line that actually runs a step
124+
(a send, a receive, a spawn, a lock, the critical section, a `cancel()`…) to
125+
set a breakpoint; playback halts **exactly** on that line, and **Play** doubles
126+
as *continue to next breakpoint*. The dot never moves and never lands on a
127+
brace, a declaration, or a dead line — the breakpoint stops precisely where it
128+
sits.
129+
- **Clean concept view** — the tracer hooks (`t.Spawn`, `t.Send`, …) live on
130+
their own lines and are stripped from the displayed source, along with the
131+
`t *trace.Tracer` parameter and the pacing calls, so what you read is **pure,
132+
idiomatic Go** — no instrumentation leaks in. Every event is mapped onto the
133+
exact statement it narrates (the hook sits directly on that statement), so
134+
highlight and breakpoints are precise, not heuristic. The same pure source is
135+
also materialized into [`backend/internal/concepts/`](backend/internal/concepts)
136+
— one compilable package per pattern — so you can read (and `go build`) the
137+
concept on its own.
131138

132139
Each pattern also ships **How it works** and **Interview notes** tabs.
133140

@@ -151,8 +158,11 @@ Each pattern also ships **How it works** and **Interview notes** tabs.
151158
```
152159
backend/
153160
cmd/server/main.go entrypoint (graceful shutdown via signal.NotifyContext)
154-
internal/trace/trace.go the instrumentation layer (Tracer, Event, source-line capture)
155-
internal/patterns/*.go one file per demo (+ registry.go: catalog, go/ast source mapping)
161+
cmd/genconcepts/main.go generates internal/concepts/ from the instrumented demos
162+
internal/trace/trace.go the dedicated event library (Tracer, Event, source-line capture)
163+
internal/patterns/*.go one file per demo (+ registry.go: catalog + go/ast strip/map)
164+
internal/patterns/helpers.go pace(): the pure, speed-scaled sleep (no tracer)
165+
internal/concepts/<name>/ generated pure-Go concept, one compilable package per pattern
156166
internal/server/server.go catalog + SSE routes
157167
frontend/
158168
src/App.tsx layout, run orchestration, the debugger play-head
@@ -163,13 +173,19 @@ frontend/
163173
docker-compose.yml backend + frontend, one command
164174
```
165175

166-
- **`trace`** — a `Tracer` that pattern code calls to narrate itself (`Spawn`,
167-
`Send`, `Recv`, `Block`, `Lock`, `Cancel`, …). Each call becomes a JSON
168-
`Event` stamped with the source line it came from. `Pace()` inserts the real,
169-
cancellable sleeps that make a run watchable. Safe for concurrent use.
176+
- **`trace`** — the dedicated event library. A `Tracer` that pattern code calls
177+
to narrate itself (`Spawn`, `Send`, `Recv`, `Block`, `Lock`, `Cancel`, …); each
178+
call becomes a JSON `Event` stamped with the source line it came from. Safe for
179+
concurrent use. Pacing is deliberately *not* here — the watchable, cancellable
180+
delay is `patterns.pace(ctx, d)`, a tracer-free helper that scales by the run
181+
speed carried on the context — so the concept code stays instrumentation-free.
170182
- **`patterns`** — each demo registers itself and exposes `Run(ctx, *Tracer)`.
171-
The source is `//go:embed`-ed; at catalog time `go/ast` strips the trace hooks
172-
and builds the event → concept-line map that powers the debugger.
183+
Narration hooks are written as their own statements sitting directly on the
184+
concept operation they describe. The source is `//go:embed`-ed; at catalog time
185+
`registry.go` drops the hook lines and the residual tracer parameter and maps
186+
each event to the exact adjacent statement — an exact, non-heuristic map. The
187+
same strip feeds `cmd/genconcepts`, which writes the pure concept out to
188+
`internal/concepts/` (`go generate ./...`).
173189
- **`server`**`GET /api/patterns` returns the catalog;
174190
`GET /api/stream?pattern=…&speed=…` runs one pattern and streams its events as
175191
SSE. The run is bound to the request context, so aborting the fetch (the

0 commit comments

Comments
 (0)