From 9ea518285786621c4ee8506b90e5d8128c47600a Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 09:29:37 +0200 Subject: [PATCH 1/7] =?UTF-8?q?docs(milestones):=20consolidation=20pass=20?= =?UTF-8?q?=E2=80=94=20assign=20epics,=20archive=20skip=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- kb/Planning/MILESTONES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/kb/Planning/MILESTONES.md b/kb/Planning/MILESTONES.md index 2ad0a9b..6be1765 100644 --- a/kb/Planning/MILESTONES.md +++ b/kb/Planning/MILESTONES.md @@ -30,6 +30,7 @@ source: mixed - [x] Tag v1.0.0. ## Open: pull-driven +**Epic:** E9 - [ ] Downstream proof that products register Manifold handles through `converge_provider::ChatBackendRegistry`. From 98da391f24de9aa150cb3e3be5257871f3f7c97c Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 11:08:24 +0200 Subject: [PATCH 2/7] =?UTF-8?q?docs:=20archive=20milestone/epic=20files=20?= =?UTF-8?q?=E2=80=94=20tracking=20moved=20to=20Linear=20(team=20RFL)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stamped by tools/linear-import/retire.py after the 2026-07-02 import (123 issues, 11 projects). Linear is now the source of truth. Co-Authored-By: Claude Fable 5 --- kb/Planning/MILESTONES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kb/Planning/MILESTONES.md b/kb/Planning/MILESTONES.md index 6be1765..9d638f9 100644 --- a/kb/Planning/MILESTONES.md +++ b/kb/Planning/MILESTONES.md @@ -1,3 +1,6 @@ +> **Archived 2026-07-02** — active milestone tracking moved to Linear (Reflective team). +> This file is kept for historical context only. Do not add new items here. + --- source: mixed --- From 7a088aeab52b385f333b79658a3ef8e964972ea4 Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 11:43:08 +0200 Subject: [PATCH 3/7] =?UTF-8?q?fix(deps):=20quick-xml=200.41=20=E2=80=94?= =?UTF-8?q?=20RUSTSEC-2026-0194/0195=20(both=207.5=20high)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two advisories published 2026-06-29 against quick-xml 0.39: quadratic duplicate-attribute checking and NsReader namespace-allocation DoS. Bump the direct dep to 0.41 and adapt to its API (read_text returns undecoded text; decode_and_unescape_value renamed). Co-Authored-By: Claude Fable 5 --- Cargo.toml | 2 +- crates/manifold/src/feed.rs | 15 ++++++++------- crates/manifold/src/model_selection.rs | 2 +- crates/manifold/src/secret/mod.rs | 2 +- crates/manifold/src/xml.rs | 6 ++++++ 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a8c4419..7834044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ parking_lot = "0.12" proptest = "1.5" lancedb = { version = "0.26", default-features = false } object_store = "0.13" -quick-xml = "0.39" +quick-xml = "0.41" regex-lite = "0.1" reqwest = { version = "0.12", features = ["blocking", "json", "rustls-tls", "stream"], default-features = false } scraper = { version = "0.20", default-features = false } diff --git a/crates/manifold/src/feed.rs b/crates/manifold/src/feed.rs index d632559..4e27ebc 100644 --- a/crates/manifold/src/feed.rs +++ b/crates/manifold/src/feed.rs @@ -723,13 +723,14 @@ fn read_text( reader: &mut Reader<&[u8]>, start: &BytesStart<'_>, ) -> Result, FeedError> { - reader + let text = reader .read_text(start.name()) - .map(|text| { - let trimmed = text.trim(); - (!trimmed.is_empty()).then_some(trimmed.to_string()) - }) - .map_err(|error| FeedError::Parse(error.to_string())) + .map_err(|error| FeedError::Parse(error.to_string()))?; + let text = text + .decode() + .map_err(|error| FeedError::Parse(error.to_string()))?; + let trimmed = text.trim(); + Ok((!trimmed.is_empty()).then_some(trimmed.to_string())) } fn local_name(name: &[u8]) -> String { @@ -744,7 +745,7 @@ fn attr_value(reader: &Reader<&[u8]>, start: &BytesStart<'_>, key: &[u8]) -> Opt .find(|attribute| local_name(attribute.key.as_ref()).as_bytes() == key) .and_then(|attribute| { attribute - .decode_and_unescape_value(reader.decoder()) + .decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, reader.decoder()) .ok() .map(std::borrow::Cow::into_owned) }) diff --git a/crates/manifold/src/model_selection.rs b/crates/manifold/src/model_selection.rs index 7aa6acd..bb8ddff 100644 --- a/crates/manifold/src/model_selection.rs +++ b/crates/manifold/src/model_selection.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; -use crate::secret::{SecretProvider, default_secret_provider}; +use crate::secret::default_secret_provider; use converge_provider::LlmError; use converge_provider::selection::{ AgentRequirements, ComplianceLevel, CostClass, DataSovereignty, ModelSelectorTrait, diff --git a/crates/manifold/src/secret/mod.rs b/crates/manifold/src/secret/mod.rs index 8afc7ce..3b56b53 100644 --- a/crates/manifold/src/secret/mod.rs +++ b/crates/manifold/src/secret/mod.rs @@ -187,7 +187,7 @@ pub fn default_secret_provider() -> &'static DefaultSecretProvider { fn env_secrets_allowed() -> bool { matches!( std::env::var("MANIFOLD_ALLOW_ENV_SECRETS").ok().as_deref(), - Some("1") | Some("true") | Some("yes") + Some("1" | "true" | "yes") ) } diff --git a/crates/manifold/src/xml.rs b/crates/manifold/src/xml.rs index f775fdb..402eae3 100644 --- a/crates/manifold/src/xml.rs +++ b/crates/manifold/src/xml.rs @@ -46,6 +46,9 @@ pub fn extract_first_text(xml: &str, local_name: &str) -> Result, let text = reader .read_text(name) .map_err(|e| XmlExtractError::Parse(e.to_string()))?; + let text = text + .decode() + .map_err(|e| XmlExtractError::Parse(e.to_string()))?; return Ok(Some(text.into_owned())); } } @@ -83,6 +86,9 @@ pub fn extract_all_texts(xml: &str, local_name: &str) -> Result, Xml let text = reader .read_text(name) .map_err(|e| XmlExtractError::Parse(e.to_string()))?; + let text = text + .decode() + .map_err(|e| XmlExtractError::Parse(e.to_string()))?; out.push(text.into_owned()); } } From d4a3010aca87e77622671e28695d70cba6ab9dea Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 11:43:08 +0200 Subject: [PATCH 4/7] =?UTF-8?q?fix(ci):=20unbreak=20Stability=20=E2=80=94?= =?UTF-8?q?=20runner-env=20cargo-deny=20+=20quick-xml=20transitive=20ignor?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo Deny job: EmbarkStudios action's docker container can't install the pinned musl toolchain or see sibling checkouts; run cargo-deny in the runner env like the audit job. Advisory ignores (deny.toml + audit, in lockstep): the transitive quick-xml 0.39.4 via object_store 0.13.2 has no fix path until object_store releases against 0.41; justification inline. Co-Authored-By: Claude Fable 5 --- .github/workflows/stability.yml | 16 +++++++++++++++- deny.toml | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stability.yml b/.github/workflows/stability.yml index 52bc4d9..b9f6af8 100644 --- a/.github/workflows/stability.yml +++ b/.github/workflows/stability.yml @@ -100,6 +100,8 @@ jobs: --ignore RUSTSEC-2025-0119 --ignore RUSTSEC-2025-0134 --ignore RUSTSEC-2026-0002 + --ignore RUSTSEC-2026-0194 + --ignore RUSTSEC-2026-0195 deny: name: Cargo Deny @@ -108,4 +110,16 @@ jobs: - uses: actions/checkout@v5 - name: Checkout Reflective sibling dependencies run: bash scripts/ci/checkout-reflective-siblings.sh - - uses: EmbarkStudios/cargo-deny-action@v2 + # Not EmbarkStudios/cargo-deny-action: its docker container can't + # install the repo's pinned musl toolchain and doesn't see the + # sibling checkouts, so `cargo metadata` fails on converge-core. + # Run cargo-deny in the runner env like the audit job above. + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_VERSION }} + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + - name: Run cargo-deny + run: cargo deny check diff --git a/deny.toml b/deny.toml index 8d21f17..d8b3de1 100644 --- a/deny.toml +++ b/deny.toml @@ -32,6 +32,16 @@ ignore = [ # lru 0.12.5 — IterMut unsoundness. Transitive via tantivy → lance-index; # we do not call IterMut on lru caches, exposure path is upstream-internal. "RUSTSEC-2026-0002", + # quick-xml 0.39.4 — duplicate-attribute quadratic run time (0194) and + # NsReader namespace-allocation DoS (0195), fixed in >=0.41. Our direct + # dep moved to 0.41 (2026-07-02); this pin is transitive via object_store + # 0.13.2 (latest available), which uses it to parse S3/Azure API list + # responses. Exposure is XML from the operator-configured storage + # provider's own API, not arbitrary attacker input, and both advisories + # are DoS-class (no memory unsafety). Drop both when object_store + # releases against quick-xml >=0.41. + "RUSTSEC-2026-0194", + "RUSTSEC-2026-0195", ] [licenses] From 531548b953b05f06eed8389b2869bcb5b749ecf9 Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 11:49:46 +0200 Subject: [PATCH 5/7] fix: import SecretProvider trait for feature-gated has_secret call sites Discovered building manifold from arena-tests, which enables provider features manifold's own featureless check never compiles: every has_secret call in is_provider_available is behind a provider feature gate, and the trait was not in scope. Co-Authored-By: Claude Fable 5 --- crates/manifold/src/model_selection.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/manifold/src/model_selection.rs b/crates/manifold/src/model_selection.rs index bb8ddff..ead543b 100644 --- a/crates/manifold/src/model_selection.rs +++ b/crates/manifold/src/model_selection.rs @@ -11,6 +11,10 @@ use serde::{Deserialize, Serialize}; use crate::secret::default_secret_provider; +// has_secret is a SecretProvider trait method; its call sites are all behind +// provider feature gates, so the import is unused in a featureless build. +#[allow(unused_imports)] +use crate::secret::SecretProvider as _; use converge_provider::LlmError; use converge_provider::selection::{ AgentRequirements, ComplianceLevel, CostClass, DataSovereignty, ModelSelectorTrait, From be5e1420439bbccf94a99fa892719ba9b6bb9d1e Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 14:22:30 +0200 Subject: [PATCH 6/7] chore(security): quick-xml 0194/0195 transitive ignores + anyhow 1.0.103 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-07-02 advisory wave: RUSTSEC-2026-0194/0195 (quick-xml <0.41, DoS-class) are transitive via object_store, which is semver-locked upstream — no fix path until object_store releases against 0.41. Ignore lists updated in lockstep (workflows, Justfile, deny.toml). anyhow 1.0.102 (RUSTSEC-2026-0190, unsound) updated where present. Co-Authored-By: Claude Fable 5 --- .github/workflows/security.yml | 2 ++ Justfile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index daef445..951a662 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -37,6 +37,8 @@ jobs: --ignore RUSTSEC-2025-0119 --ignore RUSTSEC-2025-0134 --ignore RUSTSEC-2026-0002 + --ignore RUSTSEC-2026-0194 + --ignore RUSTSEC-2026-0195 secrets: name: Secrets Scan diff --git a/Justfile b/Justfile index fe4389f..4dd537e 100644 --- a/Justfile +++ b/Justfile @@ -76,6 +76,8 @@ security-audit: --ignore RUSTSEC-2024-0436 \ --ignore RUSTSEC-2025-0134 \ --ignore RUSTSEC-2026-0002 + --ignore RUSTSEC-2026-0194 + --ignore RUSTSEC-2026-0195 cargo deny check # Session opener From a43af43edd4ba5457d4ecb8d7550d96be8da3b29 Mon Sep 17 00:00:00 2001 From: Kenneth Pernyer Date: Thu, 2 Jul 2026 17:30:52 +0200 Subject: [PATCH 7/7] fix(ci): repair Justfile line continuations + complete advisory ignores The batch ignore insertion dropped the backslash continuation on appended audit lines, breaking Justfile parsing ('recipe line has extra leading whitespace'). Also completes today's advisory set. Co-Authored-By: Claude Fable 5 --- .github/workflows/security.yml | 1 + .github/workflows/stability.yml | 1 + Justfile | 7 ++++--- deny.toml | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 951a662..58639a1 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -39,6 +39,7 @@ jobs: --ignore RUSTSEC-2026-0002 --ignore RUSTSEC-2026-0194 --ignore RUSTSEC-2026-0195 + --ignore RUSTSEC-2026-0173 secrets: name: Secrets Scan diff --git a/.github/workflows/stability.yml b/.github/workflows/stability.yml index b9f6af8..05af860 100644 --- a/.github/workflows/stability.yml +++ b/.github/workflows/stability.yml @@ -102,6 +102,7 @@ jobs: --ignore RUSTSEC-2026-0002 --ignore RUSTSEC-2026-0194 --ignore RUSTSEC-2026-0195 + --ignore RUSTSEC-2026-0173 deny: name: Cargo Deny diff --git a/Justfile b/Justfile index 4dd537e..2fb7114 100644 --- a/Justfile +++ b/Justfile @@ -75,9 +75,10 @@ security-audit: --ignore RUSTSEC-2025-0119 \ --ignore RUSTSEC-2024-0436 \ --ignore RUSTSEC-2025-0134 \ - --ignore RUSTSEC-2026-0002 - --ignore RUSTSEC-2026-0194 - --ignore RUSTSEC-2026-0195 + --ignore RUSTSEC-2026-0002 \ + --ignore RUSTSEC-2026-0194 \ + --ignore RUSTSEC-2026-0195 \ + --ignore RUSTSEC-2026-0173 cargo deny check # Session opener diff --git a/deny.toml b/deny.toml index d8b3de1..3464db9 100644 --- a/deny.toml +++ b/deny.toml @@ -42,6 +42,10 @@ ignore = [ # releases against quick-xml >=0.41. "RUSTSEC-2026-0194", "RUSTSEC-2026-0195", + # proc-macro-error2 2.0.1 unmaintained (advisory 2026-06-07). Transitive + # proc-macro, compile-time only, no runtime surface. No maintained + # upstream replacement path yet. Added 2026-07-02. + "RUSTSEC-2026-0173", ] [licenses]