Repository navigation
Releases: tanlnm512/cairn
Releases · tanlnm512/cairn
Release list
0.22.0
[0.22.0] - 2026-10-10
Added
- Parser conformance matrix in
docs/indexing.md: one row per golden-fixtured language (Rust joins as the 15th), rendered byscripts/gen_conformance_table.pybetween markers and kept fresh by a staleness test; every row is green or carries an explicit tier mark (no import/hierarchy edges). - Pyright upgrade pass is now budgeted and on by default:
cairn buildupgrades ambiguous Python call edges when pyright is available, capped by a wall-clock budget (60 s default) and a per-build edge budget (2,000 default), both configurable viacairn.json(lsp.budget_seconds,lsp.edge_budget);--lspforces an unbounded pass,--no-lspskips. Upgrades persist, so a budget-stopped pass resumes from the remainder on the next build, and the report carriesprobed/budget_hitfor observability. - Comment sweep: the comment-style baseline fell from 1213 to 226 grandfathered violations (81% removed) across telemetry, memory, graph, mcp_server, cli, dashboard, parsers, knowledge, and agent_install, with 321 migration ledger rows landing 317 durable rationales in tribal memory (Why/How bodies, ledgered before deletion) and executable ASTs proven identical per area;
python -m cairn.cli.system.audit_status|comment_styleno longer emits the runpy double-import warning (deferred collector imports inreport.py), andscripts/regen_scip_pb2.shnow emits the trimmed_scip_pb2.pyheader so regeneration cannot reintroduce debt. - Quality ratchet:
make audit-statusprints remaining-vs-total audit findings by priority fromdocs/audits/2026-10-02.mdplus a strict.status.jsonfix sidecar, andmake comment-styleenforces the AGENTS.md comment contract oversrc/through a machine-generated shrink-only baseline (comment-style-shrinkre-derives it after fixes; growth fails). Both gates run as thecheck-comment-lengthspre-commit hook and thequality-ratchetCI job, exposeARGS=--json, and surface as a count-onlyquality_gatessection incairn report. The legacy rootarchitecture.htmland audit report moved todocs/diagrams/anddocs/audits/. - Rationale indexing:
cairn buildcaptures comments whose text begins
NOTE:,WHY:, orHACK:in every language with a mapped comment node
type and persists them in an additiverationaletable, each record
attributed to the innermost enclosing callable or type whose span
contains the comment line (file-level otherwise; one record per comment
with continuation lines folded into the text; docstrings never produce
records, andTODO:/FIXME:markers are out of scope). The new
cairn rationalecommand (--symbol/--filetargets,--db,--json)
prints records ordered by line with kind tags (note/why/hack); MCP
exploreoutput adds a gatedRationalesection when any matched symbol
has records and omits it otherwise, with the MCP tool count unchanged;
incremental reindex andcairn updatedelete and re-derive each affected
file's rationale rows together with its symbols, so removed markers never
linger. - Freshness hooks:
cairn update --diff-ref A..Bscopes changed-file
detection to a git ref range (range diff only — the untracked pass and
stat fallback are skipped, and both endpoints are pre-validated so a bad
ref is a clean error);cairn hooks install/uninstallmanage a
post-commit hook (backgrounds a ranged update after every commit — the
previous commit resolves from the reflog, with an empty-tree range
fallback on a repo's first commit — pluscairn validate-paths --mark,
output discarded so the commit never waits) and a post-checkout hook
(branch switches only; file checkouts no-op);cairn init --with-hooks
installs both hooks opt-in and non-interactively. Hooks are never
overwritten when foreign and removed only when cairn-marked; in a
non-git workspacehooks installprints guidance and exits 1 while
init --with-hookswarns and init still succeeds — no partial hooks
either way. cairn communitiescommand: deterministic Louvain community detection over
structural edges, persisted to thecommunities/symbol_communities
derived tables with global and per-community hub symbols (--top-k,
default 10); a store with no structural edges reports "no communities
found". Requires the new[graph-analytics]extra (pip install 'cairn[graph-analytics]') — without it the command exits with the install
hint and leaves the store untouched.- Dashboard
/communitiesview: renders the partition with community sizes,
hub symbols, and member drill-down (nav under Explore, after Graph). - HTTP serving:
cairn serve --transport httpexposes the full MCP tool
registry over Streamable HTTP with bearer-key auth (CAIRN_MCP_API_KEY
env or--api-key, flag wins), loopback-default binding with keyless
operation restricted to loopback binds (a non-loopback bind without a key
refuses to start), Host-header transport security per bind class, an
unauthenticated/healthzprobe, an optional--statelessmode for
load-balanced deployments, and a read-only default; ships with a slim
non-root Dockerfile (CAIRN_HOME=/datavolume, HTTP entrypoint) and
deployment docs. Local stdio and SSE transports are unchanged. - Memory stance overlay: memories carry an optional stance (
preferred/
tentative/contested, orthogonal to the lifecycle tiers);
cairn memory record --stance preferred|tentative|contesteddeclares a
prior at record time, and the newcairn memory reflectrecomputes
stances across the store from supersession and citation-verification
evidence — a memory superseded by a peer when the pair shares a symbol
ref that still verifies is contested and names that peer, a memory whose
refs-verified fraction dropped below its recorded baseline downgrades to
tentative, a memory whose every cited ref verifies is preferred, and the
evidence verdict overrides a declared prior on conflict while the prior
survives when evidence yields no verdict. Reflect touches stance
metadata only — never bodies, titles, tiers, or scores — and is
idempotent and deterministic on an unchanged store; stance renders
inline incairn memory search/listoutput and MCP
recall_memory/exploreoutput. cairn prs: one read-only PR-triage command fusing GitHub state with the
local graph — a six-column open-PR list (number, title, branch, author,
CI state conservatively classified pass/fail/pending/none from
statusCheckRollup, review decision) sourced from theghCLI over the
workspace's remote;--impact PR|branchresolves the PR's diff (fetched
viagh pr diff, never local git) against its base ref to changed
symbols with dependents (depth + resolution), listing unindexed and
deleted changed files explicitly and stating the local index's build age
(Store: local index (built <age>|never)) so impact against a stale
store is visible;--conflictsranks open-PR pairs by shared-community
overlap of their touched symbols (largest overlap first, PR-number
tie-break), consuming thecommunities/symbol_communitiestables
(never building them) and printing arun cairn communitieshint
instead of failing when the tables are absent or empty;--jsonemits
the full report payload (store/prs/impact/conflicts) for agents.
Strictly read-only on both sides: the store opens read-only with no
freshness refresh, and gh runs only as a subprocess with pinned
list/view/diff argv (30 s timeout, no tokens read, logged, or passed) —
gh missing, unauthenticated, timing out, or erroring surfaces as one
actionable error with no partial table.
Fixed
- A4 retro of the post-audit admin-merged PRs:
cairn build --repono longer
crashes with an IntegrityError on files containingNOTE:/WHY:/HACK:
comments and no longer strands a permanentbuildingmarker (the repo-scoped
rebuild now clears rationale rows before their foreign-key parents); the
dashboard Communities page renders an empty panel on stores created before
community indexing instead of erroring. - Full-codebase audit wave (2026-10-02): 141 findings and 3 systemic clusters
triaged; every P0, P1, and P3 finding is now closed (59 fixed, including 2
added by the A4 retro; the remaining 87 P2 polish findings are tracked in
the audit sidecar) across the graph, parsers, memory/LLM, MCP server,
dashboard, wiki, knowledge, CLI, install/uninstall and scripts surfaces. - Hooks no longer silently no-op in source checkouts:
python -m cairn.cli.main
runs the CLI (missing__main__guard), the hook fallback sites work, and the
impact guard tolerates empty tool output. - Graph reads converge when a parser wheel is missing: unavailable-language
files are skipped by the drift and build scans (recorded as parser-unavailable
skips), dropped on reindex even when never indexed, and counted so strict
refresh settles instead of raising on every read. - Incremental updates no longer drop cross-repo imports edges; delete-only
diffs contribute blast-radius seeds; git octal escapes and quoted renames
resolve to real paths;trace_flowvisits each id once on diamond graphs;
taint finds source→sink paths through the entry symbol. - Memory lifecycle on the declared Python 3.10 floor: Z-suffixed timestamps
parse (purge actually purges), aware/naive timestamp mixes degrade instead of
crashing decay and batch-critic, memory_refs delete the id form writers
persist, task complete/drop races are locked with the claimer enforced, and
refusals no longer report as drops. - Parsers: Swift properties and initializers emit symbols (goldens
regenerated), Kotlin primary-constructor default call edges and TypeScript
genericimplementsedges are restored, decorators land on their own
declaration, and external SCIP indexers run inside the target repo. - Dashboard: chains span no longer errors on NULL tim...
0.21.2
[0.21.2] - 2026-09-29
Added
- Dashboard Memory detail pages:
/memory/{tier}/{slug}renders a memory's
body, identity, promotion history, signals and supersedes/superseded-by
links, and the Memory list's titles link them. - Dashboard knowledge graph covers the whole knowledge layer: memory items
join the docs as nodes (diamonds, tier as family) and their frontmatter
supersede pairs draw as directed edges; the node-click inspect panel
resolves memory ids and links the memory detail pages.
Changed
- Test-suite audit: ~200 tests removed or trimmed under the "one owner test
per contract / no implementation mirrors" policy — source-text mirror tests
(CSS/JS greps, import allowlists, DRY lints), near-duplicates of owner
tests, private-constant pins, and trivially-true smoke asserts. Distinct
edge coverage was folded into the owning tests; test-only seams
(MemoryStore/OKFMemoryStore/InMemoryMemoryStore,try_generate_index,
reset_probe_cache,reset_tokenizer_mode) moved into the test suite or
were deleted. - Documentation audit refresh: current CLI/MCP surfaces, 15-language parser
set, optional extras, architecture diagrams, and benchmark methodology now
match the code; the historical roadmap proposal is labeled as a snapshot. - Configuration docs cover
CAIRN_NO_REFRESHandCAIRN_LLM_BACKEND. - Benchmark docs carry the current reference-local perf and 1,000-file
scaling measurements.
Fixed
- Generated Claude/Cursor hook commands shell-quote the interpreter path
(pipx'sApplication Supportlocation no longer exits 127), and
reinstalling agent hooks replaces stale Cairn commands instead of treating
any matching entrypoint as current. - Dashboard
/healthno longer returns 500 when the embed backend is a
server family with no resolvableCAIRN_EMBED_BASE_URL: the failing probe
degrades toNoneand the page renders it as unknown. - Dashboard history pagination no longer loops on the same page when the
oldest row of an all-time page has a NULLinvoked_at(legacy-shape
tool_metricstables): thenextcursor is omitted instead of being
unparseable. cairn doctormarkup-escapes the dynamic freshness hint, so a
workspace-derived repo id containing[cannot corrupt rich's markup.- C4 architecture diagrams now agree with the boot guard everywhere (25 MCP
tools, groups 12/5/2/6; previously 22/24/9 across labels). - Agent-integration package data now ships only template/script file types,
keeping generated__pycache__bytecode out of sdists and wheels. - The dashboard first-render budget test judges the 200ms SC-1 budget on the
best of 5 fresh-app trials, so full-suite scheduler noise no longer fails it. - Agent-suite benchmark docs report the modal est_tokens median (1,479 /
99.7%) and scope the determinism claim to tool calls; estimated tokens vary
by at most about 2% run-to-run. - The AGENTS.md template
cairn install-agentswrites carries the current
tool-layer names, matching the repository copy (parity test added). - The oversized reserved-block pack test pins heuristic token costs, keeping
it independent of the process-wide exact-tokenizer mode. cairn build --repono longer crashes with an integrity error on any repo
embedded with the default multivector pass:_clear_repodeletes the
repo'sembeddings_mvrows before deleting its symbols.cairn doctorno longer spawn-executescommand/args/envread
verbatim from workspace-owned registration config files: only an entry
exactly in the shapecairn install-agentswrites is probed, and any
other shape degrades to a WARN naming the file.- The doctor memory-staleness check degrades to a WARN when its
memory_refsread fails, restoring the diagnostic's never-raises
contract. - CAIRN_ANN_BACKEND resolution lives in one shared helper, so the ANN
index, doctor, dashboard, and MCP server agree on the empty-string
opt-out. - C4 containers-level diagram says tree-sitter ×15, matching the parser
registry and the context level (was ×14).
0.21.1
[0.21.1] - 2026-09-21
Added
- SCIP compiler-index support, redesigned as an opt-in edges-only overlay:
a configured (or once auto-generated).scipindex contributes exact
call/reference edges over the unchanged tree-sitter symbol graph —
per-file authoritative, position-joined, per-document repo attribution,
with a join-anomaly guard that retains tree-sitter edges observably.
Seven-language run-once auto-generation registry (swift, java, kotlin via
scip-java, typescript, python, go, rust) withscip_gen_*degradation
records;cairn import-scipCLI and thescipconfig key;[scip]
extra restoring the vendored protobuf stub;statsgainsedge_sources
andexact_share_by_language, build summary rendersscip: n edges (d disagreements);cairn updatere-applies the configured overlay after
reindexing (import-only, never a generation);docs/scip.mdtoolchain
guide.
Changed
- Transitive closure rebuilt as an in-memory level loop with a single write
pass (byte-identical row sets, ~2× faster on cairn's own repo) and now
enforced by a CI budget gate — ≤60 s wall / ≤512 MB peak at the 1000-file
scaling point with 5× structural edges (measured 6.0 s / 228 MB), plus a
≤2 s budget for one incrementalmaintain_transitive_closurecall at
the same point, whose reads are scoped to the affected subgraph. edgestable gains an additivesourceprovenance column (NULL reads as
tree-sitter); local commit gate's staged-files ruff honors
extend-excludevia--force-exclude.
Fixed
- Agent hook stdout stays protocol-clean:
post_editandsession_end
emit cairn/prose diagnostics to stderr instead of stdout (the calling
agent parses Stop/PostToolUse hook stdout as JSON), andsession_start
routes digest subprocess failures to stderr so no error prose reaches
the agent's context. - The SCIP import's write phase is atomic: every fallible read completes
before the first delete, and the deletes/inserts run inside a savepoint
that rolls back on failure — a mid-import error can no longer commit
orphaned per-file deletes that permanently stripped tree-sitter
calls/references edges from already-processed files. - SCIP
localsymbols resolve only against same-document definition sites
(they are document-scoped by spec), instead of joining a same-named
definition from an unrelated file. _apply_scip_overlaylogs a failed config read like every other
degradation instead of returning silently.cairn import-scipexits non-zero when no index document matches the
graph (the workspace/db mismatch case that printed a normal-looking
"0 edges" line), and the success line reports matched/skipped document
counts.scripts/regen_scip_pb2.shsplices the generated stub at the detected
header end instead of a hardcoded line number that could disagree with
the accepted header range.
0.21.0
[0.21.0] - 2026-09-17
Added
- Bi-temporal memory: every memory carries
valid_from/valid_until
(additivememory_validityprojection + concept extensions, one-time
backfill);recall_memory/cairn memory searchaccept as-of dates;
cairn validate-paths --markexpires stale-reference memories at build
time with unique-successor links instead of deleting;cairn memory timeline <symbol>renders a symbol's memory history. - Multi-agent memory coordination:
cairn memory share --agent <id> --symbols a,b,cpublishes a learning to other agents' recall;cairn memory checkwarns on other agents' recent symbol activity (7-day
window);search --agent/recall_memory(agent=...)merge shared
entries with attribution; sharing unused keeps single-agent behavior
byte-identical. - Security-aware blast radius: configurable taint sources/sinks (generic
call-name defaults,cairn.jsontaintoverrides), inter-procedural
propagation over precise call edges (--fuzzyopt-in, depth-capped),
cairn taint --from --topath tracing, and taint-path warnings in
exploreandcairn blastoutput. - Closed review loop:
cairn review --base <ref>emits a structured
review-context pack (blast radius + memories + compass + wiki);
--pre-submit [--gate]warns on mistake/pattern memories keyed to the
diff;--capture-eventrecords resolved review comments as draft-tier
symbol-keyed memories; advisory.github/workflows/review.ymlposts
findings as PR comments from a committed seed bundle. cairn pack --task <text> --budget <tokens>: one-shot token-budgeted
context block — semantic+lexical seed union, one-hop precise expansion,
closure-gated centrality ranking, greedy budget fitting with per-class
drop reporting, and per-symbol source/blast/compass/memory enrichment;
deterministic, offline, no LLM. Benchpack-fit-ratearm: fit 1.00 at
99.7% token reduction vs the grep baseline.- Cross-repo federation:
federated_searchMCP tool (inventory is now 25
tools) andcairn federated-searchmerge per-store results with
per-repo attribution and rank-level RRF fusion (lexical fallback for
embedding-less stores, named dropped-store states);cairn ask --all-reposcomposes per-repo answers; opt-in--shared-embedserves
stamp-compatible stores from one embedding backend. cairn bench --suite swe-bench: deterministic (no-LLM) two-arm
efficiency measurement over a pinned 50-instance SWE-bench_Lite subset
(frozen manifest at a recorded HF revision;datasetsrides the new
benchextra); byte-identical reruns, hermetic-m coreCI smoke;
published medians: 84.6% tool-call / 99.0% token reduction vs the
grep/read control, fresh-clone reproducible (resolve-rate intentionally
out of scope for the deterministic arm).- VS Code extension (
extensions/vscode/, vsix package): zero-config
adopt-or-spawn of the local dashboard server, status item with graceful
degradation (unindexed/stopped-server states), hover blast radius,
caller/callee code-lens counts, and a compass/memory panel — over frozen
editor JSON routes (/editor/symbol|file|status) on the dashboard app;
tag-triggeredextension-release.ymlbuilds the vsix and validates
install + activation on a headless clean machine. - Query-time freshness: every graph MCP tool and CLI query command probes
(size, mtime)drift and reindexes changed files before answering;
CAIRN_NO_REFRESH=1/--no-refreshand read-only stores answer from the
stored graph with a probe-derived staleness banner (no[watch]extra
required). cairn blastcomputes the reverse-edge blast radius of a git diff
(working tree vsHEAD, or--base <ref>vs merge base) with
text/markdown/mermaid/json renderers, precise edges by default (--fuzzy
opt-in), innermost-symbol seeding by stored spans, and id-based traversal.cairn map(CLI + MCPrepo_map): deterministic repo orientation —
per-repo directory clusters with counts, in-degree hubs, global hotspots,
explicit dropped counts; zero LLM.- MCP
file_api: signature-only symbol surface per file (kind, qualified
name, synthesized signature, span; never bodies) with repo disambiguation. cairn grep: regex search over indexed files grouped by innermost
enclosing symbol, ranked by in-edge coupling, with-i/--fixed/--in
and explicit truncation counts.- Wiki/compass pages preserve human-authored
## Notessections
byte-identically across regeneration; the critic does not evaluate Notes
content. cairn viz --export FILE.htmlwrites a single self-contained HTML graph
view (inline vis-network, no network assets).cairn build --lsp: opt-in pyright stdio pass upgrades uniquely resolvable
ambiguous Python edges toexact; missing/failing server is a no-op with a
notice; existingexactedges are never downgraded.- Rust (
.rs) indexing via a generic tree-sitter tier (pinned
tree-sitter-rust); call edges stayunresolved(neverexact). discover_reposoptionally includes initialized submodules and nested
repos (include_nested_repos, default off, prefixed repo ids); linked git
worktrees seed an independent store from the main checkout then refresh
drift.cairn dataflow build --max-symbols Ncaps the public symbols indexed per
run;CAIRN_DATAFLOW_MAX_SYMBOLSsets the cap for every builder entry point
(cairn build,cairn sync,cairn dataflow build). The partial-index
warning names the env var and flag.
Changed
- The
[ingest]extra pinsonnxruntime<1.24on Python 3.10 so
pymupdf-layoutresolves to an installable wheel set. - Dataflow builds upsert rows in batches and compute the repo-keyed
cross_repo_depspayload once per repo per run instead of once per symbol. - CI test legs run the suite under pytest-xdist (
-n auto --durations=25);
PR legs run the Python 3.14 ceiling only — the full 3.10-3.14 matrix still
gates every push to main. Eval-path tests pin the dep-free hash embedder,
keeping the semantic model out of the retrieval loop. - CI installs every job from the committed uv.lock via
uv sync(the
lockfile is the tested dependency set, not a fresh pip resolution), runs
the full 3.10-3.14 matrix on merge-group entries as well as main pushes
(merge-queue-ready), and sets per-jobtimeout-minutes.
0.20.2
[0.20.2] - 2026-09-13
Added
cairn statsreports edge resolution shares (exact/ambiguous/
unresolved) over the calls/references candidate pool.cairn updateembeds changed files' symbols when a semantic backend is
reachable, and reports deferred embeds when none is available.cairn.jsoncommitted at the repo root excludes vendored dashboard static
chunks and benchmark datasource corpora from indexing.
Changed
cairn embedbuilds the multivector kinds (name,docstring) by
default;--no-multivectorrestores single-vector builds.reindex_pathssettles embed_symbols' buffered-batch degradation, keeping
an open transaction from failing subsequent file legs or losing rows.- Shared graph helpers (
_select_stale_symbols,_purge_embedding_rows,
_repo_relative_path) replace duplicated logic across the embedding and
incremental paths.
0.20.1
[0.20.1] - 2026-09-11
Removed
- The SCIP compiler-index ingestion path:
cairn import-scip, the
cairn.jsonscipkey, the[scip]extra (protobuf runtime), the
vendored protobuf stub with its regeneration script, and the external
indexer auto-generation orchestrator. Indexing is tree-sitter-only for
every language; a stalescipkey incairn.jsonis ignored like any
other unknown key.
Added
- Parser-emitted binding signals for all fourteen languages: import
aliases (imports.local_alias), definition arity (symbols.arity),
call arity, and receiver types for the nine parsers that previously
emitted none (python, c/cpp, csharp, dart, js/ts, objc, swift). The
resolver consumes them: a bare call through a local import alias
resolves exact, and same-named overloads split by argument count when
exactly one arity matches. Receiver-typed lookups that collide across
same-named types now narrow by same-file and fall through the tier walk
instead of degrading to ambiguous. Intra-workspace exact share rises on
the measured corpora (self 0.2248 → 0.2429; ds2/attrs 0.6546 → 0.6781)
with retrieval ground truth unchanged (558/558 + 234/234).
0.20.0
[0.20.0] - 2026-09-10
Added
- The dashboard graph tab draws the whole picture instead of an empty
canvas whenever a scope's filter is blank:symbolandimpactscopes with no focal name render the
workspace overview — the most-connected symbols on one canvas,
ranked by degree (fan-in + fan-out), capped at 50 symbols and 100
edges, tests excluded by default with the Include-tests toggle
opting back in, vendored/minified assets never candidates, and
edges deduplicated on(source, target, kind). Metadata reports
tests_included,truncated, andvendored_excludedso a
count at the cap is visibly truncated. A focal name still draws
that symbol's neighborhood; a name that matches nothing still
renders the empty state.- The
reposcope with no repo id buckets every repo in the store,
capped at 30 buckets with an honesttruncatedflag in metadata.
- The MCP
visualize_graphtool rides the same viz layer: module
diagrams include their real internal edges, andformat="json"
output for the repo scope carries thetruncatedfield. - The dashboard sidebar's Database item has an icon (hard-drive glyph)
like every other nav view; a source-pinned test fails if a future
nav view ships without a glyph.
Fixed
- The dashboard's workspace selector names the launch workspace (e.g.
polaris (launch)). The selector's no-selection option serves the
launch store — the dashboard process's own db — so the label shows
which workspace every view queries by default. The generic
Launch workspacelabel applies only to custom--dbpaths outside
the store registry. - Module- and symbol-scope edge queries apply the kept-node target
predicate in SQL before the 100-edge cap, so the cap lands on real
internal edges and the rendered edge set is stable across scans.
0.19.1
[0.19.1] - 2026-09-10
Added
- Relationship-kind enrichment across extraction and display. The graph's
canonical edge-kind vocabulary is nowcalls,extends,implements,
embeds,with,references,decorates,imports,contains.- Python base classes emit
extends(wasimplements); decorators emit
decorates; signature type annotations emitreferences. Swift splits
a class's first inheritance target toextends(the superclass) from
protocol conformances (implements). Go struct/interface embedding
emitsembeds. Kotlin type annotations emitreferences. - Every build synthesizes one
modulesymbol per file (dotted
repo-relative qualified name), derivescontainsedges for nesting
(module → top-level, parent → nested), gives module-level code an edge
owner, and materializesimportsmodule-to-module edges from the
imports table by longest-suffix path matching (ambiguous and external
imports are skipped). Incremental syncs refresh the affected repo's
importsedges. - The dashboard graph canvas colors and dash-patterns edges by kind from
new--edge-*theme tokens, renders an edge-kind legend whose chips
filter whole kinds, and the symbol/neighbors/inspect views pass the
stored edge kind through verbatim (previously every edge rendered and
labeled ascalls). - Resolution semantics preserved: module symbols are excluded from the
resolver's candidate indexes (a file named after its single class
would otherwise make same-name references ambiguous),embedsjoins
extends/implementsin the receiver-dispatch ancestor index, and
the new kinds stay outsideSTRUCTURAL_EDGE_KINDSso impact/trace
radius is unchanged. Requires a rebuild (cairn build) to take
effect. The DS-v2 ground-truth seal's recorded per-corpus build
facts are re-pinned to the new deterministic counts (558/558
expectations and tree_hash pins unchanged).
- Python base classes emit
Fixed
- The doc-link completion gate now confines proposed edges to the
completing task's island members: a result naming an existing doc
outsidefacts.membersis rejected with no writes (the task stays
in-progress and re-completable), instead of writingkind: inferred
edges between arbitrary knowledge docs.
0.19.0
[0.19.0] - 2026-09-09
Added
- The dashboard gained a
/knowledgecatalog over the ingested
knowledge docs: every stored doc lists exactly once with its family
(decision / spec / workflow / business-rule), status, related-doc
count (from the derivedknowledge_edgesindex), tags, and last
update. Family/status/tag filters morph-swap the results region as
htmx fragments (no page reload), rows link wholesale to
/knowledge/{family}/{slug}— a detail page rendering the doc's
title, badges, and markdown body (relationship panel follows in the
next knowledge view) — an empty workspace renders an explicit
empty state namingcairn knowledge ingest, and a store predating
the relationship index still catalogs with zero link counts. The
sidebar's Knowledge section leads with the catalog entry, the landing
grid carries a Knowledge card, and the command palette offers both
the catalog and a palette-only Knowledge Graph destination. - The
/knowledge/{family}/{slug}detail page gained the relationship
surfaces: a supersede chain widget (ordered oldest -> newest, each
member linking its detail page, the hop labeled with its relation and
a direction arrow, the viewed doc highlighted), a relationship panel
grouping every stored neighbor row under its relation with a kind
badge (extracted/inferred/derived— the same tuples
cairn knowledge relatedprints; inferred wears a dashed badge), an
explicit empty state for docs with no relationships, linked code refs
with their kind and per-ref resolution status (symbols deep-link into
the graph), and ingest provenance — source repo/path and the doc's row
in the workspace's staged.cairn/ingest-outbox/manifest.json, or an
explicit note when a doc was added directly to the store. A store
predating the relationship index renders empty panels, never an error. - The dashboard gained the
/knowledge/graphrelationship canvas (the
palette's Knowledge Graph destination, linked from the catalog):
every stored doc is a node and every indexed relationship a directed
edge drawn exactly as stored — a supersede pair reads as the two-way
link ("supersedes" on the newer doc, "superseded-by" on the older)
the related CLI and the detail panels report, with edges carrying
theirrelation+kindso inferred links render dashed and
extracted/derived links solid. The legend doubles as the filter:
kind/relation chips toggle whole edge groups client-side (no reload,
no refetch), the counts line tracks the visible share, and a node
click fetches the doc's inspect fragment (/knowledge/graph/inspect)
into the side panel — identity, relationships with kind badges, and
the full-detail link. The canvas re-themes live via the shell's
cairn:theme-changedbroadcast and drops physics under
prefers-reduced-motion: reduce(the /graph machinery's conventions). - Vendored Mermaid 11.17.2 (
mermaid.min.js, MIT) as the dashboard's last
external asset: the wiki detail view now loads it from/static/via a
lazy dynamicimport()that ships only on pages holding a mermaid fence
(replacing the jsDelivr CDN import), so fence-free wiki pages make no
mermaid request and the dashboard is fully offline at runtime. Fences
render explicitly viamermaid.run()after the late import (startOnLoad
off), with the code block kept as the no-JS/unavailable fallback. - The dashboard serves its own icon: the shell head links the vendored
favicon.svg, and the conventional/favicon.icopath serves the same
file, so the browser's automatic icon probe is a 200 instead of a 404
on every page load. - The
/graphsymbol inspect panel loads as an htmx fragment: the
node-click fetch rideshtmx.ajax(theHX-Requestheader keys the
route's fragment branch) and swaps the server-rendered panel —
identity, callers, callees, impact with affected tests, neighbor rows
deep-linking into the symbol-focused view — into#graph-panelwith
no page navigation (sidebar/topbar persist by node identity). A stale
response from an earlier selection is cancelled athtmx:beforeSwap;
requests without the header keep the JSON payload. The
/knowledge/graphinspect fetch rides the samehtmx.ajaxseam.
Changed
- The graph and database canvases re-theme live through CSS variables.
The per-kind node palette moved from hardcoded JS color tables into the
theme blocks as--kind-*tokens, and the shell broadcasts a single
cairn:theme-changedevent on a theme flip (onedata-themeobserver
app-wide, in shell.js); both canvas scripts re-read every color through
the getComputedStyle token proxy on that event, so toggling the theme
re-colors the live canvas in place — no reload, no new graph requests. - The /graph physics simulation is disabled under
prefers-reduced-motion: reduce— JS-driven motion the CSS media
query cannot reach. The graph then lays out on the deterministic
spiral (a static constellation, no stabilization animation), and the
canvas zoom/fit controls skip their eased animation. - The dashboard's list views filter and live-refresh through htmx
fragments. Filter forms on/history,/tasks, and/wikiare htmx
triggers (input changed delay:300ms; the tasks select onchange)
that morph-swap only the results region — no page navigation, the URL
and in-progress filter input are untouched, and the form lives outside
the swapped region so a swap can never destroy a field. The
#refresh-regionlive refresh on/history,/tokens, and/chains
is now an htmx poll (hx-trigger="every 5s"): each cycle re-fetches
the view'sHX-Requestbranch as a cheap region fragment and
alpine-morphs it over the region in place, replacing the old
full-page re-fetch/DOMParse swap; the live chrome (state word, pause
toggle, disconnect banner) is driven from the htmx event lifecycle —
pause refuses the poll's request viahtmx:beforeRequestwhile the
timer keeps its schedule, and window scroll is re-anchored around each
swap. The same routes serve full page or fragment from one handler
via theHX-Requestheader. The command palette is a native
<dialog>element driven by an Alpine
component:showModal()provides the focus trap and Esc-to-close, and
focus returns to the launching element on every close path. Rows come
from the same two sources as before — the server-rendered seed JSON
draws the initial list, and typing fetches filtered rows as an htmx
fragment from the new/palette/resultsroute (the same view/workspace
seed composition plus the/graph/suggestsymbol data function, with
the selected store riding the request). Keyboard behavior is
unchanged: Ctrl/Cmd+K toggles, arrows move the active row, Enter
navigates. - Accessibility pass over the dashboard shell and views. Keyboard focus
is always visible: the global:focus-visiblering (2px accent
outline) stands everywhere, the shared filter-bar inputs keep the ring
alongside their focus tint, the palette search input carries an inset
ring (the card clips an outward one) plus anaria-label, and no
stylesheet rule removes the focus outline. Every icon-only button
exposes a non-empty accessible name (theme toggle and sidebar collapse
already flip their labels with state), pinned by a hermetic sweep over
all fourteen main views.prefers-reduced-motion: reducecollapses
motion to ~zero — transitions/animations run at 0s, smooth scrolling
stops, the live-refresh pulse stops outright — while toggles, palette,
and htmx swaps behave as before. The shared table macro's real table
semantics (thead/tbody,<th scope="col">) are pinned by test.
Added
- Vendored the dashboard's interaction stack as static assets (zero CDN,
zero build step): htmx 2.0.10 (htmx.min.js, 0BSD), Alpine.js 3.17.1
(alpine.min.js, deferred, MIT), its morph plugin (alpinejs-morph.min.js,
MIT), and thealpine-morphhtmx extension (alpine-morph.js). The shell
loads all four from/static/with the?v=cache-buster, runs htmx with
allowEvaldisabled via thehtmx-configmeta, and ships the
[x-cloak] { display: none }rule so Alpine-managed DOM never flashes
pre-init. Route handlers gain theis_hx_requestseam (theHX-Request
header) for serving full pages vs fragments from one route.
Added
cairn knowledge list --jsonemits one machine-readable row per
document —concept_id(bare bundle-relative id),title,doc_type,
doc_status— so callers and validators can pin document ids without
search --jsonor sqlite. Human output and the--type/--status/
--tagfilters are unchanged.- Dangling relationship pointers are surfaced instead of silently
skipped: a declaredrelates_topointer matching neither a knowledge
concept id nor a recorded resource path warns by name — in the ingest
dry-run output (before any write, checked against the store plus the
staged run), inknowledge ingest --ingestoutput (from its post-write
index rebuild), and incairn knowledge rebuildoutput. The pointer
stays in the frontmatter (the durable record) and still never reaches
knowledge_edges; relationship-free documents ingest and embed exactly
as before, and theknowledge ingestflag set and the 22-tool MCP
surface are unchanged. - Island detection behind the LLM task queue: connected components over the
knowledge_edgesdoc graph surface pair-units detached from the corpus —
two-doc components and id-ordered singleton pairs — and queue one
doc-linktask per pair viaknowledge ingest --ingestandcairn knowledge rebuild; task facts carry the member concept_ids and titles,
and queueing is deduped per member set so re-runs never duplicate tasks.
Completing adoc-linktask throughcairn task claim/task complete --result-fileis gated by a deterministic critic that verifies every
pro...
0.18.0
[0.18.0] - 2026-09-02
Added
- Dashboard database view (
/database, System section): the store's
schema rendered as an interactive relationship graph on the same
vis-network canvas as the Graph view — tables are nodes sized by row
count, solid edges are declared foreign keys, dashed edges are
*_idreferences implied by column naming (a column already covered
by a declared FK is never re-inferred), internalsqlite_%/FTS shadow
tables are excluded, and selecting a node opens a side panel with the
table's columns, types, and incoming/outgoing references. Theme flips
re-color the live network. - Dashboard Memory view categorization: a Type filter pill row
(all · decision · pattern · mistake · workaround) above the table,
mirroring the Wiki catalog's state filter —?type=narrows the list
server-side before the newest-first limit applies, an
out-of-vocabulary value silently falls back to the unfiltered page,
the active pill renders highlighted, and the empty state names the
category when a filtered type has no records.
Fixed
- Dashboard alignment: every
.graph-controlsrow (graph filter bar,
settings embedding config, history/tasks/wiki filters) now renders on
one baseline — selects, text/password/number inputs, and submit buttons
share a fixed control height (their differing intrinsic widget heights
previously sat raggedly under the rows' bottom alignment), checkbox
toggles render as one centered line instead of stacking the checkbox
above its caption, and inline status chips (env-pin markers, the API-key
state) meet the same bottom line as the controls they annotate. Data
tables no longer collapse under width pressure: timestamp cells hold
one line instead of wrapping mid-string at their hyphens, and long-ID
codechips (memory IDs, history sessions, task IDs/resources) cap to
one ellipsized line with the full value on thetitletooltip instead
of being squeezed to a one-glyph sliver by the auto table layout.
Changed
- Dashboard sidebar: the collapse control and theme toggle are icon-only
square buttons — a chevron that flips with the collapse state, and a
sun/moon glyph showing the theme the button switches to (sun while
dark, moon while light). The visible text is gone; the accessible
name and tooltip state the action ("Switch to light theme" /
"Collapse sidebar") and stay in sync in both states.