AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
지식 코퍼스를 위한 수렴 보조 도구
Convergence assistant for knowledge corpora
개발 도구Rust · PythonGitHub @flowerornament직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Convergence assistant for knowledge corpora.
Experimental, pre-1.0, and moving fast. anneal is in active development:
the CLI surface, the anneal.dl rule layer, diagnostics, and the on-disk
formats all still change between releases, sometimes without a deprecation
path. It is built and dogfooded in the open — useful today, but not yet
stable. Pin a version if you depend on it, expect rough edges, and read the
CHANGELOG before upgrading.README 전문 보기 ↗Convergence assistant for knowledge corpora.
[!NOTE] Experimental, pre-1.0, and moving fast. anneal is in active development: the CLI surface, the
anneal.dlrule layer, diagnostics, and the on-disk formats all still change between releases, sometimes without a deprecation path. It is built and dogfooded in the open — useful today, but not yet stable. Pin a version if you depend on it, expect rough edges, and read the CHANGELOG before upgrading.
anneal reads a directory of markdown, turns it into typed facts, evaluates a
Datalog-shaped rule layer against those facts, and exposes a small
agent-friendly surface for orientation, retrieval, convergence work, and
extension.
It is built for disconnected intelligences: agents across sessions with no shared memory that need to recover what matters, read enough context, and leave the corpus more settled than they found it.
The larger idea is convergence. In a large corpus, the useful signal is not just
what can be retrieved. It is what is becoming more precise, which obligations
are getting discharged, which decisions have replaced open questions, and which
paths are falling out of the active frontier. anneal keeps that signal
visible.
Large agent-readable corpora behave like living systems. Files cite each other, supersede each other, encode obligations, accumulate local vocabulary, and move through stages of settledness. Without tooling, every new agent burns context reconstructing the same map.
anneal gives the agent a programmable corpus surface:
anneal context "what should I read before changing the release path?"
anneal schema
anneal describe search
anneal -e '? diagnostic(code, severity, subject, file, line, evidence).'
The important move is that corpus structure becomes queryable. Markdown files,
frontmatter, labels, body text, spans, references, snapshots, and project
configuration are all exposed as typed relations. The built-in prelude derives
standard convergence facts and verbs from those relations, and project
anneal.dl files can add local rules and callable @verb declarations without
changing the binary.
The command names are intentionally mnemonic, but they are not the whole tool.
status is the arrival dashboard: aggregate corpus vital signs plus
copy-runnable queries for goal-less orientation and work. context gathers a
retrieval bundle once you can name a goal. schema maps the language, and
describe teaches one relation, primitive, predicate, or verb at a time.
search, read, and handle retrieve evidence. When saved forms are too
broad, anneal -e is the normal way to ask the corpus a precise question. When
a precise question becomes reusable, edit anneal.dl and add an @verb
declaration in the same language.
curl -fsSL https://raw.githubusercontent.com/flowerornament/anneal/master/install.sh | bash
The installer is the primary path for prebuilt binaries. It installs to
~/.local/bin by default, prints the target, URL, and destination before it
writes anything, fails fast on unsupported targets, and stays aligned with the
published release matrix.
Binaries are published for:
aarch64-apple-darwinx86_64-unknown-linux-gnuaarch64-unknown-linux-gnuBy default the installer writes only the binary. To also install the bundled agent skill, pass one or more explicit skill targets:
curl -fsSL https://raw.githubusercontent.com/flowerornament/anneal/master/install.sh | bash -s -- \
--skill-target "$HOME/.agents/skills/anneal" \
--skill-target "$HOME/.codex/skills/anneal"
Other useful installer forms:
# Install somewhere else
curl -fsSL https://raw.githubusercontent.com/flowerornament/anneal/master/install.sh | INSTALL_DIR="$HOME/bin" bash
# Same override, passed as a flag
curl -fsSL https://raw.githubusercontent.com/flowerornament/anneal/master/install.sh | bash -s -- --install-dir "$HOME/bin"
# Preview without writing anything
curl -fsSL https://raw.githubusercontent.com/flowerornament/anneal/master/install.sh | bash -s -- --dry-run
git clone https://github.com/flowerornament/anneal.git
cd anneal
cargo install --path . --locked
Run without installing:
nix run --accept-flake-config github:flowerornament/anneal
Install into your profile:
nix profile install --accept-flake-config github:flowerornament/anneal
The flake advertises Anneal's public Cachix cache, where native builds for all
four supported Nix systems are published. --accept-flake-config permits that
cache for the command. Persistent system configurations should trust
https://flowerornament.cachix.org and its advertised public key explicitly.
Add as a flake input:
anneal = {
url = "github:flowerornament/anneal";
inputs.nixpkgs.follows = "nixpkgs";
};
For flake inputs that should follow the latest published release instead of
master, pin the moving release branch:
anneal.url = "github:flowerornament/anneal?ref=refs/heads/release";
Use the exported Home Manager module for a declarative setup. It installs the binary, writes machine-local user config under XDG paths, and can optionally install the bundled skill into agent-managed skill directories.
{
imports = [
inputs.anneal.homeManagerModules.default
];
programs.anneal = {
enable = true;
settings.state.historyDir = config.xdg.stateHome;
skill.enable = true;
skill.targets = [
".agents/skills/anneal"
".codex/skills/anneal"
];
};
}
Repo-owned corpus behavior lives in anneal.dl and should be committed with
the corpus. Home Manager does not write project rules, source discovery, handle
policy, or convergence config; it only installs the binary/skill and writes
machine-local preferences such as history storage under XDG paths.
Use the narrowest surface that answers the next question. The ladder is: arrive, discover the language, retrieve evidence, then write a query when the built-in verbs are too broad.
# Arrive cold
anneal status --format=text
anneal -e '? recent_frontier(h, rank, recency), *handle{id: h, file: file} order by rank asc.' --limit 12 --format=text
anneal -e '? ranked_anchor(h, rank, score, why), *handle{id: h, file: file} order by rank asc.' --limit 12 --format=text
anneal context "find the runtime overview and its current blockers" --format=text
# Discover the language before guessing
anneal describe convergence --format=text
anneal schema --format=text
# Read enough to act
anneal search "runtime overview" --limit 3 --format=text
anneal read docs/runtime-overview.md --budget 4000 --format=text
anneal read docs/runtime-overview.md --span-id 'docs/runtime-overview.md#h/summary' --format=text
anneal handle docs/runtime-overview.md --format=text
anneal handle docs/runtime-overview.md --impact --format=text
# Ask a precise corpus question
anneal -e '? *handle{id: h, kind: "file", status: s}.'
anneal -e '? diagnostic{subject: h}, area_of{h: h, area: "language"}.'
# Work the convergence frontier
anneal status --format=text
anneal -e '? diagnostic{severity: "error", subject: h}.'
Runtime commands render readable text at a terminal and JSON/NDJSON when piped.
Use --format=text in pipe-only harnesses when an agent needs prose-like
output. Use --json, --format=json, or --format=ndjson when you want
stable machine output.
Run commands from a marked corpus (.design, docs, or anneal.dl) or pass
--root <path> for an explicit ad-hoc scan. If an unmarked directory should
become a corpus, run anneal init --dry-run to inspect the project file before
writing it with anneal init.
Handle
The graph unit of knowledge. A handle may be a file, label, version, or
external reference. Headings are content spans (*span) attached to file
handles. In-repo code references such as lib/app.ex:10-20 use external
handles with external_class = "code" metadata; links to other existing
files inside the corpus or configured external mounts use
external_class = "asset" when no extracted document or resolved label/version
answers the reference. This includes excluded or out-of-window Markdown and
extensionless files. Asset payloads stay opaque; their internal references and
incoming fragments are not checked. Missing targets still produce E001, and
paths outside the corpus or configured mounts do not become assets.
Edge
A typed relationship between handles. Common edges include Cites,
DependsOn, Supersedes, Verifies, and Discharges.
Status
The lifecycle state from frontmatter. Project config can classify statuses as
active, settled, or terminal; settled does not imply terminal.
Convergence Lattice
The model that tracks movement from active uncertainty toward terminal
settledness. If an ordering is configured, anneal status can show pipeline
shape: raw ideas becoming drafts, drafts becoming reviewed artifacts, reviewed
artifacts becoming authoritative.
Snapshot
A point-in-time capture of graph state, stored in local anneal history. Snapshot
history powers at("snapshot:last") queries, flow(h, direction),
advancing(h), holding(h), drifting(h), and the status convergence summary.
anneal status records bounded automatic snapshots, coalescing unchanged
consecutive status reads.
Prelude
The built-in standard library of rules, diagnostics, ranking, and verbs. It is
loaded automatically and should not be copied into a project.
anneal status
anneal -e '? recent_frontier(h, rank, recency), *handle{id: h, file: file} order by rank asc.' --limit 12
anneal -e '? ranked_anchor(h, rank, score, why), *handle{id: h, file: file} order by rank asc.' --limit 12
anneal context "goal"
anneal help agent
status renders aggregate corpus vital signs and prints copy-runnable
orientation/work queries. When authored markdown frontmatter exists outside
typed projections and W007's exact alias set, it also names the top three keys
by distinct file-handle count and points to
unmodeled_frontmatter_key(key, distinct_file_handles, reference_name_signal, rank) for the complete ranked inventory. The lexical
signal only breaks equal-count ties; it never suggests an edge kind.
recent_frontier returns recently changed file
handles for goal-less reading; ranked_anchor returns the durable spine from
the broader anchor relation. Both close with order by rank asc so the list
arrives in reading order — rank 1 first — because order by <expr> [asc|desc]
sorts a query's result at the projection boundary, and order by … --limit N
is a true top-N. context
composes ranked summary-bearing span search, compact span metadata, and graph
neighborhood once you have a goal. Add --read-spans when you want matched
span bodies inline.
help agent prints the bundled agent skill briefing from the installed binary.
help <name> resolves static commands, project @verbs, and runtime teaching
cards through one route; when meanings share a name, help shows the precedence
winner and points to the other interpretation.
The hidden prime alias remains for installed skill loaders and muscle memory.
Useful context flags:
--hits N: number of search winners--budget N: per-hit span selection cap; also caps bodies with --read-spans--depth N: graph distance around winners--include-low-confidence: include lower-confidence search hits--read-spans: include matched span bodies inlineRead the authored goals before choosing a capability:
anneal intents
anneal describe spec_code_drift
The complete intents list groups declared goal sentences by card name and kind,
including project predicates, verbs and declared diagnostic codes. The reading
agent matches its goal to these sentences; the surface makes no semantic
ranking claim. --json emits one name, kind, intent row per goal. Read the
chosen card with describe NAME before writing a query.
anneal schema
anneal describe search
anneal describe runtime
anneal describe currency
anneal -e '? axis(name, question, oracle, disposition).'
anneal -e '? axis_of(predicate, "recency").'
anneal -e '? search("conformance", h, span, score, reason, field, low).'
anneal -e '? changed_within(h, 7), *handle{id: h, kind: "file", summary: summary}.'
anneal -e '? sources(name, recognizes, capabilities, doc).'
Use schema to see queryable relations and signatures. Use describe for one
primitive, predicate, or verb. Use describe runtime for the compact map and
copyable examples. Use describe <axis> when the vocabulary is still blurry:
axis lists the runtime dimensions, and axis_of places predicates on axes
such as relevance, currency, lifecycle, dependency-validity, recency,
importance, convergence, structure, obligations, and topic. Corpus-local
vocabulary is queryable directly through relations such as *handle{status: status},
*edge{kind: kind}, *handle{namespace: ns}, and *meta{key: key}. Adapter
information is queryable through sources(name, recognizes, capabilities, doc). Use -e when you need to compose a question directly. When a working
query should become a named project move, edit anneal.dl and add an @verb
declaration.
anneal search "runtime overview" --limit 5
anneal read docs/runtime-overview.md --budget 4000
anneal read docs/runtime-overview.md --span-id 'docs/runtime-overview.md#h/protocol'
anneal handle docs/runtime-overview.md
anneal handle docs/runtime-overview.md --impact
anneal handle docs/runtime-overview.md --lineage
search ranks content and metadata hits and includes summary for
matched spans. Scores combine lexical strength with lifecycle status,
hub degree, and currency: current operative successors outrank superseded
predecessors, while superseded material stays reachable. Search and context
hits include disposition, status, and age so you can tell current framing from
stale-but-relevant history.
Neighborhood counts in_degree and out_degree (text: in and out) are
incoming and outgoing stored edges of all kinds across the loaded corpus.
The hub penalty uses out_degree only.
read retrieves bounded content spans for one handle; use --span-id when a
search hit already identified the section you need.
handle shows incoming and outgoing edges grouped by kind and separates
in-repo code references; --impact adds direct and indirect reverse
dependencies before an edit, and --lineage follows file-level Supersedes
chains to show older predecessors, newer successors, and current heads.
Use anneal context "X" when the task is "find the section that defines X";
it returns ranked section hits, span metadata, summary, and graph
neighborhood. Add --read-spans only when inline matched bodies are worth the
extra output. Use grep -rn "X" when you need every literal occurrence with
line numbers. Use anneal -e '? ...' when the question is structural, such as
"which handles match this graph predicate?"
anneal -e '? frontier(h, energy), *handle{id: h, file: file, summary: summary}.'
anneal -e '? area_health(area, grade, files, errors, cross_edges).'
anneal -e '? diagnostic{code: code, severity: "error", subject: h, file: file, line: line}.'
anneal -e '? blocker(h, energy, source), h = "HANDLE".'
anneal -e '? *handle{id: h, file: f}, git_mtime(f, t).'
anneal -e '? authored_age(h, days), *handle{id: h, file: file}.'
anneal -e '? changed_recently(h, band), *handle{id: h, file: file}.'
potential exposes raw unsettled-work energy; frontier projects the
highest-energy candidates. area_health grades per-area convergence.
diagnostic{severity: "error", ...} filters to blockers. blocker explains why one
handle is stalled. flow classifies active movement as advancing, holding, or
drifting; settled handles are outside flow by design. authored_age is the
date-backed authored-age oracle used by recency ranking. changed_recently,
changed_within, and git_mtime are lower-authority git-backed change
signals for "what changed?" questions. In a jj added workspace they use author
time along the recorded @, read without snapshotting; unsnapshotted edits are
outside this history. Author time dates creation of the change, not its latest
amendment. Direct Git and colocated checkouts retain their existing timestamps.
repository_operation_capability(operation, availability, provider, reason)
reports availability separately for recency, target history, assertion provenance,
ignore classification, and version tags. jj assertion dates and revisions remain null.
Version tags select Git HEAD or exact recorded jj @; a failed probe yields
no version handles and reports unavailable without disabling other operations.
Code file discovery uses the validated Git index or the pinned jj tree, and
refuses an unavailable VCS discovery operation.
Conflicted, stale, or moving jj pins make dependent operations unavailable;
missing capability is distinct from an available query with no rows.
An explicit drift refresh in a jj desk writes that desk's
.anneal/drift-evidence.json for later status and check runs. Cache reads validate
the pinned backing store; changed targets invalidate on pin movement. A moved
generation never writes, and the anchor's cache is neither read nor written.
The convergence vocabulary lives in the prelude — use describe convergence,
describe potential, describe entropy, describe blocker,
describe recency, describe authored_age, describe changed_recently,
describe changed_within, or describe git_mtime to learn the
joins, then compose with -e. The check command remains as a hidden CI gate
alias for diagnostic{code: code, severity: "error", subject: h, file: file, line: line}.
anneal -e '? *handle{id: h, kind: "file"}.'
anneal -e '? *edge{from: src, to: dst, kind: "DependsOn"}.'
anneal -e '? diagnostic(code, severity, subject, file, line, evidence).'
anneal -e '? frontier(h, energy), *handle{id: h, file: file, summary: summary}.'
anneal -e '? source_of("frontier", file, lines).'
anneal -e '? search("conformance", h, span, score, reason, field, low).' --limit 20
anneal -e '? changed_within(h, 7), *handle{id: h, kind: "file"}, search{query: "conformance", handle: h}.'
anneal -e '? currency_suspect("HANDLE", newer), topic_sibling("HANDLE", newer, shared).'
The query language is Datalog-shaped. Stored relations use * prefixes, for
example *handle, *edge, *meta, *content, *span, *config,
*snapshot, and *generation. Derived predicates come from the built-in
prelude, project anneal.dl, and inline query-local rules. anneal -e -
reads a query from stdin, so agents can keep larger questions in scratch files.
Use --limit N while exploring broad predicates.
A corpus can ingest the source tree beside it, so design documents and the code they describe live in one graph:
source code {
source_root("..").
}
That single line is the language-agnostic file-level mode: file handles for
every source file, test/generated/private classification, TODO/FIXME
obligations, and version tags. Repeated source_extension("py") values widen
the scan beyond the default rs/ex/exs set. Add a pre-built artifact for item-level
handles — rustdoc_json("target/doc/crate.json") for Rust public APIs, or
eep48_beam_dir("_build/dev/lib/app/ebin") for Elixir docs chunks.
Spec citations then resolve to live code handles, and every cross-reference carries machine-verified drift evidence — how much the cited code changed since the citing line was written:
anneal check --refresh-drift # build drift evidence (explicit, cached)
anneal status # Code refs 2 intact, 75 drifted, 8 moved, ...
anneal handle SPEC.md # Cites src/cli.rs [referent-moved-ambiguous · 11 candidates]
anneal -e '? code_ref(spec, ref, path, code_handle, disposition).'
anneal -e '? assertion_drift("SPEC.md", target, commits).'
Dispositions are earned, never asserted: an intact referent means zero
commits since the assertion; a renamed file routes to its confident head; a
split stays referent-moved-ambiguous; a dirty worktree or unknown
assertion date is declared, not papered over. Cold evidence shows as a
premise line, and only the explicit refresh builds the cache.
New corpora work without configuration. anneal loads the built-in prelude,
scans markdown with default adapter settings, infers handle namespaces from
corpus evidence, and runs the standard commands immediately.
Configuration has three layers:
anneal.dl: repo-local corpus semantics: adapter discovery, convergence
statuses, terminal/active sets, pipeline ordering, linear namespaces,
frontmatter edge mappings, excludes, project rules, and @verb declarations.$XDG_CONFIG_HOME/anneal/config.toml or ~/.config/anneal/config.toml.Repo config should describe the corpus. User config should describe the machine.
anneal init --dry-run previews a scaffolded anneal.dl. anneal init writes
only when no repo config exists.
A named mount adds selected Markdown from another local repository to this
corpus. Put this in the host's anneal.dl:
source md {
scan_root(".design").
mount "murail" {
path("../murail").
scan_root("crates/murail-model/fm").
}
}
A relative path is based on the host's enclosing project boundary (its
repository or recognized project root), independently of the current directory
and whether anneal.dl lives there or inside .design. Absolute paths and
path("~/code/murail"). are also supported. Leading ~/ expands from an
absolute HOME; ~user, unset/relative HOME, other environment expansion,
globbing and fallback search are unsupported. Absolute and ~/ forms bind a
checked-in config to that machine layout; they work across differently located
desks without changing identities. Every form must resolve to exactly the
owning Git or jj repository root; a missing or non-root mount refuses loading. scan_root is relative to that mounted repository;
it can be repeated or take a list. Omitting it uses that repository's native Markdown
scan roots and external roots. Explicit mount roots override that selection.
The owning corpus root and config location are discovered as in a native run
at the repository root (for example .design/anneal.dl).
Roots must exist and remain inside their repository after canonicalization.
The default reads current workspace files, including local edits. Add
revision("489ffa98"). inside the mount to read an exact Git commit without
changing either checkout. Full commit ids and unambiguous hexadecimal prefixes
are accepted; symbolic refs, jj change ids, absent or ambiguous ids refuse.
The full resolved commit is recorded. Nothing is fetched.
Imported handles are the alias plus their native identity, independently of the
selection window, physical checkout path and pin. Corpus-internal files keep
corpus-relative keys; siblings keep their CR-D70 project-relative keys: murail:crates/murail-model/fm/atoms.md. Link to that
handle in Markdown or an edge-bearing frontmatter field:
[Atom definitions](murail:crates/murail-model/fm/atoms.md)
Section placeholders (section:...) keep their native unqualified meaning
and do not become missing-handle errors. Relative links inside imported documents stay in their own mount; explicit
aliases can link between mounts. Labels, spans and owned fact identities are
scoped too. Aliases use lowercase ASCII letters, digits, _ and -, starting
with a letter. Handle kinds, reserved prefixes, standard URI schemes, the host
corpus prefix and duplicate aliases are refused. A declared alias owns the opaque alias:path form; undeclared schemes and
every scheme:// link retain URI semantics. Incoming fragments follow ordinary Markdown link behavior and
are not validated.
An existing regular file anywhere inside the mounted repository becomes an
opaque asset if no extracted document, label or version answers the reference.
This includes unselected or excluded Markdown and data files. Asset handles
use murail:external:asset:<path>. Assets have no extracted payload or outgoing
links. Existence comes from the selected read mode: a file added after a pin
stays missing under that pin. Missing named targets remain E001; directories,
submodules and escapes outside the mount do not become assets. Pinned trees
never follow symlinks.
To preserve existing links such as [Atoms](fm/atoms.md), add a mapping inside
the mount:
link_prefix("fm/", "crates/murail-model/fm/").
This only rescues host references that remain unresolved after normal host
document, label, version and opaque-asset lookup. A local fm/atoms.md always
wins. Successful retries use the mount's native IDs and ordinary asset rules;
misses still report E001 with the original authored target. Imported references
are never remapped and mappings do not chain. Exposed directory prefixes must
end in /; they cannot be absolute, contain parent traversal or URI syntax,
collide with a mount alias, or overlap another exposed prefix. The native
prefix belongs to that mount, may be empty and cannot name another alias.
For a corpus with config in .design/anneal.dl, use scan_root("."). for its
host documents. A machine-local path("~/code/murail"). keeps this same config
usable in both sibling seat desks and nested cached desks.
Each imported row retains its owning origin; pinned origins include the full
commit and logical path, never a temporary extraction path. Non-UTF-8 paths cannot form handles and
are skipped in a pin, with their count in md.mount.skipped_non_utf8_paths. Use
anneal describe markdown_mount and
anneal describe mount_repository_operation_capability to inspect membership,
mode and the five per-handle repository operations. Those capabilities come
from the mounted repository, never the host. Pinned mounts report all five
history/working-tree operations unavailable, while retaining exact commit
provenance. Workspace mounts do not reuse the host drift cache; detailed
referent-drift cache evidence is unavailable for them.
Only foreign source md settings scan_root, external_root, scan_exclude, file_extension,
linear_namespace, label_pattern, version_pattern, section_min_depth and
section_max_depth are read, with the same supported-value restrictions as
local Markdown discovery. Foreign rules, runtime policies, prelude extensions
and nested mounts are ignored; md.mount.config_ignored records that boundary
when a foreign anneal.dl exists. The host's rules and policies govern the
combined graph. external_root retains its existing same-project semantics.
anneal.dlsource md {
file_extension(".md").
scan_root(".").
# external_root(["../formal"]).
scan_exclude(["vendor", "**/README.md"]).
}
config convergence {
ordering(["raw", "draft", "review", "approved", "published"]).
active(["draft", "review", "approved"]).
settled(["approved"]).
terminal(["published", "archived", "superseded"]).
asserts_code(["draft", "review", "approved"]).
description("draft", "Under construction; may change substantially").
description("approved", "Settled primary artifact; changes require review").
description("archived", "Superseded or retired; no further changes expected").
}
config dependency {
dead(["custom-retired"]).
valid(["custom-current"]).
}
config handles {
rejected(["SHA", "GPT"]).
linear(["REQ"]).
}
config frontmatter {
field("depends-on", "DependsOn", "forward").
field("superseded-by", "Supersedes", "forward").
}
config impact {
traverse(["DependsOn", "Supersedes", "Verifies"]).
}
config code_path_root {
root(["web", "bin"]).
}
config search_boost {
status("authoritative", 0.08).
status("draft", 0).
hub(0.01).
}
config state {
history_mode("xdg"). # xdg | repo | off
}
Markdown frontmatter has built-in mappings: references, cites, source
and sources are forward Cites; depends-on and based-on are forward
DependsOn; supersedes is inverse Supersedes; superseded-by is forward
Supersedes; discharges and verifies are forward Discharges and Verifies;
affects is inverse DependsOn. A project field entry overrides one key.
Aliases such as related, refs and tracked_by require explicit mappings;
W007 teaches their conventional meaning without projecting edges.
Use config frontmatter { unmapped("sources"). } when a key is metadata on
purpose. Its values remain queryable, its default edge mapping and W007 are
suppressed, and frontmatter_intentionally_unmapped(key) exposes that choice.
A key cannot have both explicit field and unmapped declarations. Unresolved
values of a mapped key produce E001; use the opt-out to express metadata intent.
Markdown discovery follows configured filesystem mounts rather than Git
tracking. When a mounted Markdown file is Git-ignored, status conditionally
names the included count and gitignored_scanned_file(h, file) lists the
handles. Untracked-but-not-ignored drafts remain ordinary corpus files; non-Git
corpora make no Git-ignore claim. Use scan_exclude when an included path is
not part of the intended corpus.
Label namespaces are inferred automatically. Use force(["REQ"]) only for a
sparse namespace that should be recognized before it has enough examples. Use
rejected([...]) for false positives and linear([...]) for obligation
namespaces whose labels must be discharged exactly once.
search_boost adjusts ranking calibration without changing what search can
match. Use exact status names for lifecycle boosts and hub(...) for the
bounded per-incoming-edge boost; anneal describe search_boost shows the
queryable config rows.
Use config diagnostics { escalate("W006", "error"). } to promote a declared
code for this corpus. Info and suggestion may each promote to warning or error;
warning may promote to error. Info and suggestion do not convert into each
other. Unknown codes/classes, downgrades and conflicting grades refuse at load;
identical repeats and equal grades are no-ops. diagnostic carries effective
severity, so check exits and status use it. diagnostic_policy(code, declared_severity, effective_severity, origin) includes all builtin and declared project codes;
no-ops keep origin stdlib. Status names actual promoted codes, and
anneal describe CODE teaches declared and effective severity when different.
Contribute project findings through project_diagnostic(code, severity, subject, file, line, evidence) with codes P followed by digits. Declare each card in
anneal.dl: @diagnostic(code: "P001", severity: "warning", doc: "...", rule: producer, evidence: ["kind", "value"]). The severity is mandatory and
rows use that declared class before policy promotion. Malformed declarations,
invalid literal codes and literal class mismatches refuse at load. Dynamic
invalid/undeclared codes and mismatches emit E003 naming the producing clause.
Project rows compose with builtin findings; they cannot replace the gate stream.
anneal describe P001 records project declaration ownership separately from
grading origin. Declared codes retain cards and policy even with no rows.
Search matches spans and their ancestors. A deepest-span filter selects deepest
matching spans, not textual occurrences; it can drop a match in parent prose
when a child also matches. anneal describe project_diagnostic teaches the
worked example and this boundary.
Use config suppress { rule("E001", "path/to/source.md"). } to accept one
code at an exact diagnostic subject identity. Aggregate rows use their status,
namespace or corpus subject instead. Referenced evidence targets, file-location
fallbacks and globs do not match. Underlying findings remain queryable; the
shared diagnostic stream and check exit omit the accepted instance.
config suppress { code([...]). } refuses at load. anneal describe suppress
teaches the matching unit.
@doc and @verb accept optional intents: ["Goal sentence?", "Another goal sentence."].
Teach two to four goal phrasings, including the capability's boundary. A supplied
list must be nonempty, single-line sentences ending in ? or ., with letters
and at least two words; duplicate phrasings after whitespace and Unicode case
folding refuse. Cards render the goals under Intents. A later documentation
override replaces the goals too, so omitting intents clears older phrasings.
Same-name verbs and primitives retain separate cards.
anneal.dl can also declare rules and project verbs.
# rules run after source facts exist
release_blocker(h, "broken_ref") :=
diagnostic("E001", severity, h, file, line, evidence).
@verb(
name: "release-blockers",
query: "release_row(h, why, milestone) :=\n verb_arg(\"milestone\", milestone),\n release_blocker(h, why),\n *meta{handle: h, key: \"milestone\", value: milestone}.\n\n? release_row(h, why, milestone).",
doc: "Open blockers for the next release.",
output_schema: "{\"h\":\"HandleId\",\"why\":\"String\",\"milestone\":\"String\"}",
args: ["milestone:String"],
capabilities: ["read"]
).
Load order is fixed: config and discovery facts first, source extraction
second, prelude and project rules third, evaluation last. Project predicates
shadow prelude predicates by name and arity. Gate-output relations declared
with shadow: "forbid" are protected: diagnostic/6 cannot be replaced from
anneal.dl, because doing so could make a broken corpus pass anneal check.
Use a separately named predicate for direct evaluation, or
ANNEAL_PRELUDE_PATH when intentionally replacing the whole prelude package.
Project verbs are callable by name:
anneal release-blockers v0.11.0 --format=text
anneal release-blockers --milestone v0.11.0 --explain
Reusable project moves are plain @verb declarations in anneal.dl:
@verb(
name: "area-diagnostics",
query: "area_diagnostic(h, code, file) :=\n verb_arg(\"area\", area),\n diagnostic{subject: h, code: code, file: file},\n area_of{h: h, area: area}.\n\n? area_diagnostic(h, code, file).",
doc: "Diagnostics in one area.",
output_schema: "{\"h\":\"HandleId\",\"code\":\"String\",\"file\":\"String|null\"}",
args: ["area:String"],
capabilities: ["read"]
).
After that declaration is present in anneal.dl, call it like any other verb:
anneal --root docs area-diagnostics '(root)' --format=text
If a project verb is wrong, edit or remove the @verb(...) block in
anneal.dl.
anneal.toml has been replaced by anneal.dl.
If a corpus still has anneal.toml, run:
anneal init --dry-run
anneal init --force
The force path writes unified anneal.dl from the currently loaded config and
moves anneal.toml to anneal.toml.legacy.
Pre-0.11.0 [handles].confirmed inventories are not carried forward.
Namespaces are inferred automatically; add force([...]) manually only for
sparse prefixes that need an explicit policy override. If an existing
anneal.dl still contains confirmed(...), anneal init --dry-run previews a
cleaned file and anneal init --force rewrites it without the obsolete
inventory.
Snapshot history is the automatic migration path. When XDG history is enabled,
pre-0.11.0 repo-local .anneal/history.jsonl is copied into XDG state on the
first write so snapshot-based history queries continue.
Older compatibility commands now return teaching recovery messages instead of
running the retired commands. Use the language-first ladder above:
status plus its recent_frontier/ranked_anchor queries to arrive,
context GOAL when the goal is known, schema/describe to discover,
search/read/handle to retrieve, handle --impact for reverse
dependencies, handle --lineage for Supersedes history, and anneal -e for
precise composite questions.
Common replacements:
find TEXT: anneal -e '? *handle{id: h, kind: kind, status: status}, h contains "TEXT".'get H: anneal handle H or anneal read Hmap: anneal -e '? *edge{from: src, to: dst, kind: kind}.'health: anneal status plus anneal -e '? diagnostic{code: code, severity: severity, subject: h, file: file, line: line}.'diff: anneal -e '? at("snapshot:last") { *handle{id: h, status: old} }, *handle{id: h, status: now}, old != now.'obligations: anneal -e '? undischarged(h), obligation(h), *handle{id: h, file: file, status: status}.'garden: anneal status plus anneal -e '? frontier(h, energy), entropy(h, source).'orient: anneal status, then the printed recent_frontier/ranked_anchor queries; use anneal context "GOAL" once you have a goalimpact H: anneal handle H --impactlineage H: anneal handle H --lineagework: anneal status or anneal -e '? frontier(h, energy), *handle{id: h, file: file, summary: summary}.'blocked H: anneal handle H or anneal -e '? blocker(h, energy, source), h = "H".'diagnostics: anneal -e '? diagnostic(code, severity, subject, file, line, evidence).'broken: anneal -e '? diagnostic{code: code, severity: "error", subject: h, file: file, line: line}.' or anneal checkareas: anneal -e '? area_health(area, grade, files, errors, cross_edges).'trend: anneal -e '? at("snapshot:last") { *handle{id: h, status: old} }, *handle{id: h, status: now}, old != now.'sources: anneal -e '? sources(name, recognizes, capabilities, doc).'query: anneal -eexplain: anneal -e '...' --explainAdapters emit stored relations. Rules and queries consume them.
*handle: handle id, kind, status, namespace, file, line, area, summary,
identity, revision, generation*edge: typed handle relationship*meta: frontmatter and extracted metadata, with role distinguishing
derived rows from modeled or unmodeled authored vocabulary; standard keys
include external_class, target_path, target_start_line,
target_end_line, target_exists, and target_history_status*content: bounded text chunks*span: addressable content spans*concern: concern-group membership*config: ordered runtime config facts*snapshot: history rows for at(...)*generation: source refresh bookkeepingEvery stored row carries enough identity to distinguish corpus, source, origin, revision, and generation.
The diagnostic catalog tracks local consistency and convergence health.
| Code | Severity | Description |
|---|---|---|
| E001 | Error | Broken reference |
| E002 | Error | Undischarged obligation |
| W001 | Warning | Active handle depends on a terminal target classified dead |
| W002 | Warning | Higher lifecycle state depends on lower state |
| W003 | Warning | Missing frontmatter |
| W004 | Warning | Malformed or suspicious frontmatter value |
| W005 | Warning | Lifecycle status has no effective builtin or project classification, or ordering cannot terminate |
| W006 | Warning | Code-authoritative spec cites missing code |
| W007 | Warning | Reference-like frontmatter key has no configured edge mapping |
| W008 | Warning | Frontmatter key contains a mapping omitted by the scalar metadata projection |
| I001 | Info | Section reference summary |
| I002 | Info | Multiple discharges on one obligation |
| S001 | Suggestion | Orphaned label/version handle; file is its declaring file, not an orphaned document |
| S002 | Suggestion | Reserved; namespaces are inferred automatically |
| S003 | Suggestion | Pipeline stall |
| S004 | Suggestion | Abandoned namespace |
| S005 | Suggestion | Concern group candidate |
| S006 | Suggestion | Terminal status lacks a dependency-validity classification |
The workspace is split by runtime boundary:
crates/
anneal-lang/ private parser, AST, source spans, loader (publish = false)
anneal-core/ runtime, facts, store, evaluator, prelude, verbs, trails
anneal-md/ markdown Source adapter
anneal-cli/ CLI surface over core + adapters
anneal-mcp/ MCP library surface over core + adapters
anneal-core has no markdown-specific code. Adapters implement Source and
emit FactBatch values. Retrieval providers, trail recording, policy checks,
verb projection, axis introspection, and source orchestration are separate
runtime seams so future adapters can target code, host runtimes, issue
trackers, and federated corpora without changing the query language. The
prelude exposes the dimensional map through axis and axis_of; command
surfaces are projections over those runtime predicates rather than a separate
documentation layer.
anneal-lang is a private crate (publish = false) until a second consumer
proves the syntax boundary. anneal-mcp ships as a crate/library surface, not
an installed root-CLI launcher. From a source checkout,
cargo run -p anneal-mcp -- --tools prints the crate-level MCP tool catalog.
MIT