Configuration & docs — no undocumented knobs
A hard lesson from OpenLane/LibreLane: a global, flat namespace of hundreds of config
variables, set through env/config.json, applied across steps — so you can’t tell which
step reads which variable, what its impact is, or when it applies. Loom is designed so
that failure mode is structurally impossible. This page is the convention every Loom step and
engine follows.
The three failure modes we reject
| OpenLane/LibreLane | Loom |
|---|---|
| Global, flat env-var namespace | Per-step, typed config — scoped to the op that reads it |
| “Which var does this step use?” is unclear | Config is the step’s typed struct — the surface is finite and local |
| Impact / when-it-applies undocumented | Every field documents impact + when, and docs are generated from the code |
The five rules
- No global env vars for behavior. A step’s configuration is a typed struct (Rust +
serde), passed as JSON or as typed Flow IR. Environment variables are for environment (paths, credentials) — never behavior knobs. - Every field is documented in place — a doc-comment stating what it does, its impact, when it applies, and its default. No field ships without this.
- Every step self-describes. Each step/engine emits its config as a JSON Schema via
--describe(the same pattern asvyges-events --schemaandvyges metadata). The schema lives next to the struct, so it cannot drift from behavior. One owner, no drift. - Docs are generated from
--describe, not hand-written. The mdbook reference page for a step is generated from its schema — name, type, default, description, impact, example. Regenerated in CI; drift fails the build. - There is a config catalog. One generated, searchable page lists every knob across every step — the index OpenLane never had — plus a worked example per step.
How config flows
typed config struct ──(--describe)──▶ JSON Schema ──▶ generated mdbook reference
(serde + doc-comments) (self-published) + config catalog
│ ▲
└──── consumed by the step (and by the flow layer) ────────┘
The typed struct is the single source of truth. It is what the step deserializes at run time,
what --describe publishes, and what the flow layer validates against. Documentation is a
projection of it — so it is always complete and always current.
Example — the insert-eco-buffers step
Config is a typed struct, LibreLane-compatible in shape:
#![allow(unused)]
fn main() {
/// One entry of `INSERT_ECO_BUFFERS`.
pub struct EcoBuffer {
/// `"instance/pin"` — the pin to buffer. Impact: a buffer is spliced here.
/// When: applied once, during the ECO surgery step (before legalization).
pub target: String,
/// Buffer master cell name. Impact: which library cell is inserted.
pub buffer: String,
}
}
Invoked as a step with an explicit, worked example (never a bare env var):
vyges-opendb insert-eco-buffers --input in.odb --output out.odb --config eco.json
# eco.json: { "INSERT_ECO_BUFFERS": [ { "target": "inst42/A", "buffer": "sky130_fd_sc_hd__buf_2" } ] }
What this buys a user
- Discoverability:
--describetells you exactly what a step accepts — no source-diving. - Locality: a knob’s impact is the op that owns it, not a global side effect.
- Currency: docs (defaults, impacts, examples) are generated, so they never rot.
- Composition: the flow layer carries per-op config validated against the schema, so “what to use when” is answered by the flow’s structure — not env-var archaeology.
Status
The generated half is live. Every engine’s CLI reference page is produced from both
--help (the surface: subcommands and flags) and --describe (the contract: typed config, input
schemas, maturity, provenance) — neither alone is enough, since an engine’s --describe usually
covers one primary operation while its --help covers all of them. Multi-step tools are walked
step by step, so each step’s config keys appear with their types and descriptions.
A step that publishes no contract is named on its own page rather than quietly omitted: a gap a reader can see is a gap someone can report. That is the standing convention in force — no step ships an undocumented knob.