witan-code 0.23.0__tar.gz → 0.25.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {witan_code-0.23.0 → witan_code-0.25.0}/.gitignore +1 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/CHANGELOG.md +52 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/PKG-INFO +33 -20
- {witan_code-0.23.0 → witan_code-0.25.0}/README.md +30 -17
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/BRANCH_INDEXING.md +2 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/PACKAGE_MAP.md +1 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/SYMBOL_FORMAT.md +6 -6
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/SYMBOL_TABLE.md +1 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/pyproject.toml +21 -6
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_cli_hooks.py +6 -3
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_context.py +22 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_maintenance.py +100 -5
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_reaper.py +78 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_setup.py +75 -8
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_store.py +15 -7
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/cli.py +115 -37
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/context.py +43 -11
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/extensions/pi/codegraph.ts +32 -9
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/maintenance.py +49 -6
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/reaper.py +59 -5
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/skills/witan-code/SKILL.md +15 -4
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/store.py +1 -1
- {witan_code-0.23.0 → witan_code-0.25.0}/changelog.d/README.md +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/config/claude.json +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/config/copilot.json +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/config/pi.json +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/conftest.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/CLI_REFERENCE.md +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/EDGE_PRECISION_TIERS.md +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/STAGE2_STITCHING.md +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/docs/USER_GUIDE.md +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/install.sh +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/__init__.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/conftest.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_batched_deletes.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_branches.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_bridge.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_chunked_load.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_cli_dispatch.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_cli_output_format.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_cli_selected_target.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_config.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_config_targets.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_crossrepo.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_edges.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_elicit.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_enrichment.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_github_app.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_graph.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_hooks.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_identity.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_index_failure_diagnostics.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_index_reports_its_store.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_index_requires_a_repo_key.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_indexer.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_indexer_scope.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_ingest.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_languages.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_package_map.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_rebuild_honours_the_repo_key.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_refusal_audit.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_remote_proxy.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_remote_store.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_repo.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_server.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_stitch.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_symbol_table.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_tasks.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_tool_schema.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_views.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/tests/test_visualize.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/__init__.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/__main__.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/bridge.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/bridge_extractors.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/config.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/edges.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/elicit.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/github_app.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/graph.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/hooks.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/identity.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/indexer.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/ingest.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/output.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/package_map.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries/bridge.gq +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries/code_mutations.gq +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries/code_read.gq +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries/delete.gq +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/bash.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/hcl.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/python.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/sql.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/typescript.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/queries_ts/yaml.scm +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/remote/__init__.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/remote/oidc.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/remote/proxy.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/remote/store.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/repo.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/schema/bridge-schema.pg +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/schema/code-schema.pg +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/selected_target.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/server.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/setup.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/stitch.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/views.py +0 -0
- {witan_code-0.23.0 → witan_code-0.25.0}/witan_code/visualize.py +0 -0
|
@@ -26,7 +26,7 @@ build/
|
|
|
26
26
|
*.egg-info/
|
|
27
27
|
# omnigraph binary downloaded by hatch_build.py — not committed
|
|
28
28
|
_bin/
|
|
29
|
-
# omnigraph cluster state (populated by `omnigraph cluster
|
|
29
|
+
# omnigraph cluster state (populated by `omnigraph cluster apply`)
|
|
30
30
|
__cluster/
|
|
31
31
|
# The witan UI bundle, written by `npm run build` in mcp/servers/witan/ui/.
|
|
32
32
|
# A build output, so not committed — hatch's `artifacts` setting in that
|
|
@@ -8,6 +8,58 @@ a MINOR bump may include breaking changes).
|
|
|
8
8
|
|
|
9
9
|
<!-- scriv-insert-here -->
|
|
10
10
|
|
|
11
|
+
## [0.25.0] - 2026-10-15
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- `witan-code cleanup` no longer defaults to `--keep 10`. From omnigraph 0.12
|
|
16
|
+
`--keep` counts graph commits per live branch, not versions per table, so
|
|
17
|
+
that default would cut a store to its last ten writes. With neither bound
|
|
18
|
+
it now keeps the last 30 days, as the Stop hook does. The help text
|
|
19
|
+
describes both bounds in graph commits. `--older-than` on its own
|
|
20
|
+
no longer adds an implicit `--keep 10`.
|
|
21
|
+
- The refusal for a shared cluster graph notes that from omnigraph 0.12
|
|
22
|
+
compaction is also refused while the server holds the cluster's `serve`
|
|
23
|
+
lock.
|
|
24
|
+
|
|
25
|
+
- Requires witan-core 0.41 or later, the first release on omnigraph 0.13.
|
|
26
|
+
Every deployed omnigraph server is on 0.13 and refuses a client that does
|
|
27
|
+
not send `Omnigraph-Http-Api: 0.13`.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `reap-views` ages a graph's views concurrently, `WITAN_CODE_VIEW_SURVEY_WORKERS` at a time (default 8; `1` is serial). Ageing a view is one `commit list --branch` that returns its whole reachable history, and serially production's 309-view bridge graph took 327s on its own, pushing the reaper CronJob past its 600s deadline; 8 workers take it to 50s. Each graph now also prints a `surveying views` line before its survey, so a job killed mid-sweep shows which graph it was on.
|
|
32
|
+
|
|
33
|
+
- A cluster graph that is not served is recognised on omnigraph 0.13, whose
|
|
34
|
+
404 says `graph not found` without the quoted id. It was reported as an
|
|
35
|
+
unreachable cluster.
|
|
36
|
+
|
|
37
|
+
## [0.24.0] - 2026-10-02
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- `witan-code setup --force` replaces a symlink or non-directory occupying an install destination (e.g. a stale symlink under `~/.pi/agent/extensions`). Without it, `witan-code setup` now exits 2 with the conflicting path instead of a `FileNotFoundError` traceback.
|
|
42
|
+
|
|
43
|
+
- `witan-code inject-context --client pi-builtin` renders the code-tool
|
|
44
|
+
discovery line for Pi's built-in MCP: a `codemode` script that finds the
|
|
45
|
+
`mcp__<server>__code_*` name with `searchTools()` and calls it as
|
|
46
|
+
`tools.<name>()`. The Pi extension passes it unless pi-mcp-adapter's `mcp`
|
|
47
|
+
tool is registered, in which case it still passes `--client pi`, whose
|
|
48
|
+
output is unchanged.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- The `witan-code` skill describes calling the `code_*` tools under Pi's
|
|
53
|
+
built-in MCP as well as under pi-mcp-adapter, and `witan-code setup --agent
|
|
54
|
+
pi` no longer warns that pi-mcp-adapter is missing when Pi's built-in MCP
|
|
55
|
+
will read the witan-code entry.
|
|
56
|
+
- Requires `agent-config-kit>=0.12`, which has that check; with an older one,
|
|
57
|
+
`witan-code setup --agent pi` still printed the pi-mcp-adapter warning.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- `witan-code checkpoint`'s background `optimize` now also runs `cleanup --older-than 30d` at most once per `WITAN_CODE_CLEANUP_INTERVAL` (default weekly; `0` disables). On omnigraph 0.11 `optimize` no longer reclaims the storage of branches deleted by `branches --prune` or `reap-views --apply`, and nothing ran `cleanup` unless done by hand. `witan-code optimize` gains `--cleanup-older-than` to run both in one process.
|
|
62
|
+
|
|
11
63
|
## [0.23.0] - 2026-09-28
|
|
12
64
|
|
|
13
65
|
### Added
|
|
@@ -353,7 +405,6 @@ a MINOR bump may include breaking changes).
|
|
|
353
405
|
write threw". Deliberately still non-fatal — the per-repo index succeeds and
|
|
354
406
|
is worth keeping — but "non-fatal" and "unreported" are different claims.
|
|
355
407
|
|
|
356
|
-
|
|
357
408
|
- **The test suite no longer asserts an environment.** Four tests
|
|
358
409
|
(`test_branches.py`'s three branch-view assertions and
|
|
359
410
|
`test_graph.py::test_a_shared_branch_view_needs_an_identity_to_own_it`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: witan-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.0
|
|
4
4
|
Summary: witan-code — tree-sitter code graph + cross-repo bridge (mounts under `witan code`)
|
|
5
5
|
Project-URL: Homepage, https://github.com/mitodl/agent-kit/tree/main/mcp/servers/witan-code
|
|
6
6
|
Project-URL: Repository, https://github.com/mitodl/agent-kit
|
|
@@ -10,7 +10,7 @@ License-Expression: BSD-3-Clause
|
|
|
10
10
|
Keywords: agent,code-graph,coding-agent,mcp,static-analysis,tree-sitter
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Requires-Python: >=3.11
|
|
13
|
-
Requires-Dist: agent-config-kit<1,>=0.
|
|
13
|
+
Requires-Dist: agent-config-kit<1,>=0.12
|
|
14
14
|
Requires-Dist: cyclopts<6,>=4
|
|
15
15
|
Requires-Dist: fastmcp<5,>=4
|
|
16
16
|
Requires-Dist: joserfc<2,>=1.0
|
|
@@ -26,7 +26,7 @@ Requires-Dist: tree-sitter-sql<0.4,>=0.3
|
|
|
26
26
|
Requires-Dist: tree-sitter-typescript<0.24,>=0.23
|
|
27
27
|
Requires-Dist: tree-sitter-yaml<0.8,>=0.7
|
|
28
28
|
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
29
|
-
Requires-Dist: witan-core[cli,observability,remote,sentry]<1,>=0.
|
|
29
|
+
Requires-Dist: witan-core[cli,observability,remote,sentry]<1,>=0.41
|
|
30
30
|
Provides-Extra: tasks
|
|
31
31
|
Requires-Dist: fastmcp-tasks<5,>=4; extra == 'tasks'
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
@@ -82,7 +82,7 @@ arbitration applies. See [`docs/BRANCH_INDEXING.md`](docs/BRANCH_INDEXING.md).
|
|
|
82
82
|
The two layers compose through **soft symbol-ID references**. A Layer-1 node
|
|
83
83
|
(e.g. a `lesson` or `agent_context`) can record symbol ids of the form:
|
|
84
84
|
|
|
85
|
-
```
|
|
85
|
+
```text
|
|
86
86
|
repo#relative/path.py::QualifiedName
|
|
87
87
|
e.g. https://github.com/mitodl/ol-django#app/svc.py::Service.run
|
|
88
88
|
```
|
|
@@ -299,11 +299,12 @@ witan-code setup --dry-run # preview without writing
|
|
|
299
299
|
witan-code setup --author "Jane Doe" # attribution (default: git config user.name)
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
-
On Pi, the MCP server entry (`~/.pi/agent/mcp.json`) is read
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
On Pi, the MCP server entry (`~/.pi/agent/mcp.json`) is read by Pi's
|
|
303
|
+
built-in MCP support (Pi 0.99 and later), or by the pi-mcp-adapter Pi
|
|
304
|
+
package when that is installed; confirm with `pi mcp list` or `/mcp` inside
|
|
305
|
+
Pi. `witan-code setup --agent pi` warns when Pi's settings show neither one
|
|
306
|
+
would load it (the built-in turned off with `-builtin:mcp`, or a Pi older
|
|
307
|
+
than 0.99, and no adapter declared).
|
|
307
308
|
|
|
308
309
|
If `witan` is *also* installed and witan-code is importable in that same
|
|
309
310
|
environment (e.g. via the `--with` in the `uv tool install`/MCP server's
|
|
@@ -354,6 +355,7 @@ symlink alternative:
|
|
|
354
355
|
| `WITAN_AUTHOR` / `USER` | `unknown` | attribution string |
|
|
355
356
|
| `WITAN_REPO` | — | override the detected repo slug |
|
|
356
357
|
| `WITAN_CODE_OPTIMIZE_INTERVAL` | `86400` (daily) | throttle window (seconds) for `checkpoint`'s opportunistic store compaction; `0` disables it |
|
|
358
|
+
| `WITAN_CODE_CLEANUP_INTERVAL` | `604800` (weekly) | throttle window (seconds) for the `cleanup --older-than 30d` that rides a `checkpoint` compaction; `0` disables it |
|
|
357
359
|
| `WITAN_CODE_INDEX_ROLE` | `client` | `ci` designates this process the writer of a shared graph's default-branch view. Only meaningful against a shared cluster graph; local stores are unaffected. See [Branch indexing](docs/BRANCH_INDEXING.md#who-may-write-the-shared-default-branch-view) |
|
|
358
360
|
| `WITAN_ACTOR` | derived from the `witan login` session | the identity that owns the branch views this process writes; an `act-…` id or a raw OIDC `sub`. For a non-interactive writer (CI, a maintenance job). See [Branch indexing](docs/BRANCH_INDEXING.md#per-writer-branch-views) |
|
|
359
361
|
| `WITAN_CONFIG` | `~/.config/witan/config.toml` | config file path (see below) |
|
|
@@ -582,7 +584,9 @@ graph):
|
|
|
582
584
|
instead.
|
|
583
585
|
- `cleanup [--store PATH] [--bridge] [--keep N] [--older-than DUR] --yes` —
|
|
584
586
|
reclaim disk by GC'ing old Lance versions (**destructive**; requires
|
|
585
|
-
`--yes`).
|
|
587
|
+
`--yes`). `--keep N` retains the newest N graph commits of every live
|
|
588
|
+
branch (omnigraph 0.12+; versions per table on 0.11); with neither bound it
|
|
589
|
+
keeps the last 30 days.
|
|
586
590
|
- `checkpoint` — opportunistically compact the current repo's store and the
|
|
587
591
|
bridge store if due (see [Hooks](#hooks)); registered as the `Stop` hook,
|
|
588
592
|
not usually run by hand.
|
|
@@ -640,12 +644,18 @@ mechanisms keep this in check, mirroring that module (deliberately duplicated
|
|
|
640
644
|
- **Opportunistic**: the `witan-code checkpoint` Stop hook spawns a
|
|
641
645
|
throttled, detached `witan-code optimize` for the current repo's store and
|
|
642
646
|
the shared bridge store, at most once per `WITAN_CODE_OPTIMIZE_INTERVAL`
|
|
643
|
-
(default daily; `0` disables) each — see [Hooks](#hooks).
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
`
|
|
648
|
-
|
|
647
|
+
(default daily; `0` disables) each — see [Hooks](#hooks). At most once per
|
|
648
|
+
`WITAN_CODE_CLEANUP_INTERVAL` (default weekly) the run also cleans up
|
|
649
|
+
versions older than 30 days. On omnigraph 0.11 that is the only thing that
|
|
650
|
+
reclaims the storage of branches deleted by `branches --prune` or
|
|
651
|
+
`reap-views --apply`; `optimize` no longer does.
|
|
652
|
+
- **Scheduled**: `witan-code optimize [--store PATH | --bridge]
|
|
653
|
+
[--cleanup-older-than DURATION]` / `witan-code cleanup --yes` for
|
|
654
|
+
cron/systemd-timer driven maintenance on a busy store. `optimize` alone is
|
|
655
|
+
non-destructive and safe to run repeatedly. `cleanup` GCs old Lance versions
|
|
656
|
+
to reclaim disk and is destructive, so it requires `--yes`.
|
|
657
|
+
`--cleanup-older-than` makes `optimize` run that destructive cleanup
|
|
658
|
+
afterwards with no `--yes` prompt, which is how the Stop hook calls it.
|
|
649
659
|
|
|
650
660
|
## Rebuilding an unreadable store
|
|
651
661
|
|
|
@@ -721,10 +731,13 @@ A Pi equivalent of all four lives in one extension,
|
|
|
721
731
|
can tell "no cross-repo consumers" from "no cross-repo data"), and the
|
|
722
732
|
`ToolSearch` call that makes the `code_*` tools callable when the harness
|
|
723
733
|
delivers them deferred — followed by a `code_find_definition` →
|
|
724
|
-
`code_callers`/`code_impact` call template.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
`
|
|
734
|
+
`code_callers`/`code_impact` call template. The Pi extension swaps the
|
|
735
|
+
`ToolSearch` step for Pi's calling convention: `--client pi-builtin`, for
|
|
736
|
+
Pi's built-in MCP, gives a `codemode` script (`searchTools(...)` for the
|
|
737
|
+
exact `mcp__<server>__` name, then `tools.<name>({...})`), and
|
|
738
|
+
`--client pi`, which it passes when pi-mcp-adapter's `mcp` tool is
|
|
739
|
+
registered, gives the adapter's proxy (`mcp({ search: ... })`, then
|
|
740
|
+
`mcp({ tool: ..., args: {...} })`). The default stays Claude's.
|
|
728
741
|
Independent of `witan`'s own `inject-context` hook (no cross-package
|
|
729
742
|
coupling) — register it alone for a witan-code-only install. Prints
|
|
730
743
|
nothing when the repo has neither a store nor an index in flight.
|
|
@@ -49,7 +49,7 @@ arbitration applies. See [`docs/BRANCH_INDEXING.md`](docs/BRANCH_INDEXING.md).
|
|
|
49
49
|
The two layers compose through **soft symbol-ID references**. A Layer-1 node
|
|
50
50
|
(e.g. a `lesson` or `agent_context`) can record symbol ids of the form:
|
|
51
51
|
|
|
52
|
-
```
|
|
52
|
+
```text
|
|
53
53
|
repo#relative/path.py::QualifiedName
|
|
54
54
|
e.g. https://github.com/mitodl/ol-django#app/svc.py::Service.run
|
|
55
55
|
```
|
|
@@ -266,11 +266,12 @@ witan-code setup --dry-run # preview without writing
|
|
|
266
266
|
witan-code setup --author "Jane Doe" # attribution (default: git config user.name)
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
On Pi, the MCP server entry (`~/.pi/agent/mcp.json`) is read
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
269
|
+
On Pi, the MCP server entry (`~/.pi/agent/mcp.json`) is read by Pi's
|
|
270
|
+
built-in MCP support (Pi 0.99 and later), or by the pi-mcp-adapter Pi
|
|
271
|
+
package when that is installed; confirm with `pi mcp list` or `/mcp` inside
|
|
272
|
+
Pi. `witan-code setup --agent pi` warns when Pi's settings show neither one
|
|
273
|
+
would load it (the built-in turned off with `-builtin:mcp`, or a Pi older
|
|
274
|
+
than 0.99, and no adapter declared).
|
|
274
275
|
|
|
275
276
|
If `witan` is *also* installed and witan-code is importable in that same
|
|
276
277
|
environment (e.g. via the `--with` in the `uv tool install`/MCP server's
|
|
@@ -321,6 +322,7 @@ symlink alternative:
|
|
|
321
322
|
| `WITAN_AUTHOR` / `USER` | `unknown` | attribution string |
|
|
322
323
|
| `WITAN_REPO` | — | override the detected repo slug |
|
|
323
324
|
| `WITAN_CODE_OPTIMIZE_INTERVAL` | `86400` (daily) | throttle window (seconds) for `checkpoint`'s opportunistic store compaction; `0` disables it |
|
|
325
|
+
| `WITAN_CODE_CLEANUP_INTERVAL` | `604800` (weekly) | throttle window (seconds) for the `cleanup --older-than 30d` that rides a `checkpoint` compaction; `0` disables it |
|
|
324
326
|
| `WITAN_CODE_INDEX_ROLE` | `client` | `ci` designates this process the writer of a shared graph's default-branch view. Only meaningful against a shared cluster graph; local stores are unaffected. See [Branch indexing](docs/BRANCH_INDEXING.md#who-may-write-the-shared-default-branch-view) |
|
|
325
327
|
| `WITAN_ACTOR` | derived from the `witan login` session | the identity that owns the branch views this process writes; an `act-…` id or a raw OIDC `sub`. For a non-interactive writer (CI, a maintenance job). See [Branch indexing](docs/BRANCH_INDEXING.md#per-writer-branch-views) |
|
|
326
328
|
| `WITAN_CONFIG` | `~/.config/witan/config.toml` | config file path (see below) |
|
|
@@ -549,7 +551,9 @@ graph):
|
|
|
549
551
|
instead.
|
|
550
552
|
- `cleanup [--store PATH] [--bridge] [--keep N] [--older-than DUR] --yes` —
|
|
551
553
|
reclaim disk by GC'ing old Lance versions (**destructive**; requires
|
|
552
|
-
`--yes`).
|
|
554
|
+
`--yes`). `--keep N` retains the newest N graph commits of every live
|
|
555
|
+
branch (omnigraph 0.12+; versions per table on 0.11); with neither bound it
|
|
556
|
+
keeps the last 30 days.
|
|
553
557
|
- `checkpoint` — opportunistically compact the current repo's store and the
|
|
554
558
|
bridge store if due (see [Hooks](#hooks)); registered as the `Stop` hook,
|
|
555
559
|
not usually run by hand.
|
|
@@ -607,12 +611,18 @@ mechanisms keep this in check, mirroring that module (deliberately duplicated
|
|
|
607
611
|
- **Opportunistic**: the `witan-code checkpoint` Stop hook spawns a
|
|
608
612
|
throttled, detached `witan-code optimize` for the current repo's store and
|
|
609
613
|
the shared bridge store, at most once per `WITAN_CODE_OPTIMIZE_INTERVAL`
|
|
610
|
-
(default daily; `0` disables) each — see [Hooks](#hooks).
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
`
|
|
615
|
-
|
|
614
|
+
(default daily; `0` disables) each — see [Hooks](#hooks). At most once per
|
|
615
|
+
`WITAN_CODE_CLEANUP_INTERVAL` (default weekly) the run also cleans up
|
|
616
|
+
versions older than 30 days. On omnigraph 0.11 that is the only thing that
|
|
617
|
+
reclaims the storage of branches deleted by `branches --prune` or
|
|
618
|
+
`reap-views --apply`; `optimize` no longer does.
|
|
619
|
+
- **Scheduled**: `witan-code optimize [--store PATH | --bridge]
|
|
620
|
+
[--cleanup-older-than DURATION]` / `witan-code cleanup --yes` for
|
|
621
|
+
cron/systemd-timer driven maintenance on a busy store. `optimize` alone is
|
|
622
|
+
non-destructive and safe to run repeatedly. `cleanup` GCs old Lance versions
|
|
623
|
+
to reclaim disk and is destructive, so it requires `--yes`.
|
|
624
|
+
`--cleanup-older-than` makes `optimize` run that destructive cleanup
|
|
625
|
+
afterwards with no `--yes` prompt, which is how the Stop hook calls it.
|
|
616
626
|
|
|
617
627
|
## Rebuilding an unreadable store
|
|
618
628
|
|
|
@@ -688,10 +698,13 @@ A Pi equivalent of all four lives in one extension,
|
|
|
688
698
|
can tell "no cross-repo consumers" from "no cross-repo data"), and the
|
|
689
699
|
`ToolSearch` call that makes the `code_*` tools callable when the harness
|
|
690
700
|
delivers them deferred — followed by a `code_find_definition` →
|
|
691
|
-
`code_callers`/`code_impact` call template.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
`
|
|
701
|
+
`code_callers`/`code_impact` call template. The Pi extension swaps the
|
|
702
|
+
`ToolSearch` step for Pi's calling convention: `--client pi-builtin`, for
|
|
703
|
+
Pi's built-in MCP, gives a `codemode` script (`searchTools(...)` for the
|
|
704
|
+
exact `mcp__<server>__` name, then `tools.<name>({...})`), and
|
|
705
|
+
`--client pi`, which it passes when pi-mcp-adapter's `mcp` tool is
|
|
706
|
+
registered, gives the adapter's proxy (`mcp({ search: ... })`, then
|
|
707
|
+
`mcp({ tool: ..., args: {...} })`). The default stays Claude's.
|
|
695
708
|
Independent of `witan`'s own `inject-context` hook (no cross-package
|
|
696
709
|
coupling) — register it alone for a witan-code-only install. Prints
|
|
697
710
|
nothing when the repo has neither a store nor an index in flight.
|
|
@@ -222,6 +222,8 @@ date, owner, or size. Two rules follow:
|
|
|
222
222
|
ago — reaping it would race the indexer that just created it.
|
|
223
223
|
|
|
224
224
|
The window is `WITAN_CODE_VIEW_MAX_IDLE_DAYS` (default 14; `0` disables).
|
|
225
|
+
Ageing a view is one `commit list` per view, so the sweep ages
|
|
226
|
+
`WITAN_CODE_VIEW_SURVEY_WORKERS` views at once (default 8; `1` is serial).
|
|
225
227
|
Idleness is not abandonment: a branch parked past the window and picked back up
|
|
226
228
|
loses its view, not its work, and the next index rebuilds it. Reporting is the
|
|
227
229
|
default and `--apply` deletes, because the window is the one input nothing
|
|
@@ -80,7 +80,7 @@ only makes it authoritative.
|
|
|
80
80
|
One `PackageMap` node per repo in the bridge store, keyed on the repo URI and
|
|
81
81
|
overwritten on every full-repo index (merge-by-slug):
|
|
82
82
|
|
|
83
|
-
```
|
|
83
|
+
```text
|
|
84
84
|
node PackageMap {
|
|
85
85
|
slug: String @key // canonical repo URI
|
|
86
86
|
repo: String @index
|
|
@@ -13,7 +13,7 @@ the `:CALLS/heuristic` fallback.
|
|
|
13
13
|
|
|
14
14
|
## Format
|
|
15
15
|
|
|
16
|
-
```
|
|
16
|
+
```text
|
|
17
17
|
{scheme}:{manager}:{package}:{version}:{descriptor}
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -35,7 +35,7 @@ never encoded (they are terminal).
|
|
|
35
35
|
|
|
36
36
|
### `endpoint` → scheme `http`
|
|
37
37
|
|
|
38
|
-
```
|
|
38
|
+
```text
|
|
39
39
|
{METHOD} {normalized-path}
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ never encoded (they are terminal).
|
|
|
49
49
|
representation, not the resource, and no consumer/provider pair would agree
|
|
50
50
|
on them syntactically.
|
|
51
51
|
|
|
52
|
-
```
|
|
52
|
+
```text
|
|
53
53
|
provider: http:pypi:mit-learn:main:GET /api/v0/users/me
|
|
54
54
|
consumer: http:.:.:.:* /api/v0/users/me
|
|
55
55
|
```
|
|
@@ -58,7 +58,7 @@ consumer: http:.:.:.:* /api/v0/users/me
|
|
|
58
58
|
|
|
59
59
|
Descriptor is the variable name, verbatim.
|
|
60
60
|
|
|
61
|
-
```
|
|
61
|
+
```text
|
|
62
62
|
provider: env:.:mit-learn:main:MITOL_APP_BASE_URL
|
|
63
63
|
consumer: env:.:.:.:MITOL_APP_BASE_URL
|
|
64
64
|
```
|
|
@@ -69,7 +69,7 @@ The symbol identifies the package itself; the descriptor is `.`. Manager and
|
|
|
69
69
|
package name come from the import site (consumer) or the publishing repo's
|
|
70
70
|
package map / `package.json` (provider).
|
|
71
71
|
|
|
72
|
-
```
|
|
72
|
+
```text
|
|
73
73
|
provider: pkg:npm:@mitodl/course-search-utils:main:.
|
|
74
74
|
consumer: pkg:npm:@mitodl/course-search-utils:.:.
|
|
75
75
|
```
|
|
@@ -78,7 +78,7 @@ consumer: pkg:npm:@mitodl/course-search-utils:.:.
|
|
|
78
78
|
|
|
79
79
|
Descriptor is `{sub_kind}/{key_norm}` (sub_kind: `repo` | `image` | `name`).
|
|
80
80
|
|
|
81
|
-
```
|
|
81
|
+
```text
|
|
82
82
|
provider: svc:.:ol-infrastructure:main:repo/https://github.com/mitodl/mit-learn
|
|
83
83
|
```
|
|
84
84
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "witan-code"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.25.0"
|
|
4
4
|
description = "witan-code — tree-sitter code graph + cross-repo bridge (mounts under `witan code`)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "BSD-3-Clause"
|
|
@@ -18,10 +18,19 @@ dependencies = [
|
|
|
18
18
|
# agent-config-kit by path — except there is NO `check-core-floor`
|
|
19
19
|
# equivalent for this dependency, so this line is the only guard.
|
|
20
20
|
# >=0.10: `witan-code setup`'s install report promises a warning when Pi
|
|
21
|
-
#
|
|
21
|
+
# cannot load MCP servers, which comes from `InstallResult.prerequisites`,
|
|
22
22
|
# added in 0.10.0. witan_core's `report_install` reads it with getattr, so
|
|
23
23
|
# an older resolution does not fail — it just silently drops the warning.
|
|
24
|
-
|
|
24
|
+
# >=0.11.1: `setup --force` passes `force` through to apply(), whose
|
|
25
|
+
# symlink guard (ConflictingPathError instead of FileNotFoundError) is
|
|
26
|
+
# new in 0.11.1. Older resolutions accept the flag but still crash on a
|
|
27
|
+
# dangling symlink. The workspace resolves by path, so no test here
|
|
28
|
+
# catches a stale floor.
|
|
29
|
+
# >=0.12: `witan-code setup --agent pi` only stops warning that
|
|
30
|
+
# pi-mcp-adapter is missing when Pi's built-in MCP will load the entry
|
|
31
|
+
# from 0.12.0. Older resolutions import and run, and print the false
|
|
32
|
+
# warning.
|
|
33
|
+
"agent-config-kit>=0.12,<1",
|
|
25
34
|
# `remote`: the ADR-0005 client stack (OIDC device grant + MCP-client
|
|
26
35
|
# proxy) behind `witan-code login` and the read commands' remote mode.
|
|
27
36
|
# Costs nothing extra in practice — fastmcp is already required here and
|
|
@@ -157,11 +166,17 @@ dependencies = [
|
|
|
157
166
|
# 0.38 its text never reaches the `mcp.tool_call` log line.
|
|
158
167
|
#
|
|
159
168
|
# ★ >=0.40 IS A BEHAVIOUR FLOOR too: `witan-code setup --agent pi` promises
|
|
160
|
-
# a warning when Pi
|
|
169
|
+
# a warning when Pi cannot load MCP servers, and
|
|
161
170
|
# `witan_core.cli.report_install` only prints `InstallResult.prerequisites`
|
|
162
171
|
# from 0.40.0. It reads the field with getattr, so 0.39 imports and runs —
|
|
163
172
|
# and silently drops the warning.
|
|
164
|
-
|
|
173
|
+
#
|
|
174
|
+
# ★ >=0.41 IS A COMPATIBILITY FLOOR: 0.41.0 pins omnigraph 0.13 (storage
|
|
175
|
+
# format 14) and sends `Omnigraph-Http-Api: 0.13`. Every deployed server
|
|
176
|
+
# is on 0.13, which answers a request without that header with `400
|
|
177
|
+
# api_contract_mismatch`, so 0.40 imports and runs, then fails on the
|
|
178
|
+
# first read of a shared code graph.
|
|
179
|
+
"witan-core[cli,remote,observability,sentry]>=0.41,<1",
|
|
165
180
|
"fastmcp>=4,<5",
|
|
166
181
|
"cyclopts>=4,<6",
|
|
167
182
|
# [targets.<name>] override models (config.py) — also transitively pulled
|
|
@@ -244,7 +259,7 @@ testpaths = ["tests"]
|
|
|
244
259
|
packages = ["witan_code"]
|
|
245
260
|
|
|
246
261
|
[tool.bumpversion]
|
|
247
|
-
current_version = "0.
|
|
262
|
+
current_version = "0.25.0"
|
|
248
263
|
allow_dirty = true
|
|
249
264
|
|
|
250
265
|
[[tool.bumpversion.files]]
|
|
@@ -5,6 +5,8 @@ exception escape (the hooks are documented as "always exits 0"), and must
|
|
|
5
5
|
not print an extra trailing newline beyond what the backend already returns.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
8
10
|
from witan_code import cli
|
|
9
11
|
|
|
10
12
|
|
|
@@ -62,7 +64,8 @@ def test_inject_context_cmd_defaults_to_the_claude_client(monkeypatch, capsys):
|
|
|
62
64
|
assert seen == ["claude"]
|
|
63
65
|
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
@pytest.mark.parametrize("client", ["pi", "pi-builtin"])
|
|
68
|
+
def test_inject_context_cmd_passes_an_explicit_pi_client_through(monkeypatch, client):
|
|
66
69
|
import witan_code.context as context_module
|
|
67
70
|
|
|
68
71
|
seen = []
|
|
@@ -70,7 +73,7 @@ def test_inject_context_cmd_passes_an_explicit_pi_client_through(monkeypatch):
|
|
|
70
73
|
context_module, "inject_context", lambda client: seen.append(client) or ""
|
|
71
74
|
)
|
|
72
75
|
|
|
73
|
-
command, bound, _ = cli.app.parse_args(["inject-context", "--client",
|
|
76
|
+
command, bound, _ = cli.app.parse_args(["inject-context", "--client", client])
|
|
74
77
|
command(*bound.args, **bound.kwargs)
|
|
75
78
|
|
|
76
|
-
assert seen == [
|
|
79
|
+
assert seen == [client]
|
|
@@ -417,10 +417,31 @@ def test_inject_context_default_and_explicit_claude_render_the_same(
|
|
|
417
417
|
assert "`/witan-code`" in default
|
|
418
418
|
|
|
419
419
|
|
|
420
|
+
def test_inject_context_for_pi_builtin_names_a_codemode_script(tmp_path, monkeypatch):
|
|
421
|
+
"""Pi's built-in MCP declares no MCP tool to the model by default: a
|
|
422
|
+
`codemode` script finds them with `searchTools()` and calls them as
|
|
423
|
+
`tools.<name>()`. The `mcp__<server>__` prefix depends on the user's
|
|
424
|
+
config, so the block must search first and then call the returned name.
|
|
425
|
+
"""
|
|
426
|
+
_indexed(tmp_path, monkeypatch)
|
|
427
|
+
|
|
428
|
+
text = context.inject_context(client="pi-builtin")
|
|
429
|
+
|
|
430
|
+
assert "ToolSearch" not in text
|
|
431
|
+
assert "mcp({" not in text
|
|
432
|
+
assert "`codemode` script" in text
|
|
433
|
+
assert 'return await searchTools("code_find_definition callers impact")' in text
|
|
434
|
+
assert 'return await tools.<name the search returned>({ name: "X" })' in text
|
|
435
|
+
assert "/skill:witan-code" in text
|
|
436
|
+
# The status lines above the discovery line are client-neutral.
|
|
437
|
+
assert "https://github.com/test/cg" in text
|
|
438
|
+
assert "3 files" in text
|
|
439
|
+
|
|
440
|
+
|
|
420
441
|
def test_inject_context_for_pi_names_the_mcp_proxy_not_toolsearch(
|
|
421
442
|
tmp_path, monkeypatch
|
|
422
443
|
):
|
|
423
|
-
"""
|
|
444
|
+
"""With pi-mcp-adapter (`--client pi`), MCP tools sit behind its `mcp`.
|
|
424
445
|
|
|
425
446
|
The adapter prefixes each tool with its server name, and that prefix
|
|
426
447
|
depends on the user's config, so the block must search first and then
|
|
@@ -70,16 +70,70 @@ def test_spawn_background_optimize_throttles(monkeypatch, tmp_path):
|
|
|
70
70
|
monkeypatch.setattr(maintenance.subprocess, "Popen", _FakePopen)
|
|
71
71
|
|
|
72
72
|
# first call spawns and stamps; second (within window) is throttled
|
|
73
|
-
assert maintenance.spawn_background_optimize(store, now=
|
|
74
|
-
assert maintenance.spawn_background_optimize(store, now=
|
|
73
|
+
assert maintenance.spawn_background_optimize(store, now=1_000_000.0) is True
|
|
74
|
+
assert maintenance.spawn_background_optimize(store, now=1_000_000.0 + 5) is False
|
|
75
75
|
assert len(calls) == 1
|
|
76
76
|
argv, kwargs = calls[0]
|
|
77
|
-
|
|
77
|
+
# never cleaned up before, so the first run also cleans up
|
|
78
|
+
assert argv[1:] == [
|
|
79
|
+
"-m",
|
|
80
|
+
"witan_code",
|
|
81
|
+
"optimize",
|
|
82
|
+
"--store",
|
|
83
|
+
str(store),
|
|
84
|
+
"--cleanup-older-than",
|
|
85
|
+
maintenance.CLEANUP_OLDER_THAN,
|
|
86
|
+
]
|
|
78
87
|
assert kwargs["start_new_session"] is True
|
|
79
88
|
|
|
80
|
-
# after the window elapses it spawns again
|
|
81
|
-
|
|
89
|
+
# after the optimize window elapses it spawns again, without the cleanup,
|
|
90
|
+
# whose weekly window has not
|
|
91
|
+
assert maintenance.spawn_background_optimize(store, now=1_000_000.0 + 4000) is True
|
|
82
92
|
assert len(calls) == 2
|
|
93
|
+
assert "--cleanup-older-than" not in calls[1][0]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_cleanup_rides_optimize_on_its_own_window(monkeypatch, tmp_path):
|
|
97
|
+
"""omnigraph 0.11 reclaims deleted branches' storage only in `cleanup`, so
|
|
98
|
+
the throttled optimize has to run one now and then or local stores grow
|
|
99
|
+
without bound."""
|
|
100
|
+
from witan_code import maintenance
|
|
101
|
+
|
|
102
|
+
monkeypatch.setenv("TMPDIR", str(tmp_path))
|
|
103
|
+
monkeypatch.setenv("WITAN_CODE_OPTIMIZE_INTERVAL", "3600")
|
|
104
|
+
monkeypatch.setenv("WITAN_CODE_CLEANUP_INTERVAL", "86400")
|
|
105
|
+
store = tmp_path / "g.omni"
|
|
106
|
+
store.mkdir()
|
|
107
|
+
|
|
108
|
+
calls = []
|
|
109
|
+
monkeypatch.setattr(
|
|
110
|
+
maintenance.subprocess, "Popen", lambda argv, **kw: calls.append(argv)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
for hour in range(26):
|
|
114
|
+
maintenance.spawn_background_optimize(store, now=100_000.0 + hour * 3600)
|
|
115
|
+
cleaned = ["--cleanup-older-than" in argv for argv in calls]
|
|
116
|
+
assert len(calls) == 26
|
|
117
|
+
assert cleaned.count(True) == 2
|
|
118
|
+
assert cleaned[0] and cleaned[24]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_cleanup_disabled_leaves_optimize_alone(monkeypatch, tmp_path):
|
|
122
|
+
from witan_code import maintenance
|
|
123
|
+
|
|
124
|
+
monkeypatch.setenv("TMPDIR", str(tmp_path))
|
|
125
|
+
monkeypatch.setenv("WITAN_CODE_OPTIMIZE_INTERVAL", "3600")
|
|
126
|
+
monkeypatch.setenv("WITAN_CODE_CLEANUP_INTERVAL", "0")
|
|
127
|
+
store = tmp_path / "g.omni"
|
|
128
|
+
store.mkdir()
|
|
129
|
+
|
|
130
|
+
calls = []
|
|
131
|
+
monkeypatch.setattr(
|
|
132
|
+
maintenance.subprocess, "Popen", lambda argv, **kw: calls.append(argv)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
assert maintenance.spawn_background_optimize(store, now=50_000.0) is True
|
|
136
|
+
assert "--cleanup-older-than" not in calls[0]
|
|
83
137
|
|
|
84
138
|
|
|
85
139
|
def test_spawn_marks_before_spawn_so_failure_does_not_hotloop(monkeypatch, tmp_path):
|
|
@@ -177,6 +231,47 @@ def test_cli_optimize_and_cleanup(tmp_path, capsys):
|
|
|
177
231
|
assert "Cleaned up" in capsys.readouterr().out
|
|
178
232
|
|
|
179
233
|
|
|
234
|
+
def test_cli_cleanup_with_no_bound_keeps_the_hooks_window(
|
|
235
|
+
tmp_path, capsys, monkeypatch
|
|
236
|
+
):
|
|
237
|
+
"""From omnigraph 0.12 `--keep` counts graph commits, so a default of
|
|
238
|
+
`--keep 10` would cut a store to its last ten writes. No bound means the
|
|
239
|
+
age window the Stop hook already uses."""
|
|
240
|
+
from witan_code import cli as cli_module
|
|
241
|
+
from witan_code import maintenance
|
|
242
|
+
|
|
243
|
+
store = tmp_path / "s.omni"
|
|
244
|
+
store.mkdir()
|
|
245
|
+
calls = []
|
|
246
|
+
|
|
247
|
+
class _Client:
|
|
248
|
+
def cleanup(self, **kwargs):
|
|
249
|
+
calls.append(kwargs)
|
|
250
|
+
|
|
251
|
+
monkeypatch.setattr(cli_module, "_maintenance_client", lambda _ref: _Client())
|
|
252
|
+
|
|
253
|
+
cli_module.cleanup(store=str(store))
|
|
254
|
+
assert f"newer than {maintenance.CLEANUP_OLDER_THAN}" in capsys.readouterr().out
|
|
255
|
+
assert calls == []
|
|
256
|
+
|
|
257
|
+
cli_module.cleanup(store=str(store), yes=True)
|
|
258
|
+
cli_module.cleanup(store=str(store), keep=3, yes=True)
|
|
259
|
+
assert calls == [
|
|
260
|
+
{"keep": None, "older_than": maintenance.CLEANUP_OLDER_THAN},
|
|
261
|
+
{"keep": 3, "older_than": None},
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@requires_omnigraph
|
|
266
|
+
def test_cli_optimize_then_cleanup(tmp_path, capsys):
|
|
267
|
+
from witan_code import cli as cli_module
|
|
268
|
+
|
|
269
|
+
store = _fresh_store(tmp_path)
|
|
270
|
+
|
|
271
|
+
cli_module.optimize(store=str(store), cleanup_older_than="30d")
|
|
272
|
+
assert "Optimized and cleaned up" in capsys.readouterr().out
|
|
273
|
+
|
|
274
|
+
|
|
180
275
|
def test_cli_optimize_missing_store_is_noop(tmp_path, capsys):
|
|
181
276
|
from witan_code import cli as cli_module
|
|
182
277
|
|