You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Four architectural improvements:
- Pluggable AST extractors: register_extractor() lets users override built-in
regex extractors with tree-sitter/LSP backends. Zero new dependencies.
- Batch SQL queries: 5 new get_*_batch() methods eliminate N+1 pattern in
risk_map computation (~5 queries instead of N*5).
- Process-level read locks: all read tools acquire shared process lock for
safe concurrent multi-process reads.
- Cross-platform ProcessLock: fcntl.flock on Unix, LockFileEx via ctypes on
Windows. Both support shared and exclusive locks.
18 new tests (540 total), all passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+16Lines changed: 16 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,22 @@ All notable changes to Chisel are documented in this file.
5
5
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
8
+
## [0.6.0] - 2026-03-22
9
+
10
+
### Added
11
+
12
+
-**Pluggable AST extractors**: `register_extractor(language, fn)` lets users override built-in regex extractors with tree-sitter, LSP, or other backends. `unregister_extractor()` reverts to built-in. `get_registered_extractors()` for introspection. Custom extractors checked before built-ins in `extract_code_units()`. Zero new dependencies.
13
+
-**Batch SQL queries**: 5 new `get_*_batch()` methods in `storage.py` for edges, code units, co-changes, churn stats, and blame. `_chunked()` helper splits large batches to stay under SQLite's variable limit.
14
+
-**Process-level read locks**: All read tool methods in `engine.py` now acquire `_process_lock.shared()` + `lock.read_lock()`. Write tools (`record_result`, `analyze`, `update`) acquire `_process_lock.exclusive()` + `lock.write_lock()`. Concurrent reads from multiple processes are now safe.
15
+
-**Cross-platform ProcessLock**: `project.py` uses `fcntl.flock` on Unix and `LockFileEx`/`UnlockFileEx` via ctypes on Windows. Both support shared and exclusive locks.
16
+
- 18 new tests: extractor registry (6), batch queries (7), process lock (3), engine lock wiring (2)
17
+
18
+
### Changed
19
+
20
+
-`impact.get_risk_map()` rewritten to use batch queries — computes all risk scores in ~5 queries instead of N*5 (eliminates N+1 pattern)
21
+
-`ProcessLock._acquire()` takes `exclusive: bool` instead of a platform-specific lock type constant
Copy file name to clipboardExpand all lines: CLAUDE.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,13 +31,16 @@ chisel/
31
31
-**Blame caching**: Cached by file content hash, invalidated on change.
32
32
-**Incremental updates**: File content hashes tracked in `file_hashes` table.
33
33
-**Persistent connection**: Storage uses a single SQLite connection (`check_same_thread=False`) with RWLock for thread safety.
34
-
-**Multi-agent safety**: `project.py` provides: (1) `detect_project_root()` canonicalizes via git common dir so worktrees share identity, (2) `normalize_path()` ensures consistent relative paths, (3) `resolve_storage_dir()` defaults to project-local `.chisel/` (priority: explicit > env > project-local > ~/.chisel/), (4) `ProcessLock`uses `fcntl.flock`for cross-process write coordination.
34
+
-**Multi-agent safety**: `project.py` provides: (1) `detect_project_root()` canonicalizes via git common dir so worktrees share identity, (2) `normalize_path()` ensures consistent relative paths, (3) `resolve_storage_dir()` defaults to project-local `.chisel/` (priority: explicit > env > project-local > ~/.chisel/), (4) `ProcessLock`for cross-process coordination — shared locks for reads, exclusive for writes. Cross-platform: `fcntl.flock`on Unix, `LockFileEx` on Windows.
35
35
-**SQLite concurrency**: 30s `busy_timeout` + exponential-backoff retry on `_execute` for cross-process SQLITE_BUSY.
36
36
-**Ownership vs Reviewers**: `ownership` = blame-based (who wrote the code, `role: "original_author"`). `who_reviews` = commit-activity-based (who maintains it, `role: "suggested_reviewer"`).
37
37
-**Shared constants**: `_SKIP_DIRS` and `_EXTENSION_MAP` live in `ast_utils.py`. `_CODE_EXTENSIONS` in `engine.py` is derived from `_EXTENSION_MAP`.
38
38
-**Shared dispatch**: `dispatch_tool()` in `mcp_server.py` is used by both HTTP and stdio servers. Tool schemas and dispatch tables live in `schemas.py`.
39
39
-**Edge weighting**: Test edges carry a weight (0.4-1.0) based on file proximity. Python import-path matching (`from myapp.utils import foo` → `myapp/utils.py:foo`) takes priority over name-only matching. `_compute_proximity_weight()` and `_matches_import_path()` in `test_mapper.py`.
40
40
-**AST regex improvements**: C#/Java support nested generics `<A<B>>` and annotations/attributes `@Override`/`[Test]`. Kotlin supports extension functions `fun String.foo()`. C++ supports template functions and destructors `~Foo()`. Swift supports `@objc`-style attributes. Dart supports factory constructors and getters/setters.
41
+
-**Pluggable extractors**: `register_extractor(lang, fn)` in `ast_utils.py` lets users override built-in regex extractors with tree-sitter or LSP-backed ones. `_custom_extractors` checked before `_EXTRACTORS` in `extract_code_units()`. Zero-dep — the registry is just hooks.
42
+
-**Batch SQL queries**: `storage.py` provides `get_*_batch()` methods for edges, code units, co-changes, churn, and blame. `impact.get_risk_map()` uses these to compute all risk scores in ~5 queries total instead of N*5. `_chunked()` helper splits large batches to stay under SQLite's variable limit.
43
+
-**Process-level read locks**: All read tool methods in `engine.py` acquire `_process_lock.shared()` (outer) + `lock.read_lock()` (inner). Writes acquire `_process_lock.exclusive()` + `lock.write_lock()`. This allows concurrent reads from multiple processes while blocking during writes.
|`chisel/test_mapper.py`| Test file discovery, framework detection (pytest/Jest/Go/Rust/Playwright), dependency extraction, test edge building |`ast`, `os`, `re`, `pathlib`, `chisel.ast_utils`, `chisel.project`|[glossary: test edge](wiki-local/glossary.md)|
Four architectural improvements: pluggable AST extraction for tree-sitter/LSP integration, batch SQL to eliminate N+1 in risk_map, process-level shared locks for concurrent reads, cross-platform ProcessLock (Windows support via LockFileEx).
11
+
12
+
### Pluggable AST Extraction (ast_utils.py)
13
+
-`register_extractor(language, fn)` stores custom extractors in `_custom_extractors` dict
14
+
-`extract_code_units()` checks custom extractors first, falls back to built-in regex
15
+
-`unregister_extractor(language)` reverts to built-in (raises KeyError if not registered)
16
+
-`get_registered_extractors()` returns shallow copy for introspection
17
+
- Zero new dependencies — registry is just callable hooks
18
+
19
+
### Batch SQL Queries (storage.py, impact.py)
20
+
- 5 new batch methods: `get_edges_for_code_batch`, `get_code_units_by_files_batch`, `get_co_changes_batch`, `get_churn_stats_batch`, `get_blame_batch`
21
+
-`_chunked()` helper splits lists into chunks of 900 to stay under SQLite's 999-variable limit
22
+
-`impact.get_risk_map()` rewritten to use batch queries — ~5 total queries instead of N*5
23
+
-`compute_risk_score()` unchanged for single-file use
24
+
25
+
### Process-Level Read Locks (engine.py)
26
+
- All 12 read tool methods now acquire `_process_lock.shared()` (outer) + `lock.read_lock()` (inner)
27
+
-`tool_record_result` now acquires `_process_lock.exclusive()` + `lock.write_lock()`
28
+
-`analyze()` and `update()` already used exclusive locks — no change
0 commit comments