Liking cljdoc? Tell your friends :D

re-frame-debux v0.6 roadmap

Companion to improvement-plan.md. Consolidates the four open feature requests from the 2020 sprint into a single design narrative the maintainer can use to plan a v0.6 milestone — and to triage #34, #35, #36, #37.


Why this doc exists

Four feature requests were filed in July 2020 and sat untouched until v0.7:

  • #34 — Send exceptions to 10x. (Declined 2026-04-27.)
  • #35 — Make library usable from self-hosted ClojureScript. (Declined 2026-04-27.)
  • #36 — Include all trace — don't filter out the noisy bits. (Shipped in v0.7 — commit 0177254.)
  • #37 — Send Trace which marks the beginning and end of a function call. (Shipped in v0.7 — commit 8ba53a8.)

This doc was written when all four were open; it now functions as a v0.6/v0.7 retrospective. Pair A landed as designed (with implementation-name drift documented in the Pair A § headers); Pair B was declined for reasons recorded in §35 and §34.

Original framing follows for the historical record. Status annotations have been folded in at section headers and at the recommended close-comment templates.

The mayor session that produced this doc has only local commit access — it cannot close upstream issues. The operator does the closing after reviewing this doc.


The four asks, grouped

The four issues are not independent. Two pairs naturally bundle:

Pair A — broaden what gets traced (#37 + #36)

  • #37 wants explicit function entry / exit markers in the trace stream so 10x can segment one handler from the next when views and subscriptions are also traced. Today, only re-frame events are traced (via fn-traced); views and subs are out of scope.
  • #36 wants a way to opt back in to the noisy, intermediate evaluation steps that dbgn deliberately filters out. The same user who wants per-form everything would also benefit from a clear function-boundary marker.

These two are the same shape: more trace volume, with better navigation.

Pair B — environment compatibility (#35 + #34)

  • #35 wants self-hosted ClojureScript to work. Today, the macros depend on &env (compile-time bindings introspection) which is reduced or absent in self-hosted CLJS.
  • #34 wants exceptions thrown inside traced handlers to be surfaced in 10x. Today, a thrown exception truncates the trace and shows nothing; 10x renders an empty Code panel.

These two are the same shape: make tracing reliable in environments and conditions where it currently isn't.

The natural shipping order is Pair A first, Pair B second — Pair A is additive (new options on existing macros) while Pair B touches the macro substrate (env handling, error propagation) and benefits from the tests Pair A's options will require.


Pair A — design

#37: function entry/exit markers

Shipped 2026-04-27 in commit 8ba53a8 (rfd-4j9 item 3). Implementation differs from the original proposal: the marker primitives are paired -send-frame-enter! / -send-frame-exit! (not a single send-frame-marker!), and the marker shape is {:phase :enter/:exit :frame-id "frame_N" :t <ms> [:result <return>]} written onto the active trace's :tags :trace-frames vector. Enter/exit pairing uses a gensym'd :frame-id baked in at macro expansion. Off-trace markers are silently dropped. On exception, only :enter fires — consumers detect a missing :exit as a thrown invocation.

A traced function's body emits N :code entries (one per instrumented form). What it doesn't emit is a clear "this dispatch's traced section is starting" / "ending" marker. 10x's panel infers boundaries from the surrounding :event trace — fine for one handler per epoch, ambiguous when views and subs trace too.

Original proposal (preserved for design history): send-trace! gains a sibling send-frame-marker! that writes a small entry to a new :trace-frames tag — {:phase :enter / :exit, :fn-name "...", :t <ms>, :scope :event/:sub/:view/:fx}. fn-traced and defn-traced wrap their inner dbgn-forms body in a try/finally that emits enter and exit markers. 10x can opt to render frames as bracketing chrome around the existing :code payload.

Effort: ~30 LOC. The only design question is whether the markers need a stable id linking enter to exit (probably yes — gensym at macro time, attach to both). (Resolved: yes — gensym'd :frame-id shipped.)

#36: configurable verbosity

Shipped 2026-04-27 in commit 0177254 (rfd-4j9 item 4). Landed as :verbose (alias :show-all) on fn-traced / defn-traced / dbgn / parse-opts. With :verbose true, insert-trace's zipper walker stops skipping leaf literals (numbers, strings, booleans, keywords, chars, nil) at its :else branch — they surface as their own :code entries. Opaque semantic forms stay honoured even in verbose mode because instrumenting them corrupts evaluation semantics (e.g. recur out of tail position): recur, throw, catch, and finally remain :skip-form-itself-type, while comment, var, and quote are :skip-all-args-type and emit one top-level trace without descending into their arguments. Default behaviour unchanged; opt-in only.

Open question resolved: the default skip filter exists for readability, not performance — so the option landed without benchmarking.

dbgn's walker today skips intermediate "obvious" evaluations (e.g. literals, simple symbol resolutions) to reduce noise. The user request is for a mode that emits everything.

Original proposal (preserved for design history): a :verbose (or :show-all) option on fn-traced / defn-traced that suppresses the existing skip-classification for non-special forms. The classifier in cs/macro_types.cljc would honor a per-call override flag stored in *verbose-trace?* (rebound during the wrapped call). No change to the default behavior; opt-in only.

Effort: ~40 LOC across tracing.cljc (option parsing) and macro_types.cljc (flag check).

Open question: does the noise filter exist for performance reasons or readability reasons? If performance, the option needs a benchmark. If readability, the option is purely a 10x-side concern — it can land without measurement.


Pair B — design

#35: self-hosted ClojureScript support

Self-hosted CLJS has a &env that is shape-different from JVM CLJS — local-binding values aren't introspectable in the same way. The current dbgn macro reaches into &env to enumerate symbol mappings.

Proposal: isolate the &env access into a single helper (util/local-bindings &env) that branches on (if-cljs-self-hosted? ...). In self-hosted, fall back to a coarser fallback that captures names only (not values). Document the limitation.

Effort: ~50 LOC plus an integration test against a self-hosted target (e.g. lumo or planck).

Resolved 2026-04-27: declined — operator removed item 12 from the roadmap. Insufficient demand; #35 to be closed with this context. Section retained for design reference if revisited later.

#34: exception surfacing

When a fn-traced handler throws, the body's try/catch (if any) catches; otherwise re-frame's outer event handler logs and re-throws. The :code payload accumulates entries up to the throw, then nothing — 10x sees a partial trace and renders it as if it succeeded.

Proposal: wrap each fn-traced body in an outer try/catch that:

  1. Records the caught exception as a :trace-exception tag on the trace event with {:message :stack :phase :code-entry-index}.
  2. Re-throws so re-frame's normal error-handling continues.

10x renders :trace-exception as a red bar on the Code panel.

Design tension: wrapping in try/catch prevents browser DevTools from "break on exception" stopping at the throw site. The fix: gate the exception-catch on a goog-define (default true) so users who want DevTools-driven debugging can disable it.

Effort: ~60 LOC including the goog-define plumbing and a couple of tests.

Resolved 2026-04-27: declined — operator removed item 13 from the roadmap. Design tension with browser break-on-exception not worth resolving; #34 to be closed with this context. Section retained for design reference if revisited later.


Effort summary

ItemEffortRiskSuggested orderOutcome
#37 enter/exit markers~30 LOClow1✓ shipped v0.7 (8ba53a8)
#36 verbose mode~40 LOClow–medium2✓ shipped v0.7 (0177254)
#34 exception capture~60 LOCmedium (design tension)3declined 2026-04-27
#35 self-hosted CLJS~50 LOC + testmedium (compat-test infra)4 (defer if no demand)declined 2026-04-27

Outcome: the planned split landed as Pair A only. Pair A shipped as part of v0.7 (rather than the originally-proposed v0.6 milestone) alongside the rfd-4j9 quartet. Pair B was declined for the reasons captured in §35 and §34.


Cross-references with improvement-plan.md

Items in this doc map back to the original improvement-plan.md priority tiers, now recorded there as a status ledger:

  • §6 "Next month" item 6 (:locals + :if) — shipped in commit 4d6e507 (rfd-880 item 6, 2026-04-27). Tier-1 low-risk additions that benefit any tracing improvement.
  • §6 "Next month" item 7 (wrap-handler! runtime API) — shipped in commit 4ed07c9 (rfd-8g9 item 7). Adjacent to #37: an alternative way to mark function boundaries (the wrapper itself becomes the boundary). Both shipped — wrap-handler! and #37's macro-level marker now coexist; choose based on whether the boundary needs to be a runtime wrapper or a per-invocation marker.
  • §6 "Next month" item 9 (end-to-end integration test) — wired in commit 8d4e331 (rfd-880 item 9). Was a prerequisite for Pair B; with Pair B declined the fixture instead validates Pair A and the broader macro-walker suite.
  • §6 "Original someday" item 11 (fx-map tracing) — shipped in commit bae1b0d as fx-traced / defn-fx-traced, with runtime wrap-event-fx! / wrap-event-ctx! parity added later in 67181fa. This is now separate from #36's :verbose mode rather than a possible substitute for it.

Recommended close-comment text for the four issues

The mayor session can't close GH issues; the operator can paste these as close comments after reviewing this doc.

For #34

Declined 2026-04-27 (see docs/v0.6-roadmap.md Pair B § "exception surfacing"). The design tension between catching for trace surfacing vs. browser DevTools "break on exception" wasn't worth resolving for the demand seen — the design notes are preserved in the roadmap doc if this becomes a blocker. Re-open with a concrete reproduction if you need exception detail in 10x.

For #35

Declined 2026-04-27 (see docs/v0.6-roadmap.md Pair B § "self-hosted ClojureScript support"). Insufficient demand to justify the &env-shape branching plus a self-hosted target test fixture (lumo / planck). Design notes preserved for future revisit. Re-open with a concrete reproduction if self-hosted CLJS support is a blocker.

For #36

Shipped in v0.7 (commit 0177254) as a :verbose (alias :show-all) opt-in option on fn-traced / defn-traced / dbgn. Default behaviour unchanged — leaf literals are skipped as before. With :verbose true, every walked form (except evaluation-corrupting specials like recur / throw / quote) gets a :code entry. See docs/v0.6-roadmap.md Pair A § "configurable verbosity".

For #37

Shipped in v0.7 (commit 8ba53a8) as paired -send-frame-enter! / -send-frame-exit! markers written to the active trace's :tags :trace-frames vector by fn-traced / defn-traced. Each invocation gets a gensym'd :frame-id so consumers can pair enter to exit and bound the intermediate :code entries. See docs/v0.6-roadmap.md Pair A § "function entry/exit markers".


Originally framed as the v0.6 milestone scope. In practice Pair A shipped in v0.7 (commits 8ba53a8 / 0177254) and Pair B was declined; the current post-v0.7 follow-ups live in improvement-plan.md §6.


Tier-2 implementation status (rfd-8g9)

Itemimprovement-plan refStatusCommit
Item 7 — wrap-handler! / unwrap-handler! runtime API§6 item 7✓ committed4ed07c9
Item 8 — production-mode loud-fail check§6 item 8✓ committed10d27fd
Item 6 — :locals and :if options for fn-traced§6 item 6✓ shipped4d6e507
Item 9 — end-to-end integration test§6 item 9✓ wired8d4e331 (was scaffolded in 6e4f5d1)

v0.7 quartet implementation status (rfd-4j9)

Itemimprovement-plan refStatusCommit
Item 10 — :once / duplicate-suppression option§6 item 10✓ shipped33225e8
Item 11 — fx-map tracing§6 item 11✓ shipped as fx-traced / defn-fx-tracedbae1b0d
Item 12 — function entry/exit markers (#37)§6 item 12✓ shipped as :trace-frames8ba53a8
Item 13 — "Show all" verbosity mode (#36)§6 item 13✓ shipped as :verbose / :show-all0177254

Items 6 + 9 — landed post-rfd-8g9

Item 6 — shipped 2026-04-27 in commit 4d6e507 (rfd-880 item 6). :locals and :if both ride on a leading opts map after the macro name ((fn-traced {:locals true :if pred?} [args] body)). :locals captures the function args at expansion time as [[sym val] ...] pairs and attaches them to every :code entry — the capture is &env-at-expansion-only (inner let bindings are not tracked, matching the design called out in improvement-plan.md §4). :if takes a runtime predicate called with each per-form result; send-trace! fires only when the predicate returns truthy. Both options share parse-opts plumbing and shipped together as planned.

Item 9 — wired 2026-04-27 in commit 8d4e331 (rfd-880 item 9). The four ^:pending ^:integration deftests in test/day8/re_frame/integration_test.clj are now runnable: trace capture goes via direct read of @re-frame.trace/traces (not register-trace-cb — re-frame's debounce path is unusable on CLJ because it calls (f) immediately, binding schedule-debounce to the reset! return value rather than a fn, which throws ArityException). schedule-debounce is stubbed to a no-op via with-redefs. The -e :integration -e :pending excludes were dropped from the :test-clj alias's main-opts so bb test runs the suite. The wiring also surfaced and fixed two latent dbgn macro-walker bugs (o-skip :else nav landed on (recur ...); remove-skip didn't re-evaluate after replace).

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close