diff --git a/CHANGELOG.md b/CHANGELOG.md index bfbd5562..6d29ac4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,35 @@ only when an incompatible change is made to that surface. See ## [Unreleased] +## [1.4.0] — 2026-06-30 + +Feature release on top of `1.3.1`. (Cargo SemVer `1.4.0`; Python wheels `1.4.0`.) +Highlights: a new `loomweave-llm` provider crate (so `loomweave-core` links no +outbound HTTP client), the Warpline churn consumer lighting up the churn/recency +read surfaces, Rust-plugin dead-code reachability roots (ADR-054), and a batch of +federation-keying and graph-integrity fixes. + +### Added + +- **Warpline churn consumer.** Loomweave now consumes Warpline's frozen churn + read to populate the `high_churn` and `recently_changed` surfaces + (`entity_high_churn_list` / `entity_recent_change_list`). The consumer speaks + `warpline-mcp`'s newline JSON-RPC with a bounded timeout and honest + paging/keying disclosure. Federation is enrich-only and fail-soft — with + Warpline absent the surfaces degrade cleanly. +- **Rust-plugin dead-code reachability roots (ADR-054).** The Rust language + plugin now emits reachability-root tags — visibility / entry-point / test / + handler roots, framework handlers, public-method rooting, and edition-2024 + `#[unsafe(no_mangle)]` / `#[unsafe(export_name)]` FFI entry-points — the Rust + analog of the Python public-surface roots, so Rust dead-code analysis no longer + reports the whole public API as dead. + ### Changed +- **LLM / embedding providers extracted into a new `loomweave-llm` crate.** The + provider traits and implementations moved out of `loomweave-core`; a CI gate + now asserts `loomweave-core` links no outbound HTTP client. No change to the + `summary` API or provider configuration. - **SEI git-rename consumer re-pointed to `legis`'s `/git/rename-feed`.** `LegisGitRenameSource` now reads the committed leg of `legis`'s additive superset endpoint `GET /git/rename-feed?base=…&head=HEAD` instead of the legacy @@ -24,6 +51,45 @@ only when an incompatible change is made to that surface. See working-tree authority). Enrich-only and fail-soft as before; the wire-drift guard now warns on a legacy flat-array body or a `committed`-less envelope. (Federation ledger B3.) +- **Filigree federation transport over newline JSON-RPC.** The filigree-mcp + federation client now speaks newline-delimited JSON-RPC instead of + `Content-Length` framing (#78). + +### Fixed + +- **Briefing-blocked entities keep their SEI on the read surface.** Federation + keying no longer nulls the Stable Entity Identity of briefing-blocked entities + on the MCP read path, so Filigree/Wardline lookups keyed by SEI resolve + (#79; PDR-0008). +- **Same-locator collisions are disclosed on the entity read path.** A duplicate + entity locator now surfaces as an entity-anchored `LMWV-DUPLICATE-LOCATOR` + finding anchored to the colliding (survivor) entity, so the shadowed + declaration is queryable from the entity read path instead of silently + resolving to a clean row (clarion-48af930f2a, #74). +- **Stale `contains` edge pruned when a file-scope claim moves.** The writer + prunes a stale parent/`contains` edge when a `file_scope` entity's claim moves, + self-healing the module/package dual-claim mismatch on re-emit + (clarion-abda98c869, #75). +- **Incremental re-analyze on a tag-schema move.** A plugin's files are + re-dispatched when its ontology tag schema changes between runs, so tag-driven + surfaces don't go stale on an incremental walk (clarion-e12d424f1d, #71). +- **Plugin findings can no longer forge the trusted anchor.** + `validate_plugin_finding` strips the host-reserved `anchor_entity_id` metadata + key, so a plugin-reported finding can no longer override the trusted file + anchor — which previously could hard-fail the analyze run on the findings + foreign key, or silently mis-associate the finding (#80). + +### Tests + +- Cross-repo conformance oracles for the loomweave↔filigree and + loomweave↔wardline seams (Filigree entity-associations, the Wardline taint-fact + wire, and the Wardline trust-vocabulary descriptor), pinning the wire bytes + each sibling produces or consumes so a silent drift reds in CI. + +### Security + +- Bumped `anyhow` `1.0.102` → `1.0.103` to clear a RUSTSEC soundness advisory in + `anyhow`'s `downcast_mut` (dtolnay/anyhow#451). Lockfile only; no API change. ## [1.3.1] — 2026-06-22 diff --git a/Cargo.lock b/Cargo.lock index 766cc3db..6c056128 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arrayref" @@ -1057,7 +1057,7 @@ checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "loomweave-analysis" -version = "1.3.1" +version = "1.4.0" dependencies = [ "anyhow", "serde", @@ -1067,7 +1067,7 @@ dependencies = [ [[package]] name = "loomweave-cli" -version = "1.3.1" +version = "1.4.0" dependencies = [ "anyhow", "assert_cmd", @@ -1107,7 +1107,7 @@ dependencies = [ [[package]] name = "loomweave-core" -version = "1.3.1" +version = "1.4.0" dependencies = [ "nix", "serde", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "loomweave-federation" -version = "1.3.1" +version = "1.4.0" dependencies = [ "blake3", "loomweave-core", @@ -1136,7 +1136,7 @@ dependencies = [ [[package]] name = "loomweave-llm" -version = "1.3.1" +version = "1.4.0" dependencies = [ "async-trait", "fs2", @@ -1152,7 +1152,7 @@ dependencies = [ [[package]] name = "loomweave-mcp" -version = "1.3.1" +version = "1.4.0" dependencies = [ "async-trait", "blake3", @@ -1176,7 +1176,7 @@ dependencies = [ [[package]] name = "loomweave-plugin-fixture" -version = "1.3.1" +version = "1.4.0" dependencies = [ "loomweave-core", "nix", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "loomweave-plugin-rust" -version = "1.3.1" +version = "1.4.0" dependencies = [ "ignore", "loomweave-core", @@ -1200,7 +1200,7 @@ dependencies = [ [[package]] name = "loomweave-scanner" -version = "1.3.1" +version = "1.4.0" dependencies = [ "regex", "serde", @@ -1212,7 +1212,7 @@ dependencies = [ [[package]] name = "loomweave-storage" -version = "1.3.1" +version = "1.4.0" dependencies = [ "blake3", "deadpool-sqlite", diff --git a/Cargo.toml b/Cargo.toml index 8d9c7890..5bcca542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ ] [workspace.package] -version = "1.3.1" +version = "1.4.0" edition = "2024" license = "MIT" repository = "https://github.com/foundryside-dev/loomweave" diff --git a/crates/loomweave-cli/Cargo.toml b/crates/loomweave-cli/Cargo.toml index cb3f8edd..0af6128d 100644 --- a/crates/loomweave-cli/Cargo.toml +++ b/crates/loomweave-cli/Cargo.toml @@ -19,13 +19,13 @@ anyhow.workspace = true axum.workspace = true blake3.workspace = true clap.workspace = true -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } -loomweave-llm = { path = "../loomweave-llm", version = "1.3.1" } -loomweave-analysis = { path = "../loomweave-analysis", version = "1.3.1" } -loomweave-federation = { path = "../loomweave-federation", version = "1.3.1" } -loomweave-mcp = { path = "../loomweave-mcp", version = "1.3.1" } -loomweave-scanner = { path = "../loomweave-scanner", version = "1.3.1" } -loomweave-storage = { path = "../loomweave-storage", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } +loomweave-llm = { path = "../loomweave-llm", version = "1.4.0" } +loomweave-analysis = { path = "../loomweave-analysis", version = "1.4.0" } +loomweave-federation = { path = "../loomweave-federation", version = "1.4.0" } +loomweave-mcp = { path = "../loomweave-mcp", version = "1.4.0" } +loomweave-scanner = { path = "../loomweave-scanner", version = "1.4.0" } +loomweave-storage = { path = "../loomweave-storage", version = "1.4.0" } dotenvy.workspace = true fs2.workspace = true hmac.workspace = true diff --git a/crates/loomweave-cli/pyproject.toml b/crates/loomweave-cli/pyproject.toml index 0d5d5838..e265bbab 100644 --- a/crates/loomweave-cli/pyproject.toml +++ b/crates/loomweave-cli/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "loomweave" -version = "1.3.1" +version = "1.4.0" description = "Loomweave — graph-aware code archaeology (Rust core)" readme = "../../README.md" requires-python = ">=3.11" @@ -22,8 +22,8 @@ classifiers = [ # instead, move loomweave-plugin-rust to an [project.optional-dependencies] # `rust` extra (installed via `loomweave[rust]`). dependencies = [ - "loomweave-plugin-python==1.3.1", - "loomweave-plugin-rust==1.3.1", + "loomweave-plugin-python==1.4.0", + "loomweave-plugin-rust==1.4.0", ] [project.urls] diff --git a/crates/loomweave-federation/Cargo.toml b/crates/loomweave-federation/Cargo.toml index 6a67d29e..71c5acff 100644 --- a/crates/loomweave-federation/Cargo.toml +++ b/crates/loomweave-federation/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] blake3.workspace = true -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } reqwest.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/loomweave-mcp/Cargo.toml b/crates/loomweave-mcp/Cargo.toml index 68c13c16..947eed5a 100644 --- a/crates/loomweave-mcp/Cargo.toml +++ b/crates/loomweave-mcp/Cargo.toml @@ -13,10 +13,10 @@ workspace = true [dependencies] async-trait.workspace = true blake3.workspace = true -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } -loomweave-llm = { path = "../loomweave-llm", version = "1.3.1" } -loomweave-federation = { path = "../loomweave-federation", version = "1.3.1" } -loomweave-storage = { path = "../loomweave-storage", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } +loomweave-llm = { path = "../loomweave-llm", version = "1.4.0" } +loomweave-federation = { path = "../loomweave-federation", version = "1.4.0" } +loomweave-storage = { path = "../loomweave-storage", version = "1.4.0" } reqwest.workspace = true rusqlite.workspace = true serde.workspace = true diff --git a/crates/loomweave-plugin-fixture/Cargo.toml b/crates/loomweave-plugin-fixture/Cargo.toml index 264fd97f..5f43f4d3 100644 --- a/crates/loomweave-plugin-fixture/Cargo.toml +++ b/crates/loomweave-plugin-fixture/Cargo.toml @@ -24,7 +24,7 @@ name = "loomweave-fixture-plugin" path = "src/main.rs" [dependencies] -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } serde_json.workspace = true [target.'cfg(unix)'.dependencies] diff --git a/crates/loomweave-plugin-rust/Cargo.toml b/crates/loomweave-plugin-rust/Cargo.toml index ee43e872..604e28b6 100644 --- a/crates/loomweave-plugin-rust/Cargo.toml +++ b/crates/loomweave-plugin-rust/Cargo.toml @@ -27,7 +27,7 @@ name = "loomweave-rust-plugin" path = "src/main.rs" [dependencies] -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } ignore = { workspace = true } serde_json.workspace = true syn = { version = "2", features = ["full", "extra-traits", "visit"] } diff --git a/crates/loomweave-plugin-rust/plugin.toml b/crates/loomweave-plugin-rust/plugin.toml index 2444518f..cabab224 100644 --- a/crates/loomweave-plugin-rust/plugin.toml +++ b/crates/loomweave-plugin-rust/plugin.toml @@ -1,7 +1,7 @@ [plugin] name = "loomweave-plugin-rust" plugin_id = "rust" -version = "1.3.1" +version = "1.4.0" protocol_version = "1.0" # Bare basename per ADR-021 — the host refuses any path component. This is the # DISCOVERY name; the cargo artifact is `loomweave-rust-plugin` (off-glob). diff --git a/crates/loomweave-storage/Cargo.toml b/crates/loomweave-storage/Cargo.toml index 9420a23c..1f45f299 100644 --- a/crates/loomweave-storage/Cargo.toml +++ b/crates/loomweave-storage/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] blake3.workspace = true -loomweave-core = { path = "../loomweave-core", version = "1.3.1" } +loomweave-core = { path = "../loomweave-core", version = "1.4.0" } deadpool-sqlite.workspace = true rusqlite.workspace = true serde.workspace = true diff --git a/packaging/rust-plugin-dist/Cargo.toml b/packaging/rust-plugin-dist/Cargo.toml index 56b24240..ac83da86 100644 --- a/packaging/rust-plugin-dist/Cargo.toml +++ b/packaging/rust-plugin-dist/Cargo.toml @@ -19,7 +19,7 @@ [package] name = "loomweave-plugin-rust-dist" -version = "1.3.1" +version = "1.4.0" edition = "2024" license = "MIT" repository = "https://github.com/foundryside-dev/loomweave" @@ -31,4 +31,4 @@ name = "loomweave-plugin-rust" path = "src/main.rs" [dependencies] -loomweave-plugin-rust = { path = "../../crates/loomweave-plugin-rust", version = "1.3.1" } +loomweave-plugin-rust = { path = "../../crates/loomweave-plugin-rust", version = "1.4.0" } diff --git a/packaging/rust-plugin-dist/pyproject.toml b/packaging/rust-plugin-dist/pyproject.toml index 8ebb300d..1f96b046 100644 --- a/packaging/rust-plugin-dist/pyproject.toml +++ b/packaging/rust-plugin-dist/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "loomweave-plugin-rust" -version = "1.3.1" +version = "1.4.0" description = "Loomweave Rust language plugin — JSON-RPC entity/edge extractor" readme = "README.md" requires-python = ">=3.11" diff --git a/packaging/rust-plugin-dist/wheel-data/data/share/loomweave/plugins/rust/plugin.toml b/packaging/rust-plugin-dist/wheel-data/data/share/loomweave/plugins/rust/plugin.toml index 2444518f..cabab224 100644 --- a/packaging/rust-plugin-dist/wheel-data/data/share/loomweave/plugins/rust/plugin.toml +++ b/packaging/rust-plugin-dist/wheel-data/data/share/loomweave/plugins/rust/plugin.toml @@ -1,7 +1,7 @@ [plugin] name = "loomweave-plugin-rust" plugin_id = "rust" -version = "1.3.1" +version = "1.4.0" protocol_version = "1.0" # Bare basename per ADR-021 — the host refuses any path component. This is the # DISCOVERY name; the cargo artifact is `loomweave-rust-plugin` (off-glob). diff --git a/plugins/python/plugin.toml b/plugins/python/plugin.toml index 8548dafc..24302cef 100644 --- a/plugins/python/plugin.toml +++ b/plugins/python/plugin.toml @@ -1,7 +1,7 @@ [plugin] name = "loomweave-plugin-python" plugin_id = "python" -version = "1.3.1" +version = "1.4.0" protocol_version = "1.0" # Bare basename per ADR-021 §Layer 1 + WP2 scrub commit eb0a41d — the host # refuses manifests whose `executable` carries any path component. diff --git a/plugins/python/pyproject.toml b/plugins/python/pyproject.toml index 23da5036..2360d0e9 100644 --- a/plugins/python/pyproject.toml +++ b/plugins/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "loomweave-plugin-python" -version = "1.3.1" +version = "1.4.0" description = "Loomweave Python language plugin — v1.0 release" readme = "README.md" requires-python = ">=3.11" diff --git a/plugins/python/src/loomweave_plugin_python/__init__.py b/plugins/python/src/loomweave_plugin_python/__init__.py index 8001872c..6958d5d5 100644 --- a/plugins/python/src/loomweave_plugin_python/__init__.py +++ b/plugins/python/src/loomweave_plugin_python/__init__.py @@ -1,3 +1,3 @@ """loomweave-plugin-python — Python language plugin for Loomweave.""" -__version__ = "1.3.1" +__version__ = "1.4.0" diff --git a/plugins/python/tests/test_package.py b/plugins/python/tests/test_package.py index d3d54454..beabe2d1 100644 --- a/plugins/python/tests/test_package.py +++ b/plugins/python/tests/test_package.py @@ -17,7 +17,7 @@ def _read_toml(path: Path) -> dict[str, Any]: def test_package_version_matches_pyproject() -> None: - assert loomweave_plugin_python.__version__ == "1.3.1" + assert loomweave_plugin_python.__version__ == "1.4.0" def test_plugin_version_lockstep_across_pyproject_manifest_and_module() -> None: @@ -42,7 +42,7 @@ def test_plugin_version_lockstep_across_pyproject_manifest_and_module() -> None: def test_manifest_declares_current_v1_ontology_only() -> None: manifest = _read_toml(_PLUGIN_ROOT / "plugin.toml") - assert manifest["plugin"]["version"] == "1.3.1" + assert manifest["plugin"]["version"] == "1.4.0" assert manifest["capabilities"]["runtime"]["wardline_aware"] is True assert manifest["integrations"]["wardline"]["expected_descriptor_version"] == ( EXPECTED_DESCRIPTOR_VERSION diff --git a/plugins/python/tests/test_server.py b/plugins/python/tests/test_server.py index dd0d18d2..6a974fc2 100644 --- a/plugins/python/tests/test_server.py +++ b/plugins/python/tests/test_server.py @@ -86,7 +86,7 @@ def test_initialize_roundtrip() -> None: assert response["id"] == 1 result = response["result"] assert result["name"] == "loomweave-plugin-python" - assert result["version"] == "1.3.1" + assert result["version"] == "1.4.0" assert result["ontology_version"] == "0.9.0" assert set(result["capabilities"]) == {"wardline"} assert result["capabilities"]["wardline"]["status"] in { diff --git a/plugins/python/uv.lock b/plugins/python/uv.lock index 60dcf2cc..ab41841c 100644 --- a/plugins/python/uv.lock +++ b/plugins/python/uv.lock @@ -464,7 +464,7 @@ wheels = [ [[package]] name = "loomweave-plugin-python" -version = "1.3.1" +version = "1.4.0" source = { editable = "." } dependencies = [ { name = "packaging" },