Skip to content

v0.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Sep 13:33
· 31 commits to main since this release

Changes

Added

  • hird effect-diff, a gate on effect reach. A new hird-policy
    crate diffs a committed effect graph against a freshly emitted one
    under the graph's identity contract and classifies every change: a row
    gaining an effect or opening, a protocol constructor added, removed, or
    reshaped, and a new actor or tool widen; a removal narrows; a
    signature change, a supervision change, and a pure addition are
    neutral. hird effect-diff <baseline> <input> re-emits the graph,
    prints the report as text or --json, and exits nonzero when reach
    widened, so a change cannot quietly grant a program more than its
    baseline allowed. --exact exits nonzero on any drift, so a narrowing
    cannot leave the baseline over-granting either. A baseline written
    under another schema_version is refused; fields added after schema 1
    default when absent.
  • The approved-baseline workflow, dogfooded. demo/effect-baselines/
    holds one committed graph per demo program, demo/effect-baselines.sh
    checks or regenerates them, a CI job runs the check, and a test guards
    them locally, so a change that moves a graph must update its baseline
    in the same reviewable diff. docs/effect-graph.md states the JSON
    shape, the identity contract — which fields a diff compares, and which
    (every line, and source order) it ignores — and the copyable recipe.
  • Functions in the effect graph. The graph gains a functions
    section: name, line, parameters, result, and the declared row, so a
    reviewer reads off the graph that a plain function is honestly
    ! {Tool<X>} and nothing more. The text form prints one fn line per
    declaration. Additive under schema version 1.
  • pub tools, actors, and supervisors cross module boundaries. A
    pub tool exports its marker — for Tool<Name> rows and handle
    arms — and its generated function; a pub actor exports the actor
    entry for spawn and child specs and its message type transparently,
    so another module names Pid<Msg> and builds messages; a pub supervisor exports the name for supervise and child. Each imports
    selectively by name, and a qualifier import reaches the tool function.
    Supervisor children carry their actor's message type, so child() on
    an imported supervisor types without importing the actor. The IR
    records an imported tool under the qualified spelling its calls carry,
    and codegen routes those calls through the tool dispatcher rather than
    emitting a remote call into a function the declaring module never
    emits.
  • docs/context-packing.md. Real get_context_for_symbol
    transcripts for the demo Planner actor at 50 and 400 tokens, what
    each budget dropped and why, with a README pointer.
  • MCP resources and an authoring prompt. hird-mcp advertises
    resources and prompts alongside tools. Three resources serve the
    documents an agent otherwise had to be handed out of band, embedded in
    the binary: hird://docs/writing-hird-llm (the agent-facing guide),
    hird://docs/parser-diagnostics (the P… code index), and
    hird://phrasebook. One prompt, author_supervised_module(file, purpose), scripts the authoring loop: write a supervised actor module,
    then verify it with check_file and the introspection tools until the
    compiler confirms it.
  • list_definitions MCP tool. hird-mcp gains a module outline:
    every name a file's top-level declarations bind, in source order, with
    kind, line, a one-line signature, doc, and the signature's approximate
    token cost, plus the file's imports (module, defining file, qualifier or
    selected members). A type lists its constructors, a tool its generated
    function, an actor its message type and constructors. Until now a
    module's names were discoverable only by failing a lookup and reading
    error.data.available, and get_context_budget names no symbols; the
    per-entry cost sizes a get_context_for_symbol budget that keeps the
    signature whole.
  • check_file MCP tool. hird-mcp gains a ninth tool that checks a
    file's whole program and returns every diagnostic as a result, warnings
    included, with ok saying whether any is an error. Until now diagnostics
    only surfaced when another tool failed on the queried file, so a warning
    such as C0056 on a clean module was invisible and an agent had to probe
    tool by tool to find the first error. Each diagnostic names its file,
    so a sibling that fails to parse is reported under its own path.

Fixed

  • explain_effect_row rejected names it listed as available. A tool
    name (ReadRepo) was not_found although lookup_definition accepted
    it; it now explains the tool's generated function, Tool<ReadRepo>
    included, and reports that function as name. A defined name that is not
    a function (a type, an alias, an actor) is not_a_function rather than
    not_found.
  • get_context_for_symbol declared budget required. The tool's
    input schema listed every property as required, so a strict MCP client
    could not omit budget although the server defaults it to 400. The
    property now carries "default": 400 and is optional; an input property
    is required exactly when it declares no default.
  • Type aliases were half-visible to the MCP tools. lookup_definition
    on an alias answered kind type alias with no type, while
    get_context_for_symbol and render_ir_fragment on the same name said
    not_found and listed it under the available names. The checker now
    records each module's aliases as the types they expand to
    (CheckedFile::aliases, a parametric alias quantified), so
    lookup_definition reports the expansion, get_context_for_symbol
    summarizes the alias as its signature and doc, get_context_budget
    counts aliases under types, and render_ir_fragment fails with no_ir
    and the expansion in error.data.expansion — aliases are erased before
    lowering, so there is no IR to render. The descriptor's kind list names
    type alias.

Changed

  • emit-effect-graph --json is keyed by module. The flag emitted a
    single module's graph; it now emits one document for a file and a
    directory alike — schema_version plus modules, the program's module
    graphs keyed by module name and sorted by it, each still carrying its
    own schema_version and module so one graph stays self-describing
    when consumed alone. A consumer of the 0.3 shape now reads through
    modules. The text form locates declarations by the module's own file
    name (agent_fleet.hird:12), never a checkout path, so it is stable
    across machines.
  • MCP actor tools follow imports. explain_actor_protocol and
    emit_actor_effect_graph take actor_name as the file would write it:
    local, selectively imported, Qualifier.name through a use, or
    Module.name for any module in the program. Actor reach is now built
    program-wide, resolving each row's type names through the naming
    module's own scope so same-named declarations in two modules stay
    distinct, and every emitted node carries a module tag.
  • MCP diagnostics carry codes, hints, and positions. Parse diagnostics
    in error.data.diagnostics now include their P… code and help hint
    (they carried message and line only); parse and check diagnostics alike
    gain a 1-based character column and an exclusive end_line/
    end_column, and check diagnostics list their same-file related
    locations. check_file returns the same shape.
  • MCP tool descriptors disclose more. Every hird-mcp tool now carries
    MCP annotations (readOnlyHint, idempotentHint, no destructive or
    open-world behaviour), a title, and an outputSchema matching its
    structuredContent. Descriptions state the result fields, the error codes
    a call can fail with, and which sibling tool to prefer for related
    questions; parameter descriptions spell out path resolution, character
    columns, actor-name scope, and how budget drops sections.

Install

Download the archive for your platform below and extract it; each one
carries three binaries:

  • hird — the compiler CLI (check, build, run, emit-ast, emit-effect-graph)
  • hird-lsp — the language server (diagnostics, hover, go-to-definition)
  • hird-mcp — the MCP server exposing compiler introspection to LLM agents

Or build from source with Rust 1.97+:

cargo install --git https://github.com/no-materials/hird hird-cli

Compiling and running Hirð programs (hird build, hird run) needs
Erlang/OTP on PATH; hird check works without it.

See the README to get started.