| title | Formulas & Molecules |
|---|
Last verified against code: 2026-03-17
Formula files are reusable workflow definitions stored as
*.formula.toml. Gas City resolves those files through ordered formula
layers, stages the active winners into .beads/formulas/, and asks the
configured beads backend to instantiate molecules from them.
Current merge-wave status:
- The in-flight Pack/City v2 merge still uses
*.formula.tomlandorders/*.order.toml. - We decided to remove the
.formula./.order.infix after the merge, not during it. - That follow-up is tracked in gastownhall/gascity#586.
The important current-state boundary is this:
- Gas City owns formula discovery and layer resolution.
- The beads backend owns formula materialization.
bdis the full-featured backend for real formula execution today.
- Formula file: A
*.formula.tomlfile selected through formula layers. This is the current on-disk naming; simplification is tracked separately in#586. - Formula layers: Ordered directories computed from packs, city config, and rig config. Higher-priority layers shadow lower-priority files by name.
- Molecule: A runtime instance created from a formula.
- Wisp: An ephemeral molecule created for dispatch or order execution.
- Attached molecule: A formula instantiated onto an existing bead via
Store.MolCookOn. - Convergence formula subset: The subset of formula metadata used by the
convergence subsystem, validated in
internal/convergence/formula.go.
formula layers
from config + packs
|
v
ResolveFormulas()
cmd/gc/formula_resolve.go
|
v
.beads/formulas/*.formula.toml
|
v
Store.MolCook / Store.MolCookOn
|
+--> BdStore -> bd mol wisp / bd mol bond
+--> exec.Store -> script mol-cook / mol-cook-on
\--> Mem/File -> simplified molecule root for tests/tutorials
ComputeFormulaLayers() in internal/config/pack.go computes the ordered
layer set for the city and each rig. ResolveFormulas() in
cmd/gc/formula_resolve.go then:
- Scans each layer for
*.formula.toml - Keeps the highest-priority winner for each filename
- Symlinks winners into
<target>/.beads/formulas/ - Removes stale formula symlinks without touching real files
This keeps the active formula set visible to backend tools such as bd.
The store interface is the runtime seam:
Store.MolCook(formula, title, vars)creates a new molecule or wispStore.MolCookOn(formula, beadID, title, vars)attaches a molecule to an existing bead
Current implementations behave as follows:
BdStoreininternal/beads/bdstore.godelegates tobd mol wispandbd mol bond, then parses the returned root bead ID.exec.Storeininternal/beads/exec/exec.goforwardsmol-cookandmol-cook-onto a user script.MemStoreandFileStorecreate a simplified molecule root bead. They are suitable for tests and tutorials, not full formula execution.
Formulas are consumed in two main places:
cmd/gc/cmd_sling.gocreates wisps duringgc sling --formulaand attached molecules via--on.cmd/gc/order_dispatch.gocreates wisps when formula-backed orders fire. In the current merge wave, orders are discovered fromorders/*.order.toml; removal of the.order.infix is deferred to#586.
Closed wisps are purged by the controller's wisp GC in
cmd/gc/wisp_gc.go. The interval and TTL come from
[daemon].wisp_gc_interval and [daemon].wisp_ttl.
- Formula resolution is last-wins by filename across ordered layers.
ResolveFormulas()only mutates symlinks under.beads/formulas/; it never overwrites real files.- Molecule creation always goes through the configured
beads.Store. - Full multi-step formula execution is backend-dependent today;
BdStoreis the production path. - Wisp garbage collection only targets closed molecules past the configured TTL.
| Depends on | How |
|---|---|
internal/config |
Computes formula layers from city, packs, and rigs |
internal/beads |
Instantiates formulas via MolCook and MolCookOn |
internal/convergence |
Validates convergence-specific formula metadata |
| Depended on by | How |
|---|---|
cmd/gc/cmd_sling.go |
Creates wisps and attached molecules from formulas |
cmd/gc/order_dispatch.go |
Fires formula-backed orders |
cmd/gc/wisp_gc.go |
Purges expired closed molecules |
| Contributor docs | Reference formula layout and resolution behavior |
| Path | Responsibility |
|---|---|
cmd/gc/formula_resolve.go |
Layer winner selection and symlink staging |
cmd/gc/cmd_sling.go |
Formula-backed sling and attached-molecule flows |
cmd/gc/order_dispatch.go |
Formula-backed order dispatch |
cmd/gc/wisp_gc.go |
TTL-based cleanup for closed molecules |
internal/config/config.go |
FormulaLayers data shape |
internal/config/pack.go |
ComputeFormulaLayers() |
internal/beads/beads.go |
MolCook / MolCookOn store interface |
internal/beads/bdstore.go |
Production formula instantiation via bd |
internal/beads/exec/exec.go |
Script-backed formula instantiation |
internal/beads/memstore.go |
Simplified in-memory molecule creation |
internal/beads/filestore.go |
Persistent wrapper over MemStore |
internal/convergence/formula.go |
Convergence-specific formula validation |
Formula layers are assembled from:
- city packs
[formulas].dirincity.toml- rig packs
[[rigs]].formulas_dir
Wisp cleanup is configured in city.toml:
[daemon]
wisp_gc_interval = "5m"
wisp_ttl = "24h"See Formula Files for the file format itself.
cmd/gc/formula_resolve_test.goverifies winner selection, stale cleanup, and real-file preservationinternal/beads/bdstore_test.goverifiesbd mol wisp/bd mol bondwiring and root ID parsinginternal/beads/memstore_test.goandinternal/beads/filestore_test.goverify simplified molecule creation for test-oriented storescmd/gc/order_dispatch_test.goandcmd/gc/cmd_sling_test.gocover the higher-level formula dispatch paths
- Gas City does not currently own a general in-process formula parser for the main runtime path.
- Step-bead materialization is backend-dependent; production behavior comes
from
bd. - Tutorial and in-memory stores intentionally implement a smaller molecule model than the production backend.
- Formula Files for the file layout
- Dispatch for sling-based formula routing
- Orders for formula-backed scheduled work
- Bead Store for the
MolCookinterface boundary