Skip to content

codetopo 0.3.0

Choose a tag to compare

@nrupala nrupala released this 11 Oct 03:26
· 110 commits to main since this release

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-recapture workflow now rebuilds codetopo-server from
    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 what hf-sync then 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 a docker-compose.yml that 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/call results are CallToolResults now. Success
    results were the tool's bare JSON object; the MCP schema requires
    a content array, 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 carry content (the result JSON as a text
    block) plus structuredContent with the same object.
  • ACP speaks ACP. The agent's wire protocol was a home-grown
    dialect: protocolVersion as the string "1.0" (the spec's is
    the integer 1), string prompts (the spec's are content-block
    arrays), invented session/new result fields, and answers in a
    custom field instead of a streamed agent_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/refusal stop reasons,
    and session/cancel handled 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 module a::b (the longest module prefix — before,
    every Rust use of an item became an external stub); bare
    workspace-package imports (npm from '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 with UnknownSymbol is fixed; and crate::-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.json name, 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 as corpus::crates::codetopo-core::src::…; ids now
    read codetopo-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 corrected schemas/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 when schemas/mcp-tools.json differs from the live MCP
    tool catalogue or its recorded server version, and when
    schemas/openapi.yaml's info version or route set differs from
    what codetopo-server serves (its version had been stale at 0.1.0;
    now 0.2.4). The ACP agent's initialize handshake reports the
    crate version instead of a hardcoded 0.1.0. All seven crates
    bumped 0.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) plus codetopo 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 core Graph::neighborhood computation.
    • 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 --json on descendants / ancestors / blast-radius / path /
      stats, emitting the HTTP response shapes (minus index_id), so
      scripts get one format from either door.
    • schemas/mcp-tools.json regenerated from the live binary
      (twelve tools); schemas/openapi.yaml documents the new routes.
      (No version bump in this PR.)
  • 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 reads edge
      and an assignment target is a writes edge — before this, reads
      hid inside references edges with a kind: "read" attribute,
      invisible to kind-walking queries (blast radius included).
      CODE_SCHEMA §4 amended accordingly (dated amendment note);
      references remains restorable from older snapshots.
    • Heritage edges. Rust impl Trait for Type → implements,
      trait supertraits → inherits; TypeScript extends /
      implements clauses likewise. New emit channel
      (FileSymbols.heritage) resolved by the builder.
    • Tiers classify. By node kind in the builder — skeleton
      (package/module/file) core, type declarations recommended,
      the rest optional — instead of every node carrying the default.
    • More of the tree is seen: .mjs / .cjs files, Markdown
      files as doc nodes, 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 zero implements edges,
      correctly: every impl in this codebase targets an external trait.
      (No version bump in this PR — W2 owns the next one.)
  • 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 --snapshot command 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 bumped 0.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 --db command prints them after the fact.
    The store stamps a format version (PRAGMA user_version) and
    refuses databases from newer formats. The symbols: 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 used INSERT OR IGNORE, so
    stale symbols from an older graph survived forever. Indexing and
    restore now go through Store::replace_graph — one transaction
    that swaps the whole graph and advances a graph generation
    counter; the store also carries a meta table with a schema
    version (v1) from here on.

  • Snapshot proofs are certificates, and restore verifies.
    <snapshot>.proof is 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 restore verifies
    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-unverified flag and is
    loud about it. Regression tests cover reindex replacement and
    every refusal path.

  • Blast radius walked the wrong way. blast_radius returned 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.

  • path no longer fabricates directed paths. When no directed
    path existed, the reverse walk was relabelled as a forward path.
    path now returns None honestly; the MCP test that expected a
    backwards chain is corrected, with a no-path-backwards assertion.

  • codetopo verify exits 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 warnings on every PR and push to main — the
    repo's first automated gate.

  • README quickstart verified end-to-end; three drifts corrected
    (transcript in docs/quickstart-verification.md). The install
    command now names the four packages — a bare
    cargo install --git https://github.com/nrupala/codetopo fails with
    cargo's "multiple packages with binaries found" error. The example
    query id now matches what indexing ~/my-project actually produces
    (my-project::… — the directory name, hyphen included). The
    descendants example output header now matches the binary
    (2 descendants of …, count first). The fixture numbers (15 nodes /
    23 edges and the full stats breakdown) were re-run against the
    built CLI and confirmed exact, and the previously undocumented
    restore command is now listed with the other queries. (Rebased
    onto the final-shape main before merge: the stats breakdown's
    references kind is reads since 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.2 crate bump this change originally carried was
    superseded while the PR waited: the final-shape work took the
    crates to 0.2.3, so this change ships no version bump.)

Added

  • Portfolio certification rollout. CONTRIBUTING.md now 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 bumped 0.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.