Skip to content

Economy as code + the why layer (loops, attribution, phase portraits); relicense to AGPL-3.0#38

Merged
zntznt merged 6 commits into
mainfrom
claude/machinations-io-clone-CjGNg
Jul 16, 2026
Merged

Economy as code + the why layer (loops, attribution, phase portraits); relicense to AGPL-3.0#38
zntznt merged 6 commits into
mainfrom
claude/machinations-io-clone-CjGNg

Conversation

@zntznt

@zntznt zntznt commented Jul 16, 2026

Copy link
Copy Markdown
Owner

What

Two feature arcs on top of main, plus a relicense:

Economy as code — the diagram becomes a first-class code artifact:

  • .econ text format (js/dsl.js): readable, diff-friendly, lives in git. Lossless round-trip with the canvas (serialize→parse→serialize is a byte-identical fixpoint; a kitchen-sink fixture covering every node/connection feature guards it). Sugar for the common cases, generic key=value attrs diffed against constructor defaults for everything else, so new model fields serialize automatically. File menu: Save as text / Open reads both formats.
  • Design tests / assertions (js/assertions.js): always gold < 500, eventually score >= 100, at step 25: queue <= 3, checked per step. Saved with the diagram (Checks rail panel, assert lines in .econ), run in-app against a single run or every Monte Carlo trial, and in CI via cli.js --check / --assert (exit code 2 on failure, --pass-rate for stochastic tolerance).
  • JS-module export (js/codegen.js): File → Export as JS module (or --emit) bundles model + engine + diagram into one dependency-free UMD file with a createEconomy() API (step/run/get/set/fire/values/onStep, seed + param overrides). The balanced economy is the shipped economy.
  • CLI conversions: --to-dsl / --to-json; .econ input accepted everywhere.

The why layer — the app explains behavior, not just plots it:

  • Feedback-loop detection (js/loops.js): every elementary cycle in the causal graph (flows, triggers, activators, modifiers, plus implicit formula-read edges signed by numeric probing with a pinned RNG), classified reinforcing / balancing / circulation / unclear. Loops rail panel with canvas spotlight; cli.js --loops.
  • Spike attribution (js/attribution.js): click any timeline point to see why it changed — per-connection inflows/outflows, applied modifier deltas, honest residual. The engine now attaches per-span flow records to history entries, merged forward through stride decimation so delta = in − out + mods + residual holds exactly on long runs (unit-tested across 1500 steps and two stride doublings). History gains a step-0 baseline. cli.js --why "Gold@120".
  • Phase portraits: new on-canvas chart style plotting node A vs node B in state space with a fading trajectory and 2D-snapping hover; the predator-prey demo ships one (its limit cycle draws itself as an orbit).

License: relicensed MIT → AGPL-3.0 (canonical text in LICENSE, SPDX id in package.json, README section).

Architecture

All six new modules (dsl, assertions, codegen, loops, attribution + engine hooks) follow the repo's DOM-free contract: same files load in the browser, cli.js, and test/run.js. Docs in docs/ECONOMY_AS_CODE.md; CLAUDE.md and KB articles updated alongside.

Tests

  • 229/229 unit tests (about 30 new): DSL round-trip fixpoint + kitchen sink, assertion semantics + Monte Carlo hooks, generated-module runs under Node with seed determinism, loop classification (both probe slopes, caps, dedup), the attribution decimation invariant, and CLI end-to-end including exit codes.
  • Browser smoke test passes (no console/page errors), with new checks driving the Checks panel, Loops spotlight, why-popover, and phase-portrait rendering in the real app.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL


Generated by Claude Code

claude added 6 commits July 16, 2026 18:51
Full-codebase lint sweep. Two findings fixed:
- test/smoke.js accepts a SMOKE_CHROMIUM env var to point the launch at a
  pre-installed Chromium binary, for sandboxes where Playwright's browser
  download is unavailable. Default behavior (and CI) unchanged.
- js/app-props.js queue-metrics comment still described the old em-dash
  placeholder; the code shows "n/a".

Everything else came back clean: syntax on all JS files, no curly-quote
delimiters, em dashes only in code comments (user-facing copy clean), no
console.log/debugger/TODO leftovers, no trailing whitespace or tabs, CSS
uses tokens outside :root, index.html script order intact (mixins after
app.js), model/engine still DOM-free. 197/197 unit tests and the browser
smoke test pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
Three DOM-free additions that make an economy a first-class code artifact,
documented in docs/ECONOMY_AS_CODE.md:

- js/dsl.js: the .econ text format. dslSerialize/dslParse round-trip the
  diagram JSON losslessly (serialize-parse-serialize is a byte-identical
  fixpoint; normalizeEconJSON defines semantic equality up to id renaming).
  Sugar for the common cases, generic key=value attrs diffed against
  constructor defaults for everything else, so new model fields serialize
  automatically. File menu: Save as text (.econ); Open file now reads both
  formats.

- js/assertions.js: design tests over a run. Quantifiers always / never /
  eventually / at end / at step N over the existing formula language, with
  node labels (sanitized), diagram variables and step in scope. CLI flags
  --assert, --assert-file, --pass-rate; exit code 2 on failure. Monte Carlo
  trials are checked individually via new perStep/onTrialEnd hooks in
  runMonteCarlo.

- js/codegen.js: buildEconomyModule bundles model + engine + diagram into a
  dependency-free UMD file with a createEconomy() API (step/run/get/set/
  fire/values/onStep, seed + param overrides). File menu: Export as JS
  module; CLI: --emit out.js. CLI also converts formats with --to-dsl /
  --to-json and accepts .econ input everywhere.

Docs: README economy-as-code sections, CLAUDE.md architecture + commands,
three KB articles. Tests: 15 new unit tests (round-trip kitchen sink,
fixpoint, sugar, assertions semantics, generated-module runs under Node,
CLI end-to-end including exit codes) and a smoke check for the in-page
round trip, codegen fetch path, menu items and KB category. 212/212 unit
tests and the browser smoke test pass.

Also relicense the project from MIT to AGPL-3.0: canonical license text in
LICENSE, SPDX id in package.json, License section in README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
Assertions graduate from CLI-only flags to a first-class diagram feature:

- Diagram.assertions (array of assertion strings) serializes with the file,
  round-trips through .econ as `assert "..."` directives, and is covered by
  the kitchen-sink round-trip fixture.
- New Checks rail panel (js/app-analysis.js): edit the saved checks with live
  parse validation, then verify them against a fresh isolated run (Check
  once) or inside every trial of a Monte Carlo batch (Check batch, async
  with progress). Results render as PASS/FAIL rows with failure details and
  per-assertion fail counts across runs. The live canvas state is never
  touched; a seeded clone never leaks its RNG stream into the session.
- cli.js --check runs the suite saved in the diagram (plus any --assert
  extras), so a .econ file carries its own tests: `node cli.js economy.econ
  --check` is a complete CI job.
- KB article updated for the panel, docs (README, ECONOMY_AS_CODE.md,
  CLAUDE.md) extended.

Tests: unit coverage for the assert directive, Diagram serialization and
--check exit codes (213/213 pass); smoke coverage for the panel render,
single + Monte Carlo checking and .econ serialization. Browser smoke passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
js/loops.js (DOM-free): detectLoops(diagram) enumerates every elementary
cycle in the causal graph and classifies it by the system-dynamics polarity
rule: reinforcing (R, even negative links), balancing (B, odd), pure
resource circulation (F) or unclear (?).

The causal graph goes beyond the drawn arrows: flows and triggers are
positive links, reverse triggers negative, activators take their operator's
sign, modifiers the sign of their amount (mode-aware: step-mode modifiers
carry no source influence), and formula reads (register formulas, rate/
weight/modifier formulas, variable conditions) become implicit edges from
the publishing node, signed by a numeric probe of the formula with the RNG
pinned so stochastic formulas probe deterministically. Parallel edges
collapse per node pair; enumeration is a bounded smallest-start DFS with
maxLoops/maxLen caps plus an explored-edge budget, reporting truncation.

UI: new Loops rail panel lists loops (badge, node chain, per-link signs)
and clicking one spotlights it on the canvas via a new renderer emphasis
pass that fades non-members; leaving the panel clears it. CLI: --loops
prints the same table. KB article, README and CLAUDE.md updated.

Sanity-checked against the bundled demos: the business cycle surfaces its
deposit-compounding R loops and the GDP-investment accelerator, the auction
economy shows its just-in-time production gates as B loops (66 ms on the
largest demo).

Tests: 8 new unit tests (self-loops by factor sign, sign multiplication,
F circulations, operator-signed activators, numeric formula probes both
slopes, trigger/reverse-trigger, dedup + caps + truncation, CLI output)
and a smoke check driving the panel, spotlight and clear path. 221/221
unit tests and the browser smoke test pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
…d it)

Engine now records the data attribution needs:
- Every history entry carries `flows`: resource amounts per connection and
  applied modifier deltas per connection, accumulated over the entry's whole
  span. Stride decimation merges dropped entries' flows forward and folds
  trailing spans into the live accumulator, so the identity
  delta = inflows - outflows + modifiers + residual holds exactly on long
  runs. Modifier recording uses APPLIED amounts (capacity clamps, empty
  takes). Interactive fires between steps accumulate into the open span.
- reset() records a step-0 baseline entry, giving charts/scrub the run start
  and attribution a "from" value for the first step. Checkpoints carry the
  open span accumulator.

js/attribution.js (DOM-free): attributeChange() builds the ranked breakdown
with node-type-aware semantics (drains only take, limited sources only give,
registers recompute from formulas), a residual line for internal behavior
(conversions, queue balk/renege) and float-dust guarding.

UI: plain click on the timeline (drag still compares, click still clears a
comparison) picks the nearest series point and opens a why-popover: value
span, delta, signed rows per contribution, residual, and a canvas spotlight
of the contributing connections while open; Esc or click-away closes and
restores any prior emphasis. CLI: --why "Gold@120" (repeatable, "Gold" =
final step) prints the same table after a single run.

Docs: KB article, README analysis bullet, CLAUDE.md. Two pre-existing
history-length expectations updated for the step-0 baseline (checkpoint
truncation unit test, CSV smoke row count).

Tests: 7 new unit tests including the decimation invariant (1500-step run,
every entry's flows sum exactly to its snap delta) and CLI --why end-to-end;
smoke drives the popover, spotlight and Escape path. 228/228 unit tests and
the browser smoke test pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
New chartType 'phase' on on-canvas charts: the first tracked node is the x
axis, the second the y axis, and the run traces a trajectory through state
space. Older segments fade (about 12 opacity-ramped chunks, cheap to
rebuild), a hollow dot marks the start and a solid one the live end, axis
extents show the raw data range and axis labels take their node's series
color. Limit cycles read as orbits, equilibria as inward spirals, runaway
growth as a curve off the corner.

Hover snaps to the nearest trajectory point in 2D (time charts keep their
x-only mapping) and reads out step + both values. The chart style picker
gains a Phase chip with an explanatory note, and the predator-prey demo now
ships a phase portrait beside its time chart, where the limit cycle draws
itself as an orbit.

Serialization and .econ round-trip come free via the existing chartType
field (type=phase). KB and README updated.

Tests: unit round-trip for the type through JSON and .econ; smoke renders
the portrait (chunks, markers, colored axis labels), verifies the 2D hover
snap and that demo 1 includes one. 229/229 unit tests and the browser smoke
test pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTK8CoB2M6ofpYRXpQEVVL
@zntznt
zntznt merged commit 8a828e1 into main Jul 16, 2026
1 check passed
@zntznt
zntznt deleted the claude/machinations-io-clone-CjGNg branch July 16, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants