Changelog¶
All notable changes to Noether are documented here.
Format follows Keep a Changelog. Noether uses Semantic Versioning.
Pre-0.2 internal numbering. Before reaching crates.io, Noether used a sequential
0.1.0 … 0.6.0internal numbering tracked in this file. The first public release to crates.io is 0.2.1; public numbering resets there. The pre-public entries are kept below under Pre-release history for reference — don't map them to crates.io versions.
[0.4.1] — 2026-04-16¶
DX release: two ergonomic frictions that surfaced while building a realistic telemetry pipeline (see /home/alpibru/workspace/noether-telemetry for the exercise that drove these) are now fixed. Backward-compatible for the type system; additive for the graph resolver. No change to stage content hashes — existing stages and compositions keep their identities.
Changed¶
- Nullable Record fields are now optional. A field declared
T | Null(orNull, orAny) in a record type is treated as optional — the value may omit the key entirely instead of being required to include it with a null. Type-checker rule:is_subtype_of(Record{…}, Record{field: T | Null})now accepts records that don't containfield. Non-nullable fields remain strictly required. Motivation: config-like stage inputs (threshold_pct,peak_power_kwp) previously had to be carried through every upstream stage's output schema, becauseT | Nullmeant "present, possibly null" not "may be absent." The new semantics line up with how JSON Schema + TypeScript + pydantic treat nullable fields.
Added¶
- Stage lookup by
namein graph references. Composition graphs can now write{"op": "Stage", "id": "volvo_map"}— the graph loader resolves the string against the store by name when it doesn't match an ID prefix. Active stages win over Draft / Deprecated; duplicates across Active lifecycles are still an explicitAmbiguouserror. Previously, graphs had to paste the 8-char content-hash prefix thatstage addemitted. Existing hex-prefix references continue to work — the new behaviour only kicks in when the reference isn't a valid hex prefix of any stored stage. Stage.name: Option<String>field, populated automatically from the spec's top-levelname. Not part of the content hash — changing the human-authored name doesn't change the stage's identity. Two stages with the same name but different types remain distinct identities (the resolver errors on ambiguity rather than guessing).StageStore::find_by_name(name)default method — returns all stages with a matchingnamefield, across all lifecycles. Built onlist(), so every store impl gets it for free.
Migration¶
Nothing breaks. Existing stage specs, composition graphs, and stores from v0.4.0 load and run identically. Two optional cleanups you can make to take advantage:
- Drop the carried-through config fields from intermediate stage outputs. A downstream stage that declares
field: Number | Nullno longer requires every upstream stage to include that field. - Rewrite graph references from hex prefixes to names:
{"id": "abc12345"}→{"id": "<spec-name>"}.
[0.4.0] — 2026-04-15¶
First release with noether-grid: a broker + worker pair that pools LLM capacity across machines. Three new binaries ship alongside the existing noether / noether-scheduler: noether-grid-broker, noether-grid-worker, and the noether-grid-protocol crate they share.
See crates/noether-grid-broker/README.md for the per-role deploy walkthrough and docs/research/grid.md for the design.
Added¶
noether-grid-broker— pools worker capacity, splits Lagrange graphs soEffect::Llmstages dispatch to a worker with matching capability while pure stages execute locally. Retry-with-exclusion on worker failure, optional postgres write-through persistence (--features postgres), self-contained HTML dashboard at/, Prometheus metrics at/metrics, per-agent quotas via--quotas-file.noether-grid-worker— enrols with a broker, advertises its LLM capabilities, serves/execute(full graph) and/stage/{id}(single-stage,RemoteStage-compatible). Auto-discovers four subscription CLIs (Claude, Gemini, Cursor Agent, OpenCode) plus API-key providers (Anthropic, OpenAI, Mistral, Vertex AI).- Subscription-CLI support in
noether-engine. Newcrate::llm::cli_providermodule generalises over Claude Desktop, Gemini CLI, Cursor Agent, and OpenCode. Opt in viaNOETHER_LLM_PROVIDER={claude-cli,gemini-cli,cursor-cli,opencode}or auto-detect when no API key is set. Suppress viaNOETHER_LLM_SKIP_CLI=1for sandboxed environments. RemoteStageerror surface now propagates the remote worker'sok: false, error: <msg>verbatim instead of masking it as "missing data.output field".- Three research notes in
docs/research/:grid.md(design),grid-capabilities.md(future generalisation beyond LLMs),llm-here.md(planned consolidation with caloron's_llm.pyand agentspec's resolver).
Migration¶
You only need to migrate if you adopt noether-grid. The noether CLI, stdlib, scheduler, and graph format are unchanged.
If you deploy grid:
-
Store path must match on broker and all workers. Both
noether-grid-brokerandnoether-grid-workerdefault their--store-pathto$HOME/.noether/store.json(matching the CLI'snoether_dir()). Previous prerelease builds of grid used a CWD-relative.noether/store.jsondefault, which silently diverged when the broker and worker were launched from different directories. If you pinned an earlier grid build and relied on that behaviour, setNOETHER_STORE_PATHexplicitly — or nothing, and let the new$HOMEdefault apply. -
Subscription CLIs are auto-detected by default. Running grid on a machine with
claude/gemini/cursor-agent/opencodeon$PATHnow advertises each as pooled capacity. If you want a headless worker that ignores ambient CLI auth (e.g. a CI runner, a Nix-sandboxed executor), setNOETHER_LLM_SKIP_CLI=1. -
Bare-string
"llm"effects now route. A stage declaring"effects": ["llm"]parses asEffect::Llm { model: "unknown" }and dispatches to any worker with any LLM capability. Previous behaviour was to refuse routing withno worker matches ["unknown"]. If you previously worked around this by declaringEffect::Llm { model: "<specific>" }, nothing changes — exact-model match still wins when the model is set.
Fixed¶
jobs_failed_totalnow increments on the splitter-refusal terminal path (it previously only counted post-dispatch failures).- Worker capability probing logs the resolved path per subscription CLI at
INFO, so a silent zero-capabilities advertisement surfaces its cause instead of requiring out-of-band debugging. - Broker logs the resolved store path + stage count at boot and warns loudly when the seeded catalogue looks small (<20 extra stages).
Known caveats (not blockers)¶
- Cost model today assumes metered APIs — subscription-path jobs report
cents_spent_total = 0. Capacity-based metrics (jobs_routed_total{provider},capacity_used_ratio) are the v0.4.1 plan; seedocs/research/grid-capabilities.md. - Cross-machine + multi-seat fan-out is implemented and unit-tested, but has not been piloted on production hardware as of this release. The MVP pilot was single-broker + single-worker on one host.
[0.3.1] — 2026-04-14¶
Bug-fix release driven by issues caloron-noether hit migrating from v0.2.
Fixed¶
-
Python
from __future__ importno longer breaks the Nix wrapper. Stage implementations starting withfrom __future__ import annotationsused to land at line ~17 of the synthesized wrapper, which Python rejects withSyntaxError: from __future__ imports must occur at the beginning of the file. The wrapper now hoists every top-levelfrom __future__ import …line to the very first lines of the wrapped module. -
noether stage get <prefix>now resolves prefixes, the same waystage activateand graph loaders already do. Previous versions did an exact-string lookup and then surfaced a "did you mean" hint that echoed the user's input back at them — because the hint also truncated to 16 characters even when the input was already 16 characters. Both halves are fixed:cmd_getresolves throughresolve_stage_id, and the hint shows IDs at prefix length + 8 chars so collisions become visible. -
Stage-spec effect parser accepts
Llm,Cost,Unknown, plus lowercase / snake_case variants. v0.2 specs that declared"effects": ["Llm"]were silently dropping that effect with a crypticWarning: unknown effect 'Llm', ignoring.log line — the stage would then run as if it were Pure. Now decoded correctly. Llm without an explicitmodeldefaults to"unknown"; Cost withoutcentsdefaults to0.
Upgrading from v0.2¶
The v0.2 → v0.3 transition has two breaking surfaces beyond what the v0.3.0 release notes covered. Both now have clearer error messages but existing specs still need a one-time rewrite:
-
Effect names are now case-tolerant —
Llm,llm, and any ofnon-deterministic/nondeterministic/NonDeterministicall work. If you sawunknown effect '<X>', ignoringwarnings on v0.3.0, re-add this release and the warnings go away. -
Type-spec format is
{"Record": [["field", T], …]}, not the{"type": "Record", "fields": {…}}form some v0.2 examples used. We don't ship an automatic migration; rewrite by hand. The simplified syntax (bare strings like"Text","Number") works for primitives inside Record cells.
If you maintain a downstream stage catalogue and want a one-shot noether stage-spec migrate command, file an issue — happy to add.
[0.3.0] — 2026-04-14¶
Added¶
noether-schedulermigrated into the public repo. The cron-based composition runner now lives atcrates/noether-scheduler/, ships a binary in every GitHub release, and publishes to crates.io (cargo install noether-scheduler). Previously lived in the privatenoether-cloudworkspace.--config <PATH>flag onnoether-scheduler, alongside the existing positional argument.noether-scheduler --config scheduler.json,noether-scheduler scheduler.json, and the barenoether-scheduler(defaults to./scheduler.json) all work.- Dedicated scheduler guide — config schema, cron semantics, webhook payload, systemd unit template, Docker recipe, troubleshooting.
Changed¶
- Workspace version bumped to
0.3.0to cover path-dep versions onnoether-core,noether-store,noether-engine(no runtime API changed; the bump is for coherent cross-crate publishing). - Dockerfile in
noether-cloud/infra/now buildsnoether-schedulerfrom the public noether checkout instead of a local workspace copy.
Fixed¶
- Documentation now reflects the actual crates.io publishing flow. No more references to a private source.
[0.2.1] — 2026-04-14¶
Added¶
- crates.io metadata on every crate (
description,licenseset toEUPL-1.2,repository,homepage,keywords,categories). The 0.2.0 publish failed with "missing metadata"; 0.2.1 is functionally identical to 0.2.0 but actually installable. - Path-dep versions (
version = "0.2") on workspace path dependencies so downstream crates resolve correctly once published.
Notes¶
- First release actually on crates.io. Use
cargo install noether-clifrom this version onward.
[0.2.0] — 2026-04-13¶
Feedback-driven release. External developer wrote up every friction they hit building a real pipeline on 0.1; this release addresses it.
Added — engine¶
Letoperator for binding named intermediate results and carrying original-input fields throughSequentialpipelines. Solves the canonicalscan → hash → diffpattern where a later stage needs a field an earlier stage erased. Bindings run concurrently against the outer input; the body sees a merged record{...outer fields, name → binding output}.- Python
def execute(input)contract validated atstage addtime. Specs missing a top-levelexecuteare rejected with a clear error pointing at the docs instead of the cryptic'NoneType' object is not subscriptableruntime failure. - Stage ID prefix resolution in graphs. Graph loaders accept the 8-char IDs
noether stage listprints; the CLI resolves them to full SHA-256s at load time and errors clearly when a prefix is ambiguous. - Boot-time curated-stages loader in
noether-registry:NOETHER_STAGES_DIRenv → every*.jsonunder the directory is parsed, signed with the stdlib key, upserted, and marked Active. Idempotent on content hash. - Progressive embedding cache + inter-batch pacing. Partial cold-start progress survives rate-limit crashes. New env knobs:
NOETHER_EMBEDDING_CACHE,NOETHER_EMBEDDING_BATCH,NOETHER_EMBEDDING_DELAY_MS.
Added — CLI¶
stage addauto-promotes Draft → Active by default; opt out with--draft.stage sync <dir>— bulk-import every*.jsonspec, idempotent on hash.stage listgains--signed-by stdlib|custom|<keyprefix>,--lifecycle <state>,--full-ids.noether runandnoether composeread JSON from stdin when--inputis absent and stdin is a pipe (echo '{...}' | noether run graph.json).- Embedding-provider warnings suppressed on commands that don't actually use semantic search (
list,get,add,stats, ...). Surface them viaNOETHER_VERBOSE=1or onsearch/compose.
Changed — docs¶
- "Python Stage Contract" is the lead of
guides/custom-stages.md. guides/composition-graphs.mdcorrected to match the real schema (id/stages/predicate/if_true/delay_ms), added the stages-vs-branches rationale table, documented Sequential's no-projection limitation, added the newLetoperator section.- Three overlapping
getting-started/pages merged into one. guides/remote-registry.mdrewritten to lead with the public registry atregistry.alpibru.comand the Docker-Hub-style auth model (anonymous read, authed write).- Obsolete
guides/stage-store-build.md(564 lines, duplicate ofcustom-stages.md) deleted.
Fixed¶
- Python stages that defined
executebut importedsys.stdinat module level would sometimes race the wrapper. Wrapper rewrite. cargo publishpreviously failed with "missing metadata" (addressed in 0.2.1).
Not a bug¶
- "stdin dropped under Nix executor" reported in feedback turned out to be a CLI UX bug — the Nix executor forwards stdin correctly; the CLI just wasn't reading its own pipe. That's now the stdin fallback.
Pre-release history¶
Internal numbering before crates.io. Kept for reference; do not map to public versions.
[0.6.0] — 2026-04-09 (internal)¶
- Canonical stage identity (
canonical_id): SHA-256 of name + input - output + effects. Enables automatic versioning —
noether stage addauto-deprecates the previous version when a stage with the same canonical_id is re-registered. noether stage activatepromotes Draft stages to Active; supports ID prefix matching.- OpenAI LLM + embedding provider (
OPENAI_API_KEY; Ollama-compatible viaOPENAI_API_BASE). Anthropic LLM provider (ANTHROPIC_API_KEY). - Simplified type syntax (
normalize_type): stage spec files accept"Text"instead of{"kind":"Text"},{"Record":[["field","Text"]]}instead of the verbose canonical form. - Stage spec
tagsandaliasesparsed from simple format. - Deprecated stage resolution:
noether runtransparently followsDeprecated → successor_idchains. - 370 stage specs across 50 open-source libraries (in
noether-cloud/stages/). - Capability benchmark with 4 scenarios: type safety, parallel execution, reusability, token analysis.
noether store dedup --applyusesDeprecated{successor_id}instead ofTombstone, preserving forward pointers.noether stage listdefaults to Active lifecycle filter.
[0.5.0] — 2026-04-08 (internal)¶
- Runtime budget enforcement:
BudgetedExecutor<E>with atomic cost accounting.noether run --budget-cents <n>.CompositionResult::spent_centsreports actual spend. - Cloud Registry hardening:
DELETE /stages/:id, offset-based pagination,get_livefor on-demand fetches.noether-schedulergainsregistry_url/registry_api_keyconfig fields. - NixExecutor hardening: configurable timeout, output caps, stderr cap. Wall-clock timeout via
mpsc::channel+kill -9.classify_errordistinguishes Nix infra failures from user code errors.NixExecutor::warmup()pre-fetches the Python 3 runtime. - Effects v2:
EffectKind,EffectPolicy,infer_effects,check_effects,noether run --allow-effects <...>. Remote stages implicitly carryNetwork + Fallible; unknown stages carryUnknown. - Stdlib count: 76 stages.
[0.1.0] — 2026-04-07 (internal)¶
- Phase 0 — Type system (
NType), structural subtyping, stage schema, Ed25519 signing, SHA-256 content addressing. - Phase 1 —
StageStoretrait,MemoryStore, 50 stdlib stages, lifecycle validation. - Phase 2 — Lagrange composition graph, type checker,
ExecutionPlan,run_composition, traces. - Phase 3 — Composition Agent, semantic three-index search,
VertexAiLlmProvider,noether compose. - Phase 4 —
noether buildwith--serve :PORTbrowser dashboard,--dry-run, store dedup. - ACLI-compliant CLI with structured JSON output.
- MkDocs documentation site.