Run codegen

Codegen keeps the lexicons (under lexicons/dev/idiolect/) the single source of truth. Anything that lives downstream of a lexicon (Rust types, TypeScript validators, family modules, spec-driven HTTP routes and CLI subcommands) is regenerated, not hand-edited.

The crate is idiolect-codegen. It runs as a workspace binary (cargo run -p idiolect-codegen).

Invocation

cargo run -p idiolect-codegen           # write the generated tree
cargo run -p idiolect-codegen -- --check # verify no drift

The default mode regenerates. The --check flag re-derives in memory and byte-compares against the working tree, exiting non-zero on any drift. CI runs --check on every PR.

What gets emitted

OutputSourceWhere
Per-record Rust typeslexicons/dev/idiolect/*.json and the vendored lexicons/dev/panproto/*crates/idiolect-records/src/generated/
idiolect-records family modulethe shipped lexiconscrates/idiolect-records/src/generated/family.rs
Per-record fixtures (Rust)lexicons/dev/idiolect/examples/*.jsoncrates/idiolect-records/src/generated/examples.rs
TypeScript validators + typessame lexiconspackages/schema/src/generated/
Orchestrator HTTP routesorchestrator-spec/queries.jsoncrates/idiolect-orchestrator/src/generated/
Orchestrator CLI dispatcherorchestrator-spec/queries.jsoncrates/idiolect-cli/src/generated.rs
Observer method descriptorsobserver-spec/methods.jsoncrates/idiolect-observer/src/generated.rs
Verifier kind taxonomyverify-spec/runners.jsoncrates/idiolect-verify/src/generated.rs

The three spec files are single JSON documents (not directories). Each declares an array of entries that codegen reads.

The drift gate

--check is the drift gate. CI runs it on every PR. A red gate means somebody edited a lexicon (or a spec) without rerunning the default mode, or hand-edited a generated file. Both are fixable by running cargo run -p idiolect-codegen.

Adding a new lexicon

  1. Drop a JSON document under lexicons/dev/idiolect/.
  2. Run cargo run -p idiolect-codegen.
  3. Optional: add a fixture under lexicons/dev/idiolect/examples/<name>.json so the examples module emits a typed accessor.
  4. Re-run the workspace tests to confirm the family round-trips.

The codegen rejects malformed lexicons (NSID violations, unresolved references) before emitting, so a broken lexicon errors at this step rather than at compile time.

Adding a new spec entry

The orchestrator query, observer method, and verifier runner specs each carry a different shape. The pattern is the same:

  1. Add a JSON entry to the spec file.
  2. Run cargo run -p idiolect-codegen.
  3. Implement the hand-written half (the panproto-expr predicate for an orchestrator query, the ObservationMethod impl for an observer method, the VerificationRunner impl for a verifier runner).

The dispatcher routes to the new entry by its kind. The generated tree handles parsing and shape.

Library API

For consumers outside the workspace, the emitter is callable as a library:

#![allow(unused)]
fn main() {
use idiolect_codegen::emit::{emit_rust, emit_typescript};
use idiolect_codegen::emit::family::{FamilyConfig, idiolect_family};
}

emit_rust(docs, examples, family) and emit_typescript(docs, examples, family) take pre-loaded LexiconDoc and Example slices plus a FamilyConfig, and return a Vec<EmittedFile>. Loading the lexicons from disk is the caller's job.

FamilyConfig::new(marker_name, id, nsid_prefix) constructs a config from any string-like inputs. The shipped default for dev.idiolect.* is idiolect_family().

The crate is publish = false and not on crates.io. Downstream consumers depend on it via a path or git reference rather than a registry version.