Test the craft of the function. The datamancer holds the code to the fire — Hickey's heat removes impurity, Beckman's hammer tests composition. What survives the forge is well-made.
A forged blade is shaped by repeated heating and hammering. Each strike removes impurity. Each fold strengthens the metal.
The other wards ask: is it tangled? Is it alive? Is it true? Is it beautiful? The forge asks: is it well-made?
Two lenses in one fire. Rich Hickey removes what doesn't belong. Brian Beckman tests whether what remains holds together. The forge is both acts at once.
Data should flow through functions, not mutate in place. A function that takes data and returns data is forged. A function that reaches into shared state, mutates it, and returns nothing is cast.
self for things that should be arguments?&mut self should mutate self, not the world.The type signature should be the contract. If you can call the function wrong, the forge failed.
f64 where it should take a newtype? (DrawdownDepth(f64) vs bare f64)&str where it should take an enum? Direction as a string is a lie. Direction as Direction::Long is a truth.Option<T> says "might fail." Result<T, E> says "might fail, and here's why." T says "always succeeds" — is that actually true?Not too many. Not too few. The right abstraction is one that earns its existence.
f64 is false generality. Specialize it.Hickey: "Every new thing you add has a cost. Is this worth it?" Beckman: "Does this abstraction close? Can I compose through it?"
Functions compose when the output of one is the input of the next. The forge checks the joints.
&ManagerContext and reading 3 of 12 fields is honest — the context is immutable, assembled by the caller, and the function projects what it needs. That's the wat pattern: pass ctx, project with :field. Destructuring the context into bare parameters at the call site makes the caller noisier and the function less composable (adding a field changes the signature AND all call sites). Hidden coupling is different: reaching into &mut self or global state the caller doesn't see. THAT is claiming a bigger world.transducer → functor → fold should be A → B → C with clean boundaries. If stage 2 reaches back into stage 1's state, the composition is broken.It takes data in. It returns data out. The types say what it does. The name says why. It composes with its neighbors without knowing them. It can be tested alone. It does one thing. It does it completely.
A forged function is one you can hold up to both lenses simultaneously — Hickey nods "nothing unnecessary" and Beckman nods "it composes" — and neither flinches.
Read the target file (default: src/state.rs). For each function, each method, each closure:
Report findings as: the function, what the forge reveals, and what a well-forged version would look like. Not a rewrite — a direction.
Skip findings annotated with rune:forge(category) in a comment at the site. The annotation must include a reason after the dash. Report the rune so the human knows it exists, but don't flag it as a finding.
Runes suppress bad thoughts without denying their presence. A rune tells the ward: the datamancer has been here. This is conscious.
// rune:forge(escape) — the fold's IO was extracted; this coupling is measured
Categories: escape (algebraic leak is known and measured), coupling (hidden dependency is intentional), bare-type (using f64/str instead of newtype is acceptable here), premature (single-use helper earns its place for clarity).
Hickey said: simplicity is a prerequisite for reliability. Beckman said: composability is a prerequisite for understanding. The forge tests both. A well-forged function is simple AND composable. It survives the fire of "what if I use this in a context the author didn't imagine?" Because a forged function doesn't know its context. It knows its inputs and its outputs. Everything else is someone else's concern.
The datamancer forges. The function survives or it doesn't. The forge is the crucible where Rich's heat and Brian's hammer meet.