From 31f92e74e64f8ad0a9e7895b23c4f42397728ddc Mon Sep 17 00:00:00 2001 From: forhappy Date: Mon, 27 Jul 2026 10:32:39 -0700 Subject: [PATCH 1/2] docs: design Compass AST cache v1 cutover --- ...ompass-ast-cache-v1-hard-cutover-design.md | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md diff --git a/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md b/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md new file mode 100644 index 0000000..6369eb0 --- /dev/null +++ b/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md @@ -0,0 +1,66 @@ +# Compass AST Cache v1 Hard Cutover + +**Date:** 2026-07-27 + +## Goal + +Replace the historical Graphify-derived AST cache namespace +`compass-out/cache/ast/v0.9.21/e6` with a Compass-owned compatibility +namespace: + +```text +compass-out/cache/ast/v1/e6 +``` + +The `v1` segment identifies Compass AST cache compatibility. It is independent +of the Compass package release and advances only when extractor behavior makes +existing AST entries unsafe to reuse. The `e6` segment remains the binary cache +encoding version. + +## Hard-cutover behavior + +`Cache::open` recognizes `v1` as the only current AST namespace. Its existing +stale-version cleanup removes every other `v*` directory under +`compass-out/cache/ast/`, including `v0.9.21`. + +Compass does not read, copy, migrate, or fall back to entries in the old +namespace. The first build after upgrading is therefore a cold AST-cache build. +Semantic and Program IR caches are outside this cutover and remain untouched. + +## Code design + +In `compass-files`, rename the internal default and state from extractor-version +terminology to AST-cache-version terminology: + +- `AST_EXTRACTOR_VERSION` becomes `AST_CACHE_VERSION`; +- `Cache::extractor_version` becomes `Cache::ast_cache_version`; +- the test-only/customization builder becomes `with_ast_cache_version`. + +The default compatibility value is the explicit string `"1"`. It must not use +`CARGO_PKG_VERSION`, because normal Compass releases must not discard compatible +AST caches. + +Directory selection and stale-cache cleanup continue to use the same version +value, ensuring that the namespace Compass writes is also the only namespace it +preserves. + +## Documentation + +The extraction-pipeline documentation will describe `v1` as the Compass-owned +AST compatibility namespace and state that incompatible extractor changes +advance it. Historical implementation plans remain historical records and are +not rewritten. + +## Verification + +A focused contract test will first require `ast/v1/e6` and verify that opening +the cache deletes a populated `ast/v0.9.21` directory. The test must fail before +the production constant and names are changed, then pass afterward. + +Verification consists of: + +1. focused `compass-files` cache contract testing; +2. the complete `compass-files` test suite; +3. formatting and Clippy for `compass-files`; +4. `graphify update .` after code changes, as required by the parent project. + From 431601ff8218a67b03fa60a7391b477d97f63600 Mon Sep 17 00:00:00 2001 From: forhappy Date: Mon, 27 Jul 2026 10:54:30 -0700 Subject: [PATCH 2/2] Use Compass AST cache v1 namespace --- crates/compass-files/src/cache.rs | 15 +++++---------- crates/compass-files/tests/contracts.rs | 16 +++++----------- docs/implementation/extraction-pipeline.md | 10 ++++++---- ...7-compass-ast-cache-v1-hard-cutover-design.md | 4 ++-- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/crates/compass-files/src/cache.rs b/crates/compass-files/src/cache.rs index 73bb59b..aefbeff 100644 --- a/crates/compass-files/src/cache.rs +++ b/crates/compass-files/src/cache.rs @@ -11,7 +11,7 @@ use sha2::{Digest, Sha256}; use crate::{FileError, StatHashIndex, file_hash, io_error, write_bytes_atomic, write_json_atomic}; -const AST_EXTRACTOR_VERSION: &str = "0.9.21"; +const AST_CACHE_VERSION: &str = "1"; const CACHE_ENCODING_VERSION: u32 = 6; const MESSAGEPACK_EXTENSION: &str = "msgpack"; @@ -108,7 +108,7 @@ impl CacheKind { pub struct Cache { root: PathBuf, cache_base: PathBuf, - extractor_version: String, + ast_cache_version: String, hashes: StatHashIndex, hash_policy: CacheHashPolicy, session_hashes: HashMap, @@ -141,7 +141,7 @@ impl Cache { let cache = Self { root, cache_base, - extractor_version: AST_EXTRACTOR_VERSION.to_owned(), + ast_cache_version: AST_CACHE_VERSION.to_owned(), hashes, hash_policy: options.hash_policy, session_hashes: HashMap::new(), @@ -151,11 +151,6 @@ impl Cache { Ok(cache) } - pub fn with_extractor_version(mut self, version: impl Into) -> Self { - self.extractor_version = version.into(); - self - } - /// Program-only cache users do not consult the path stat index. Disabling /// its drop-time flush prevents a parallel Program worker from overwriting /// hashes recorded by the graph extraction worker. @@ -167,7 +162,7 @@ impl Cache { pub fn directory(&self, kind: &CacheKind, prompt_fingerprint: Option<&str>) -> PathBuf { let mut directory = self.cache_base.join(kind.directory_name()); if matches!(kind, CacheKind::Ast) { - directory = directory.join(format!("v{}", self.extractor_version)); + directory = directory.join(format!("v{}", self.ast_cache_version)); } else if let Some(fingerprint) = prompt_fingerprint { directory = directory.join(format!("p{fingerprint}")); } @@ -419,7 +414,7 @@ impl Cache { fn cleanup_stale_ast(&self) { let base = self.cache_base.join("ast"); - let current = format!("v{}", self.extractor_version); + let current = format!("v{}", self.ast_cache_version); let Ok(entries) = fs::read_dir(&base) else { return; }; diff --git a/crates/compass-files/tests/contracts.rs b/crates/compass-files/tests/contracts.rs index b237621..9cfdb1a 100644 --- a/crates/compass-files/tests/contracts.rs +++ b/crates/compass-files/tests/contracts.rs @@ -508,9 +508,9 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< let root = directory.path().join("root"); let cache_root = directory.path().join("cache-root"); fs::create_dir_all(&root)?; - fs::create_dir_all(cache_root.join("compass-out/cache/ast/v0.9.20"))?; + fs::create_dir_all(cache_root.join("compass-out/cache/ast/v0.9.21"))?; fs::write( - cache_root.join("compass-out/cache/ast/v0.9.20/stale.json"), + cache_root.join("compass-out/cache/ast/v0.9.21/stale.json"), "{}", )?; fs::create_dir_all(cache_root.join("compass-out/cache/ast/vold"))?; @@ -535,17 +535,11 @@ fn cache_versions_legacy_fingerprints_pruning_and_cleanup_are_total() -> Result< assert!( default_cache .directory(&CacheKind::Ast, None) - .ends_with("ast/v0.9.21/e6") + .ends_with("ast/v1/e6") ); - assert!(!cache_root.join("compass-out/cache/ast/v0.9.20").exists()); + assert!(!cache_root.join("compass-out/cache/ast/v0.9.21").exists()); - let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))? - .with_extractor_version("current"); - assert!( - cache - .directory(&CacheKind::Ast, None) - .ends_with("ast/vcurrent/e6") - ); + let mut cache = Cache::open(&root, CacheOptions::output_directory(Some(&cache_root)))?; assert!( cache .directory(&CacheKind::SemanticMode("deep".to_owned()), Some("abc")) diff --git a/docs/implementation/extraction-pipeline.md b/docs/implementation/extraction-pipeline.md index c6a7c0b..52168a8 100644 --- a/docs/implementation/extraction-pipeline.md +++ b/docs/implementation/extraction-pipeline.md @@ -260,10 +260,12 @@ Cases to test: Performance qualification compares cold, warm unchanged, single-file change, rename, and delete cases against Compass-owned baselines. -Extractor semantics use a versioned AST cache namespace. The C declarator and -positional-document corrections advance that namespace to `v0.9.21`, so the -first update after upgrading refreshes AST facts. Later unchanged updates return -to the normal warm path. +Extractor semantics use the Compass-owned AST compatibility namespace `v1`. +This cache version is independent of the Compass package release and advances +only when changed extractor behavior makes existing AST entries unsafe to +reuse. The first update after such a version change refreshes AST facts; later +unchanged updates return to the normal warm path. The nested `e6` namespace is +the binary cache encoding version, not part of the AST compatibility version. ## Watch mode diff --git a/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md b/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md index 6369eb0..e96dbac 100644 --- a/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md +++ b/docs/superpowers/specs/2026-07-27-compass-ast-cache-v1-hard-cutover-design.md @@ -34,7 +34,8 @@ terminology to AST-cache-version terminology: - `AST_EXTRACTOR_VERSION` becomes `AST_CACHE_VERSION`; - `Cache::extractor_version` becomes `Cache::ast_cache_version`; -- the test-only/customization builder becomes `with_ast_cache_version`. +- the public extractor-version override is removed so callers cannot create + alternate AST namespaces. The default compatibility value is the explicit string `"1"`. It must not use `CARGO_PKG_VERSION`, because normal Compass releases must not discard compatible @@ -63,4 +64,3 @@ Verification consists of: 2. the complete `compass-files` test suite; 3. formatting and Clippy for `compass-files`; 4. `graphify update .` after code changes, as required by the parent project. -