Output capture: the root *out* and *err* tees and the per-eval banks
they fill.
A tee sits at the one point every unbound deref of its var arrives at —
the var's root — and discriminates per write, not per install: every
write passes through to the original writer unchanged, and writes whose
thread carries a bank in scope are additionally copied into that bank.
It never diverts, never withholds, never attributes by wall-clock
coincidence (§11). Host threads that bound *out* or *err* themselves
never reach the root and never see the tee.
Both streams fill the same bank, interleaved in arrival order and
unmarked — chronology beats channel separation, as in a terminal — so
what *err* carries reaches the reader at last: warnings (a refer
rejected in favor of an interned def says so only here — measured
2026-08-05, invisible then), reflection notes, stack traces from
stray threads, anything host code mutters.
Output capture: the root `*out*` and `*err*` tees and the per-eval banks they fill. A tee sits at the one point every unbound deref of its var arrives at — the var's root — and discriminates per write, not per install: every write passes through to the original writer unchanged, and writes whose thread carries a bank in scope are additionally copied into that bank. It never diverts, never withholds, never attributes by wall-clock coincidence (§11). Host threads that bound `*out*` or `*err*` themselves never reach the root and never see the tee. Both streams fill the *same* bank, interleaved in arrival order and unmarked — chronology beats channel separation, as in a terminal — so what `*err*` carries reaches the reader at last: warnings (a refer rejected in favor of an interned def says so only here — measured 2026-08-05, invisible then), reflection notes, stack traces from stray threads, anything host code mutters.
The attribution key. Rides two rails (§11): scoping around eval-chain
construction, which Quiescent re-establishes around every descendant task
body — and a plain per-link binding, which Clojure conveyance carries
into future/agent/bound-fn. One sc/ask covers both, because its
fallback is a var deref and var derefs read thread bindings (measured
2026-08-04). Never bound outside those two sites.
The attribution key. Rides two rails (§11): `scoping` around eval-chain construction, which Quiescent re-establishes around every descendant task body — and a plain per-link `binding`, which Clojure conveyance carries into `future`/`agent`/`bound-fn`. One `sc/ask` covers both, because its fallback is a var deref and var derefs read thread bindings (measured 2026-08-04). Never bound outside those two sites.
(bank)A fresh bank: the tail of what one eval printed, plus how much has ever arrived, so trimming stays announceable.
A fresh bank: the tail of what one eval printed, plus how much has ever arrived, so trimming stays announceable.
(deposit! bank s)Append s to bank, trimming to the cap from the front.
Append `s` to `bank`, trimming to the cap from the front.
(install!)Swap the tees in at the roots of *out* and *err*. Idempotent; called
by init!, so a host that never inits never has its roots touched.
getRawRoot, not the deref: install runs on a caller's thread, where
either var may well be thread-bound to something that is not the root
at all.
Swap the tees in at the roots of `*out*` and `*err*`. Idempotent; called by `init!`, so a host that never inits never has its roots touched. `getRawRoot`, not the deref: install runs on a caller's thread, where either var may well be thread-bound to something that is not the root at all.
(since bank from)What arrived after position from (a cumulative-character count), as
[text total] — the caller holds total as its next cursor. This is
the heartbeat's read (§4): each beat drains what the eval said since the
last one. A from the cap already trimmed past gets the drop announced,
same as text.
What arrived after position `from` (a cumulative-character count), as `[text total]` — the caller holds `total` as its next cursor. This is the heartbeat's read (§4): each beat drains what the eval said since the last one. A `from` the cap already trimmed past gets the drop announced, same as `text`.
(text bank)Everything bank holds, with the drop announced when the cap trimmed —
a tail that silently posed as the whole output would be §5's nasty
failure mode wearing a different hat.
Everything `bank` holds, with the drop announced when the cap trimmed — a tail that silently posed as the whole output would be §5's nasty failure mode wearing a different hat.
(uninstall!)Restore the original root writers. Safe to call when not installed.
Restore the original root writers. Safe to call when not installed.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |