Repository navigation
codetopo 0.3.0
First crates.io publication: all seven crates at 0.3.0 — cargo install codetopo-cli codetopo-server codetopo-mcp codetopo-acp.
Added
- The Space bundle recaptures itself. The Hugging Face Space's
data (space-static/data/) was a one-time capture from 0.2.5;
the demo-graph workflow refreshed only the aimlds demo, so the
Space kept rendering the old snapshot after merges. A new
space-recaptureworkflow now rebuildscodetopo-serverfrom
every merge to main, re-indexes this repository with it, and
commits a fresh byte-for-byte capture
(scripts/capture-space-bundle.sh) — but only after the honesty
gates pass (snapshot counts equal stats totals; snapshot audit
head equals the verify head with a valid chain). The bot's data
commit is whathf-syncthen mirrors to the Space; two guards
(paths-ignore + a head-commit check) keep the workflow from
re-triggering on its own output. - Deterministic render:
codetopo render. A layout spec
(JSON) and an SVG drawing that are pure functions of the stored
graph — same graph in, same bytes out, on any machine. Layers
follow the longest dependency path over the SCC-condensed
dependency DAG (cycles share a layer; containment is drawn but
never layers); in-layer order is by node id; coordinates are
integer grid cells. The spec is self-contained and versioned
(layout_version: 1); the SVG consumes the spec alone. Rules
and size behaviour:docs/render.md. - Self-hosted container. A multi-stage
Dockerfile(four
binaries on a slim Debian runtime — they link only libc/libm)
and adocker-compose.ymlthat runs the HTTP API over a/data
volume, with the server's env contract
(CODETOPO_BIND/DATA_DIR/KEYS_FILE/METERING_LOG) wired in.
The server still refuses to start without a keys file.
Workspace crates version 0.2.8.
Fixed
- MCP
tools/callresults are CallToolResults now. Success
results were the tool's bare JSON object; the MCP schema requires
acontentarray, and strict SDK clients parsed the old shape as
no content at all (in-house tests read the bare object and stayed
green — the defect class this fix's wire-level test exists to
catch). Results now carrycontent(the result JSON as a text
block) plusstructuredContentwith the same object. - ACP speaks ACP. The agent's wire protocol was a home-grown
dialect:protocolVersionas the string"1.0"(the spec's is
the integer1), string prompts (the spec's are content-block
arrays), inventedsession/newresult fields, and answers in a
custom field instead of a streamedagent_message_chunk+
stopReason. Rewritten to ACP v1: spec handshake (integer
version, honest capabilities — no session loading, text-only
prompts, no auth methods),{sessionId}results, content-block
prompt text, streamed chunks,end_turn/refusalstop reasons,
andsession/cancelhandled as a notification. Workspace crates
version 0.2.7; live frames from both protocol binaries validate
against the protocols' official JSON schemas
(docs/protocol-proof.md).
Added
- Real import resolution in the graph builder. Discovery named
the packages; this makes their imports link.use a::b::Item
now lands on modulea::b(the longest module prefix — before,
every Rustuseof an item became an external stub); bare
workspace-package imports (npmfrom 'ui-kit', cross-crate
use) resolve through their bindings to the defining module,
with hyphen/underscore normalization; an extractor's
external::pre-mark no longer doubles (external::external::std
is gone); a package's root module is its package node — the id
collision that silently dropped root modules and rejected their
imports withUnknownSymbolis fixed; andcrate::-qualified
calls resolve into the caller's package. Also:pyproject.toml
([project]/[tool.poetry]) joins Cargo.toml and package.json
as a discovery manifest (CODE_SCHEMA names PyPI projects as
packages). Rules documented in CODE_SCHEMA §3.1. - Package/workspace discovery in the indexer. Indexing now
finds the manifest that owns each file — a Cargo.toml
[package]name or a package.jsonname, nearest manifest
wins — and indexes the file under that package with paths
relative to the package root, which is what the extract
contract always specified. Until now one caller-chosen package
and repo-relative paths covered the whole tree, so a workspace
indexed ascorpus::crates::codetopo-core::src::…; ids now
readcodetopo-core::…. Files under no manifest keep the
caller's package (--package, defaulting to the repo
directory name) exactly as before. Workspace crates version
0.2.6.
Added
- Scale gate passed at 10,000 files (
docs/scale-gate.md). A
deterministic 10k-file fixture (generator:
scripts/gen_scale_fixture.py; gate test:
codetopo-cli/tests/scale_gate.rs, run deliberately in release
mode) indexes in ~3.5 s at 134 MB peak RSS (46,001 nodes / 90,017
edges), and the long-pole queries answer in well under a second
in-process. Defect found and fixed: cycle detection (Tarjan
SCC) was recursive — a 10,000-deep call chain overflowed a 2 MB
thread stack and aborted the process; it is now an iterative
Tarjan with identical results, so depth is bounded by heap, not
by the caller's stack. Also correctedschemas/README.md's MCP
tool count (eight → twelve). All seven crates bumped
0.2.4→0.2.5. - Contract drift gates + ACP version honesty. The published
contracts can no longer drift silently: workspace tests (run in CI)
now fail whenschemas/mcp-tools.jsondiffers from the live MCP
tool catalogue or its recorded server version, and when
schemas/openapi.yaml's info version or route set differs from
whatcodetopo-serverserves (its version had been stale at 0.1.0;
now 0.2.4). The ACP agent'sinitializehandshake reports the
crate version instead of a hardcoded0.1.0. All seven crates
bumped0.2.3→0.2.4. - One surface, three doors (final-shape W3). The CLI, HTTP API,
and MCP server now answer the same questions with the same shapes:- Discovery everywhere.
Graph::search_symbols(exact < prefix <
contains ranking) pluscodetopo search,GET …/search, and
codetopo_search_symbols— before this, every query needed an
exact node id nobody could discover from the tools themselves. - Neighborhood everywhere. CODE_SCHEMA §6 names the neighborhood
query binding, but no surface exposed it. Now:codetopo neighborhood,GET …/neighborhood,codetopo_neighborhood,
all serving the coreGraph::neighborhoodcomputation. - One symbol, in full:
codetopo symbol,GET …/symbol,
codetopo_get_symbol; and the schema itself:codetopo schema,
GET /v1/schema,codetopo_describe_schema— one generator in
the CLI library, so the three cannot drift. - One package identity. Indexing without an explicit package now
derives the same name on every surface (the repository
directory's file stem). The HTTP surface previously recorded
unknown, so the same tree produced different node ids — and
different query results — depending on the door used. - CLI
--jsonon descendants / ancestors / blast-radius / path /
stats, emitting the HTTP response shapes (minusindex_id), so
scripts get one format from either door. schemas/mcp-tools.jsonregenerated from the live binary
(twelve tools);schemas/openapi.yamldocuments the new routes.
(No version bump in this PR.)
- Discovery everywhere.
- Extractor conformance (final-shape W4). The graph now carries
the schema's full edge vocabulary:- Reads/writes are first-class. A resolved ref is a
readsedge
and an assignment target is awritesedge — before this, reads
hid insidereferencesedges with akind: "read"attribute,
invisible to kind-walking queries (blast radius included).
CODE_SCHEMA §4 amended accordingly (dated amendment note);
referencesremains restorable from older snapshots. - Heritage edges. Rust
impl Trait for Type→implements,
trait supertraits →inherits; TypeScriptextends/
implementsclauses likewise. New emit channel
(FileSymbols.heritage) resolved by the builder. - Tiers classify. By node kind in the builder — skeleton
(package/module/file)core, type declarationsrecommended,
the restoptional— instead of every node carrying the default. - More of the tree is seen:
.mjs/.cjsfiles, Markdown
files asdocnodes, Rust type aliases and TS enums / type
aliases as type symbols. - Verified on this repo's own code: 1,932 reads edges, writes,
19 doc nodes, tiers distributed — and zeroimplementsedges,
correctly: every impl in this codebase targets an external trait.
(No version bump in this PR — W2 owns the next one.)
- Reads/writes are first-class. A resolved ref is a
- Proof certificates v2 + standalone verification (final-shape W2).
Snapshot certificates now carry a serial number, issuer, signer-key
fingerprint, and a signed one-year validity window; an expired
certificate is refused. Restore and the new
codetopo verify-proof --snapshotcommand share one verification
gate (schema major per CODE_SCHEMA §8, audit chain, certificate) —
restore writes only after the gate passes, and verify-proof runs
the same gate without writing. Certificate v1 files still verify
and are reported as v1 (no serial or validity window). All seven
crates bumped0.2.2→0.2.3. - Persisted diagnostics + store format gate (restored from PR #13,
see the reconciliation note below). Builder diagnostics are
stored with the index and replaced on reindex; the new
codetopo diagnostics --dbcommand prints them after the fact.
The store stamps a format version (PRAGMA user_version) and
refuses databases from newer formats. Thesymbols:line at index
time now counts actual symbol nodes. All seven crates are at
0.2.2.
Fixed
-
Reconciliation: PR #13's W1 changes are back. Two store-
integrity implementations were built in parallel today (PR #12 in
the demo thread, PR #13 in the main thread). PR #12's merge used
its branch tree wholesale and silently dropped PR #13's tree
changes (persisted diagnostics, the format gate, the honest
symbols count, the 0.2.2 bump) even though both merges are in
history. This entry's PR restores them, merged with PR #12's
verified restore: where the two disagreed on restore semantics,
verify-then-replace (PR #12) stands — a snapshot is restored
only after its schema, audit chain, and proof certificate verify,
and restore then replaces by design; PR #13's refuse-populated
variant was not kept, because refusal alone verifies nothing.
Lesson recorded: a hand-built merge must merge trees, not adopt
one side's tree, when the base has moved. -
The store now replaces; it never silently unions. Reindexing or
restoring into an existing database usedINSERT OR IGNORE, so
stale symbols from an older graph survived forever. Indexing and
restore now go throughStore::replace_graph— one transaction
that swaps the whole graph and advances a graph generation
counter; the store also carries ametatable with a schema
version (v1) from here on. -
Snapshot proofs are certificates, and restore verifies.
<snapshot>.proofis now a structured certificate (format id,
algorithm, signer, the snapshot's SHA-256, audit head, and counts,
HMAC-signed) instead of a bare MAC.codetopo restoreverifies
before writing: schema version, the snapshot's audit chain against
its declared head (always), and the certificate whenever one
exists (digest/head/counts/signature). Restoring without a
certificate requires the explicit--allow-unverifiedflag and is
loud about it. Regression tests cover reindex replacement and
every refusal path. -
Blast radius walked the wrong way.
blast_radiusreturned what a
node depends on; it now returns what depends on it — the
dependents that break when it changes — as DESIGN §3 promises.
CODE_SCHEMA §6 corrected to match, and the tests that enshrined the
old direction rewritten, with a chain regression test. -
pathno longer fabricates directed paths. When no directed
path existed, the reverse walk was relabelled as a forward path.
pathnow returnsNonehonestly; the MCP test that expected a
backwards chain is corrected, with a no-path-backwards assertion. -
codetopo verifyexits non-zero on a broken audit chain. It
printed "chain valid: false" and returned success; it now errors,
with a tamper regression test. -
CI added (
.github/workflows/ci.yml):cargo test+
cargo clippy -D warningson every PR and push tomain— the
repo's first automated gate. -
README quickstart verified end-to-end; three drifts corrected
(transcript indocs/quickstart-verification.md). The install
command now names the four packages — a bare
cargo install --git https://github.com/nrupala/codetopofails with
cargo's "multiple packages with binaries found" error. The example
query id now matches what indexing~/my-projectactually produces
(my-project::…— the directory name, hyphen included). The
descendantsexample output header now matches the binary
(2 descendants of …, count first). The fixture numbers (15 nodes /
23 edges and the fullstatsbreakdown) were re-run against the
built CLI and confirmed exact, and the previously undocumented
restorecommand is now listed with the other queries. (Rebased
onto the final-shape main before merge: thestatsbreakdown's
referenceskind isreadssince extractor conformance (W4) —
the README example line was updated to match the current binary
during the rebase, and the fixture was re-run to confirm it.) (The
0.2.1→0.2.2crate bump this change originally carried was
superseded while the PR waited: the final-shape work took the
crates to0.2.3, so this change ships no version bump.)
Added
- Portfolio certification rollout.
CONTRIBUTING.mdnow documents
the PR-flow discipline (draft PR → tests green → owner merges;
every PR adds a CHANGELOG entry under Unreleased and bumps crate
versions; merge commits reference PR numbers; releases tagged
vX.Y.Z). All seven crates bumped0.2.0→0.2.1.
Changed
- Release 0.3.0 — first crates.io publication. All seven
crates publish at 0.3.0 with full registry metadata
(descriptions, keywords, categories, homepage); internal
dependencies now carry registry versions alongside their paths.
Version, tag, changelog, GitHub release, and the crates.io
versions are reconciled at this gate — the v0.2.0 tag predates
the final 0.2.x line and remains as-is for history.