clew-trace 1.0.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.
- clew_trace-1.0.0/.doxygen-guard.yaml +62 -0
- clew_trace-1.0.0/.gitignore +69 -0
- clew_trace-1.0.0/.pre-commit-config.yaml +58 -0
- clew_trace-1.0.0/LICENSE +21 -0
- clew_trace-1.0.0/PKG-INFO +284 -0
- clew_trace-1.0.0/README.md +245 -0
- clew_trace-1.0.0/clew/__init__.py +96 -0
- clew_trace-1.0.0/clew/__main__.py +14 -0
- clew_trace-1.0.0/clew/_common.py +173 -0
- clew_trace-1.0.0/clew/ast_symbols.py +1781 -0
- clew_trace-1.0.0/clew/buildoptions.py +696 -0
- clew_trace-1.0.0/clew/call_edges.py +1306 -0
- clew_trace-1.0.0/clew/callback_edges.py +1386 -0
- clew_trace-1.0.0/clew/cli.py +2464 -0
- clew_trace-1.0.0/clew/coverage.py +663 -0
- clew_trace-1.0.0/clew/critical_sections.py +597 -0
- clew_trace-1.0.0/clew/datamodel.py +1077 -0
- clew_trace-1.0.0/clew/declaration.py +616 -0
- clew_trace-1.0.0/clew/diagnostics.py +252 -0
- clew_trace-1.0.0/clew/dispatch.py +472 -0
- clew_trace-1.0.0/clew/dispatch_edges.py +765 -0
- clew_trace-1.0.0/clew/dominated_edges.py +356 -0
- clew_trace-1.0.0/clew/doxygen.py +1263 -0
- clew_trace-1.0.0/clew/enrichment.py +105 -0
- clew_trace-1.0.0/clew/errors.py +72 -0
- clew_trace-1.0.0/clew/event_edges.py +358 -0
- clew_trace-1.0.0/clew/export_command.py +183 -0
- clew_trace-1.0.0/clew/external.py +280 -0
- clew_trace-1.0.0/clew/filedocs.py +433 -0
- clew_trace-1.0.0/clew/gitenv.py +69 -0
- clew_trace-1.0.0/clew/guardconfig.py +512 -0
- clew_trace-1.0.0/clew/harvest.py +614 -0
- clew_trace-1.0.0/clew/harvest_plan.py +186 -0
- clew_trace-1.0.0/clew/indexcache.py +538 -0
- clew_trace-1.0.0/clew/init_command.py +613 -0
- clew_trace-1.0.0/clew/kconfig.py +796 -0
- clew_trace-1.0.0/clew/kconfig_gates.py +567 -0
- clew_trace-1.0.0/clew/locks.py +1068 -0
- clew_trace-1.0.0/clew/mcp_config.py +615 -0
- clew_trace-1.0.0/clew/mcp_server/__init__.py +32 -0
- clew_trace-1.0.0/clew/mcp_server/__main__.py +13 -0
- clew_trace-1.0.0/clew/mcp_server/_sdk.py +105 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/_provenance.md +134 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/_templates/ambiguous.json +33 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/_templates/disambiguate.json +40 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/_templates/provenance.json +39 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/_templates/rows.json +14 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/dossier.json +17 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/index.json +13 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/propose_declaration.json +8 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions/search.json +17 -0
- clew_trace-1.0.0/clew/mcp_server/descriptions.py +239 -0
- clew_trace-1.0.0/clew/mcp_server/emptiness.py +449 -0
- clew_trace-1.0.0/clew/mcp_server/freshness.py +417 -0
- clew_trace-1.0.0/clew/mcp_server/server.py +1723 -0
- clew_trace-1.0.0/clew/mcp_server/state.py +905 -0
- clew_trace-1.0.0/clew/mcp_server/tools_query.py +1938 -0
- clew_trace-1.0.0/clew/precommit.py +386 -0
- clew_trace-1.0.0/clew/preprocessor.py +834 -0
- clew_trace-1.0.0/clew/propose/__init__.py +83 -0
- clew_trace-1.0.0/clew/propose/astdefs.py +432 -0
- clew_trace-1.0.0/clew/propose/command.py +250 -0
- clew_trace-1.0.0/clew/propose/context.py +56 -0
- clew_trace-1.0.0/clew/propose/dryrun.py +259 -0
- clew_trace-1.0.0/clew/propose/model.py +179 -0
- clew_trace-1.0.0/clew/propose/notindexed.py +322 -0
- clew_trace-1.0.0/clew/propose/registry.py +488 -0
- clew_trace-1.0.0/clew/propose/render.py +416 -0
- clew_trace-1.0.0/clew/propose/scanning.py +428 -0
- clew_trace-1.0.0/clew/propose/sharedkey_detect.py +319 -0
- clew_trace-1.0.0/clew/propose/sharedkey_report.py +409 -0
- clew_trace-1.0.0/clew/propose/threads_detect.py +443 -0
- clew_trace-1.0.0/clew/propose/threads_report.py +350 -0
- clew_trace-1.0.0/clew/prose.py +168 -0
- clew_trace-1.0.0/clew/py_entrypoints.py +405 -0
- clew_trace-1.0.0/clew/pyast.py +805 -0
- clew_trace-1.0.0/clew/query/__init__.py +239 -0
- clew_trace-1.0.0/clew/query/_common.py +679 -0
- clew_trace-1.0.0/clew/query/corpus.py +961 -0
- clew_trace-1.0.0/clew/query/dossier.py +638 -0
- clew_trace-1.0.0/clew/query/externcalls.py +242 -0
- clew_trace-1.0.0/clew/query/graph.py +487 -0
- clew_trace-1.0.0/clew/query/kconfig.py +519 -0
- clew_trace-1.0.0/clew/query/locks.py +731 -0
- clew_trace-1.0.0/clew/query/macros.py +214 -0
- clew_trace-1.0.0/clew/query/models.py +1931 -0
- clew_trace-1.0.0/clew/query/source.py +387 -0
- clew_trace-1.0.0/clew/query/subject.py +798 -0
- clew_trace-1.0.0/clew/query/symbols.py +1973 -0
- clew_trace-1.0.0/clew/query/traversal.py +412 -0
- clew_trace-1.0.0/clew/reachability.py +239 -0
- clew_trace-1.0.0/clew/requirements.py +780 -0
- clew_trace-1.0.0/clew/scope.py +729 -0
- clew_trace-1.0.0/clew/shared_key_edges.py +2452 -0
- clew_trace-1.0.0/clew/signature.py +353 -0
- clew_trace-1.0.0/clew/stagetimer.py +141 -0
- clew_trace-1.0.0/clew/threads.py +1516 -0
- clew_trace-1.0.0/clew/tiers.py +564 -0
- clew_trace-1.0.0/clew/tomlcompat.py +94 -0
- clew_trace-1.0.0/clew/treescan.py +371 -0
- clew_trace-1.0.0/clew/vocabulary.py +1080 -0
- clew_trace-1.0.0/clew/wire.py +193 -0
- clew_trace-1.0.0/docs/CORE_HYPOTHESIS.md +53 -0
- clew_trace-1.0.0/pyproject.toml +101 -0
- clew_trace-1.0.0/requirements.yaml +945 -0
- clew_trace-1.0.0/tests/conftest.py +270 -0
- clew_trace-1.0.0/tests/data/csample/.doxygen-guard.yaml +28 -0
- clew_trace-1.0.0/tests/data/csample/.pre-commit-config.yaml +10 -0
- clew_trace-1.0.0/tests/data/csample/README.md +25 -0
- clew_trace-1.0.0/tests/data/csample/data_model_keys.yaml +16 -0
- clew_trace-1.0.0/tests/data/csample/docs/architecture.md +56 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/dm.c +60 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/dm.h +25 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/dm_helpers.h +39 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/dm_key.h +10 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/integer_storage.c +39 -0
- clew_trace-1.0.0/tests/data/csample/gen/ingot/integer_storage.h +11 -0
- clew_trace-1.0.0/tests/data/csample/requirements.yaml +40 -0
- clew_trace-1.0.0/tests/data/csample/src/command/command_handler.c +40 -0
- clew_trace-1.0.0/tests/data/csample/src/command/command_handler.h +6 -0
- clew_trace-1.0.0/tests/data/csample/src/dispatch/dm_event_dispatch.c +37 -0
- clew_trace-1.0.0/tests/data/csample/src/dispatch/dm_event_dispatch.h +6 -0
- clew_trace-1.0.0/tests/data/csample/src/event_bus/event_bus.c +78 -0
- clew_trace-1.0.0/tests/data/csample/src/event_bus/event_bus.h +24 -0
- clew_trace-1.0.0/tests/data/csample/src/legacy/legacy_calibration.c +48 -0
- clew_trace-1.0.0/tests/data/csample/src/main.c +47 -0
- clew_trace-1.0.0/tests/data/csample/src/sensor/sensor_driver.c +42 -0
- clew_trace-1.0.0/tests/data/csample/src/sensor/sensor_driver.h +7 -0
- clew_trace-1.0.0/tests/data/csample/src/sound/sound_service.c +18 -0
- clew_trace-1.0.0/tests/data/csample/src/sound/sound_service.h +12 -0
- clew_trace-1.0.0/tests/data/csample/src/telemetry/telemetry.c +30 -0
- clew_trace-1.0.0/tests/data/csample/src/telemetry/telemetry.h +7 -0
- clew_trace-1.0.0/tests/data/csample/test/test_sound.c +29 -0
- clew_trace-1.0.0/tests/data/doxygen_schema.sql +357 -0
- clew_trace-1.0.0/tests/data/kconfigsample/Kconfig +20 -0
- clew_trace-1.0.0/tests/data/kconfigsample/Kconfig.motor +22 -0
- clew_trace-1.0.0/tests/data/kconfigsample/src/motor.c +32 -0
- clew_trace-1.0.0/tests/data/pysample/__init__.py +8 -0
- clew_trace-1.0.0/tests/data/pysample/models.py +33 -0
- clew_trace-1.0.0/tests/data/pysample/spawner.py +230 -0
- clew_trace-1.0.0/tests/data/schema_vocabulary.json +176 -0
- clew_trace-1.0.0/tests/gitfixture.py +173 -0
- clew_trace-1.0.0/tests/integration/conftest.py +243 -0
- clew_trace-1.0.0/tests/integration/test_export_roundtrip.py +170 -0
- clew_trace-1.0.0/tests/integration/test_fixture_fidelity.py +150 -0
- clew_trace-1.0.0/tests/integration/test_gate_coverage_integration.py +304 -0
- clew_trace-1.0.0/tests/integration/test_indexcache_integration.py +616 -0
- clew_trace-1.0.0/tests/integration/test_manifest_replay_integration.py +625 -0
- clew_trace-1.0.0/tests/integration/test_richness.py +230 -0
- clew_trace-1.0.0/tests/integration/test_robustness_integration.py +141 -0
- clew_trace-1.0.0/tests/integration/test_self_index.py +346 -0
- clew_trace-1.0.0/tests/richdb.py +1159 -0
- clew_trace-1.0.0/tests/schema_snapshot.py +161 -0
- clew_trace-1.0.0/tests/test_ast_recovery.py +308 -0
- clew_trace-1.0.0/tests/test_ast_symbols.py +1218 -0
- clew_trace-1.0.0/tests/test_binding_edges.py +565 -0
- clew_trace-1.0.0/tests/test_build_docs_db.py +2210 -0
- clew_trace-1.0.0/tests/test_buildoptions.py +656 -0
- clew_trace-1.0.0/tests/test_cli_surface.py +484 -0
- clew_trace-1.0.0/tests/test_conceptual_search.py +116 -0
- clew_trace-1.0.0/tests/test_coverage.py +413 -0
- clew_trace-1.0.0/tests/test_critical_sections.py +728 -0
- clew_trace-1.0.0/tests/test_datamodel.py +939 -0
- clew_trace-1.0.0/tests/test_declaration.py +439 -0
- clew_trace-1.0.0/tests/test_descriptions.py +418 -0
- clew_trace-1.0.0/tests/test_diagnostics.py +530 -0
- clew_trace-1.0.0/tests/test_disambiguation.py +378 -0
- clew_trace-1.0.0/tests/test_dispatch.py +895 -0
- clew_trace-1.0.0/tests/test_doc_links.py +113 -0
- clew_trace-1.0.0/tests/test_dominated_edges.py +373 -0
- clew_trace-1.0.0/tests/test_dossier_batch.py +352 -0
- clew_trace-1.0.0/tests/test_dossier_one_shot.py +531 -0
- clew_trace-1.0.0/tests/test_doxyfile_refusal_messages.py +218 -0
- clew_trace-1.0.0/tests/test_emptiness.py +480 -0
- clew_trace-1.0.0/tests/test_enrichment.py +152 -0
- clew_trace-1.0.0/tests/test_event_edges.py +402 -0
- clew_trace-1.0.0/tests/test_external_provenance.py +514 -0
- clew_trace-1.0.0/tests/test_extra_input_patterns.py +370 -0
- clew_trace-1.0.0/tests/test_filedocs.py +295 -0
- clew_trace-1.0.0/tests/test_freshness.py +659 -0
- clew_trace-1.0.0/tests/test_git_env_isolation.py +121 -0
- clew_trace-1.0.0/tests/test_gitenv.py +148 -0
- clew_trace-1.0.0/tests/test_graph_stats.py +379 -0
- clew_trace-1.0.0/tests/test_guard_config_discovery.py +279 -0
- clew_trace-1.0.0/tests/test_guard_config_skew.py +343 -0
- clew_trace-1.0.0/tests/test_index_scope_inheritance.py +395 -0
- clew_trace-1.0.0/tests/test_indexcache_doxygen_cache.py +150 -0
- clew_trace-1.0.0/tests/test_init.py +1337 -0
- clew_trace-1.0.0/tests/test_kconfig.py +1125 -0
- clew_trace-1.0.0/tests/test_locks.py +444 -0
- clew_trace-1.0.0/tests/test_macro_query.py +463 -0
- clew_trace-1.0.0/tests/test_macro_recovery.py +371 -0
- clew_trace-1.0.0/tests/test_mcp_routing.py +515 -0
- clew_trace-1.0.0/tests/test_mcp_server.py +3250 -0
- clew_trace-1.0.0/tests/test_operator_excludes.py +292 -0
- clew_trace-1.0.0/tests/test_overrides.py +257 -0
- clew_trace-1.0.0/tests/test_packaging.py +243 -0
- clew_trace-1.0.0/tests/test_payload_completeness.py +266 -0
- clew_trace-1.0.0/tests/test_plugin_manifest.py +233 -0
- clew_trace-1.0.0/tests/test_preprocessor.py +810 -0
- clew_trace-1.0.0/tests/test_propose.py +868 -0
- clew_trace-1.0.0/tests/test_py310_compat.py +438 -0
- clew_trace-1.0.0/tests/test_python_ast.py +821 -0
- clew_trace-1.0.0/tests/test_query.py +1862 -0
- clew_trace-1.0.0/tests/test_r1_richness.py +892 -0
- clew_trace-1.0.0/tests/test_repair_attribute.py +103 -0
- clew_trace-1.0.0/tests/test_requirements_declared.py +419 -0
- clew_trace-1.0.0/tests/test_robustness.py +86 -0
- clew_trace-1.0.0/tests/test_roster_origin_split.py +642 -0
- clew_trace-1.0.0/tests/test_scope.py +507 -0
- clew_trace-1.0.0/tests/test_search_type_corpora.py +296 -0
- clew_trace-1.0.0/tests/test_self_edges.py +562 -0
- clew_trace-1.0.0/tests/test_shared_parse.py +353 -0
- clew_trace-1.0.0/tests/test_stage_timings.py +219 -0
- clew_trace-1.0.0/tests/test_subject_surface.py +590 -0
- clew_trace-1.0.0/tests/test_thread_spawns.py +542 -0
- clew_trace-1.0.0/tests/test_tiers.py +799 -0
- clew_trace-1.0.0/tests/test_treescan_pruning.py +248 -0
- clew_trace-1.0.0/tests/test_vocabulary.py +449 -0
- clew_trace-1.0.0/tests/test_wire.py +197 -0
- clew_trace-1.0.0/tests/vocab_dump.py +51 -0
- clew_trace-1.0.0/tests/wire_expect.py +90 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
# doxygen-guard configuration for clew (dogfooding — we enforce on ourselves).
|
|
3
|
+
#
|
|
4
|
+
# Every function/class in the shipped Python package carries:
|
|
5
|
+
# ## @brief one-line summary
|
|
6
|
+
# ## @version bumped on body change
|
|
7
|
+
# ## @return on non-void functions
|
|
8
|
+
#
|
|
9
|
+
# @req COVERAGE IS NOT GATED. It was, by scripts/req_coverage_audit.py, which is
|
|
10
|
+
# DELETED. Nothing now checks that a requirement claiming `implemented` resolves to
|
|
11
|
+
# an implementing function, or that one claiming `planned` resolves to none.
|
|
12
|
+
# Last measured by hand: 30 of 34 resolve; the 4 that do not are untagged
|
|
13
|
+
# deliberately (two satisfied by configuration and human review, two unbuilt).
|
|
14
|
+
#
|
|
15
|
+
# Also not gated: whether a TEST covers a requirement. req_test_edges is sparse.
|
|
16
|
+
|
|
17
|
+
output_dir: docs/generated/
|
|
18
|
+
|
|
19
|
+
# Declare where our catalog lives, so the INDEX finds it without a CLI flag.
|
|
20
|
+
# Not cosmetic dogfooding: `bd2d34e` taught clew to honour this field
|
|
21
|
+
# precisely because a repo that declares its catalog had the declaration read by
|
|
22
|
+
# the gate and ignored by the index — and we then shipped that fix without
|
|
23
|
+
# applying it to ourselves. A bare `--scope from-guard` build of this repo
|
|
24
|
+
# ingested ZERO catalog rows, so `req_trace` returned implementers with no title,
|
|
25
|
+
# status or acceptance, and the "30 of 34" figure was reproducible only with an
|
|
26
|
+
# undocumented `--requirements` argument. Through the MCP server, which passes no
|
|
27
|
+
# flags, it could not be reproduced at all.
|
|
28
|
+
impact:
|
|
29
|
+
requirements:
|
|
30
|
+
file: requirements.yaml
|
|
31
|
+
|
|
32
|
+
validate:
|
|
33
|
+
exclude:
|
|
34
|
+
- "^\\.venv/"
|
|
35
|
+
- "^\\.claude/"
|
|
36
|
+
- "^workspace/"
|
|
37
|
+
- "^tests/" # tests are exempt (fixtures, asserts)
|
|
38
|
+
- "^scripts/" # dev helpers
|
|
39
|
+
- "^acceptance/" # benchmark harness + evidence, not shipped code
|
|
40
|
+
- "^docs/generated/"
|
|
41
|
+
# NOTE: this key was DUPLICATED — an empty `tags:` sat directly above the real
|
|
42
|
+
# one. YAML takes the last, so the real block won and nothing broke, which is
|
|
43
|
+
# why it survived. The trap is that a reorder or an insertion between them
|
|
44
|
+
# would silently hand the win to the empty mapping, dropping the declared @req
|
|
45
|
+
# id pattern and disabling tag validation with no error anywhere.
|
|
46
|
+
tags:
|
|
47
|
+
# clew's own requirement id shape. Declared so @req tags validate
|
|
48
|
+
# against it — NOT hardcoded anywhere in the pipeline.
|
|
49
|
+
#
|
|
50
|
+
# A BARE ID, nothing else. This pattern used to carry an optional
|
|
51
|
+
# `( \[inferred\])?` suffix, because the project had a convention where a tag
|
|
52
|
+
# could mark its own mapping as inferred rather than asserted, and
|
|
53
|
+
# doxygen-guard validates the WHOLE remainder of the `@req` line rather than
|
|
54
|
+
# just the first token.
|
|
55
|
+
#
|
|
56
|
+
# The convention was RETIRED 2026-07-30 by the owner: "I would rather not put
|
|
57
|
+
# requirements as inferred or not — they should simply be the stated ID,
|
|
58
|
+
# inferred is noise." The marker is gone from the tags, the parser and the
|
|
59
|
+
# schema — `req_edges.confidence` no longer exists, because a column that can
|
|
60
|
+
# hold only one value states nothing.
|
|
61
|
+
req:
|
|
62
|
+
pattern: "^REQ-DDB-[A-Z]+-[0-9]{3}$"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
# BOTH spellings, because `.venv/` with the trailing slash matches a DIRECTORY only and so
|
|
3
|
+
# does not match a SYMLINK named `.venv`. `git add -A` would then stage a tracked symlink
|
|
4
|
+
# pointing at an absolute machine path. Same hazard as the two `doxygen_out` spellings below:
|
|
5
|
+
# one spelling of an ignore silently covers less than a reader assumes.
|
|
6
|
+
.venv
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.pyc
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.ipynb_checkpoints/
|
|
11
|
+
workspace/
|
|
12
|
+
|
|
13
|
+
# Tool caches. `.ruff_cache/` is the one that matters: ruff writes its own
|
|
14
|
+
# `.ruff_cache/.gitignore` containing `*`, so the directory self-hides and looks handled —
|
|
15
|
+
# but that is ruff's file, not this repo's guarantee. Delete the cache and the protection
|
|
16
|
+
# goes with it.
|
|
17
|
+
.cache/
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.tox/
|
|
22
|
+
htmlcov/
|
|
23
|
+
.coverage
|
|
24
|
+
|
|
25
|
+
# Build outputs. `python -m build` runs in CI, so anyone reproducing it locally creates these.
|
|
26
|
+
dist/
|
|
27
|
+
build/
|
|
28
|
+
|
|
29
|
+
# Working-instructions and scratch. `.claude/CLAUDE.md` lives here deliberately: how someone
|
|
30
|
+
# instructs their own agent is theirs to maintain, not a shipped asset.
|
|
31
|
+
.claude/
|
|
32
|
+
|
|
33
|
+
# The doxygen-guard output directory the guard's own config declares
|
|
34
|
+
# (`output_dir: docs/generated/`), so any guard run recreates it.
|
|
35
|
+
docs/generated/
|
|
36
|
+
|
|
37
|
+
# Built indexes and their caches. DEFENSE IN DEPTH, not convenience: clew is pointed at
|
|
38
|
+
# third-party repos, so a stray database in the tree is that repo's source graph — symbol
|
|
39
|
+
# names, file paths, call structure — in a single file. It is the highest-value thing here to
|
|
40
|
+
# leak by accident, and a multi-megabyte binary is exactly what a hurried `git add -A`
|
|
41
|
+
# swallows without anyone reading the diff. `--output` accepts any path, so these patterns
|
|
42
|
+
# exist to keep that true when someone builds an index somewhere new.
|
|
43
|
+
*.db
|
|
44
|
+
*.db-journal
|
|
45
|
+
*.db-wal
|
|
46
|
+
*.idxcache
|
|
47
|
+
|
|
48
|
+
# Doxygen scratch from a HAND-RUN doxygen. The pipeline forces OUTPUT_DIRECTORY away from the
|
|
49
|
+
# target repo, so this only catches manual invocations.
|
|
50
|
+
#
|
|
51
|
+
# TWO SPELLINGS, not a glob. The dot-prefixed form is the one the pipeline produces and
|
|
52
|
+
# `doxygen_out/` never matched it — a leading dot makes it a non-match, which once staged 47
|
|
53
|
+
# files of doxygen XML. `*doxygen_out/` would also swallow a deliberately-named directory, and
|
|
54
|
+
# an over-broad ignore that hides a real file is a worse hazard than the mess it prevents.
|
|
55
|
+
#
|
|
56
|
+
# Deliberately NOT ignoring bare `html/` or `xml/` for the same reason: those are ordinary
|
|
57
|
+
# directory names.
|
|
58
|
+
doxygen_out/
|
|
59
|
+
.doxygen_out/
|
|
60
|
+
|
|
61
|
+
# Private-target acceptance evidence. A cell transcript quotes the target's source verbatim,
|
|
62
|
+
# so committing one publishes that source. THIS IS THE ONLY PROTECTION — there is no scrub
|
|
63
|
+
# gate, so a `git add -f` here is unguarded. Both spellings, per the rule at the top.
|
|
64
|
+
acceptance/targets/internal
|
|
65
|
+
acceptance/targets/internal/
|
|
66
|
+
|
|
67
|
+
# In-flux findings. A committed workbook becomes a citable claim before it has been measured;
|
|
68
|
+
# measured results go in `acceptance/targets/*/` beside the transcripts that produced them.
|
|
69
|
+
acceptance/docs/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
exclude: |
|
|
3
|
+
(?x)^(
|
|
4
|
+
\.venv/.*|
|
|
5
|
+
\.claude/.*|
|
|
6
|
+
workspace/.*|
|
|
7
|
+
docs/generated/.*|
|
|
8
|
+
acceptance/targets/.*|
|
|
9
|
+
tests/data/.*|
|
|
10
|
+
.*\.egg-info/.*
|
|
11
|
+
)$
|
|
12
|
+
|
|
13
|
+
repos:
|
|
14
|
+
- repo: https://github.com/brandon-arrendondo/knots
|
|
15
|
+
rev: v1.15.1
|
|
16
|
+
hooks:
|
|
17
|
+
- id: knots
|
|
18
|
+
|
|
19
|
+
- repo: https://github.com/tvanfossen/doxygen-guard
|
|
20
|
+
rev: 1.4.2
|
|
21
|
+
hooks:
|
|
22
|
+
- id: doxygen-guard
|
|
23
|
+
args: [validate]
|
|
24
|
+
files: ^(clew|acceptance|tests)/.*\.py$
|
|
25
|
+
|
|
26
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
27
|
+
rev: v0.16.1
|
|
28
|
+
hooks:
|
|
29
|
+
- id: ruff
|
|
30
|
+
args: ["--fix", "--select=E4,E7,E9,F", "--target-version=py310", "--cache-dir=.cache/ruff"]
|
|
31
|
+
- id: ruff-format
|
|
32
|
+
args: ["--line-length=100", "--target-version=py310", "--cache-dir=.cache/ruff"]
|
|
33
|
+
|
|
34
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
35
|
+
rev: v6.0.0
|
|
36
|
+
hooks:
|
|
37
|
+
- id: trailing-whitespace
|
|
38
|
+
- id: end-of-file-fixer
|
|
39
|
+
- id: check-yaml
|
|
40
|
+
- id: check-json
|
|
41
|
+
- id: check-toml
|
|
42
|
+
- id: check-added-large-files
|
|
43
|
+
args: ['--maxkb=512']
|
|
44
|
+
- id: check-merge-conflict
|
|
45
|
+
- id: check-symlinks
|
|
46
|
+
- id: mixed-line-ending
|
|
47
|
+
|
|
48
|
+
- repo: local
|
|
49
|
+
hooks:
|
|
50
|
+
- id: pytest
|
|
51
|
+
name: pytest
|
|
52
|
+
## `cache_dir` is set once, in pyproject's `[tool.pytest.ini_options]`. It used to be
|
|
53
|
+
## overridden here as well, which is two places for one setting and therefore one place
|
|
54
|
+
## to forget when it changes.
|
|
55
|
+
entry: .venv/bin/python -m pytest tests/ -q
|
|
56
|
+
language: system
|
|
57
|
+
pass_filenames: false
|
|
58
|
+
always_run: true
|
clew_trace-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tristan Van Fossen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: clew-trace
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Queryable, layered symbol database for C, C++ and Python codebases: doxygen + tree-sitter call graph, threads, locks and dataflow edges, served to AI agents over MCP.
|
|
5
|
+
Project-URL: Homepage, https://github.com/tvanfossen/clew
|
|
6
|
+
Project-URL: Repository, https://github.com/tvanfossen/clew
|
|
7
|
+
Project-URL: Issues, https://github.com/tvanfossen/clew/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: call-graph,documentation,doxygen,embedded,mcp,static-analysis,traceability,tree-sitter
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: anyio>=4.0
|
|
22
|
+
Requires-Dist: doxygen-guard<2,>=1.4.2
|
|
23
|
+
Requires-Dist: kconfiglib<15,>=14
|
|
24
|
+
Requires-Dist: mcp-types<3,>=2
|
|
25
|
+
Requires-Dist: mcp<3,>=2
|
|
26
|
+
Requires-Dist: pyyaml<8,>=6.0
|
|
27
|
+
Requires-Dist: rich>=13.0
|
|
28
|
+
Requires-Dist: tomli>=2; python_version < '3.11'
|
|
29
|
+
Requires-Dist: tree-sitter-c<0.23.5,>=0.23
|
|
30
|
+
Requires-Dist: tree-sitter-cpp<0.23.5,>=0.23
|
|
31
|
+
Requires-Dist: tree-sitter-python<0.23.5,>=0.23
|
|
32
|
+
Requires-Dist: tree-sitter<0.24,>=0.23
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pre-commit>=3.6; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# clew
|
|
41
|
+
|
|
42
|
+
> *A **clew** is the ball of thread Ariadne gave Theseus — the thing you follow to find your way
|
|
43
|
+
> out of the labyrinth. It is the archaic spelling, and the direct ancestor, of the word "clue".*
|
|
44
|
+
|
|
45
|
+
**Give an agent the thread, not a map of the whole maze.** `clew` runs doxygen and tree-sitter
|
|
46
|
+
over a C, C++ or Python repository and compiles the result into a SQLite graph — symbols, call
|
|
47
|
+
edges, callbacks, threads, locks, dataflow, requirement links, file inventory, prose — then
|
|
48
|
+
serves it over MCP. Pull on one symbol and you get its callers, its locks, the thread it runs on
|
|
49
|
+
and the requirement it satisfies.
|
|
50
|
+
|
|
51
|
+
**What it is for is aggregation, not capability.** Nothing here is a question `grep` cannot
|
|
52
|
+
answer. The difference is that "who calls this, transitively, across a function-pointer
|
|
53
|
+
boundary, and which of those run on another thread" is one query instead of a dozen searches
|
|
54
|
+
and a mental model you rebuild every session. It is a cache for work you would otherwise redo.
|
|
55
|
+
|
|
56
|
+
**doxygen-guard is optional.** [It](https://github.com/tvanfossen/doxygen-guard) is a
|
|
57
|
+
pre-commit gate that keeps a repo's doxygen accurate, and a repo that uses it gets a richer
|
|
58
|
+
index — briefs, requirement tags, versioned comments. But `clew` needs neither the gate nor a
|
|
59
|
+
Doxyfile: a repo that declares nothing gets its whole tree indexed, and three of the four repos
|
|
60
|
+
in `acceptance/targets/` are measured that way. If you want the gate, it is a separate tool with
|
|
61
|
+
one job; if you do not, this still works.
|
|
62
|
+
|
|
63
|
+
## The four tools
|
|
64
|
+
|
|
65
|
+
An agent is the user, and these are the whole of what the server exposes. A human-facing browser
|
|
66
|
+
over the index is deliberately deferred: repository searchability for a person is what an IDE
|
|
67
|
+
already is, so building a second one is not where the value is.
|
|
68
|
+
|
|
69
|
+
| tool | for |
|
|
70
|
+
|---|---|
|
|
71
|
+
| **`dossier`** | Everything about one named symbol in ONE reply: body excerpt, brief, both edge directions, locks held, the thread it runs on, requirements and covering tests, liveness. There is no separate `callers` or `chain_trace` tool — this is why. |
|
|
72
|
+
| **`search`** | Find the name when you do not have one, or enumerate a whole layer. `corpus=` picks: symbols, prose, files, config, locks, threads. An empty result is graded and says what it did *not* read. |
|
|
73
|
+
| **`index(action=…)`** | Administration, not questions: `refresh` builds, `status` diagnoses, `targets` lists every indexed repo, `stats` grades one, `cull` deletes aged-out databases. |
|
|
74
|
+
| **`propose_declaration`** | Reads the repo's evidence and proposes a `.clew.yaml`, entirely as comments, for when a causal layer looks emptier than the code warrants. |
|
|
75
|
+
|
|
76
|
+
Every reply names the `target` it answered from, and carries a `staleness` block only when that
|
|
77
|
+
index is actually stale — so a current index costs nothing to establish.
|
|
78
|
+
|
|
79
|
+
Reads are live: query tools open the database per call. Writes are refused when the server
|
|
80
|
+
process predates its own source, because a build through stale logic can silently drop whole
|
|
81
|
+
layers and then report success.
|
|
82
|
+
|
|
83
|
+
## What's in the database
|
|
84
|
+
|
|
85
|
+
| Layer | Answers |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Structure | "what is this symbol?" — doxygen symbol tables, briefs, extents |
|
|
88
|
+
| Prose | "*why* does this subsystem exist?" — README/docs markdown, FTS5-indexed |
|
|
89
|
+
| Call graph | "who calls this, and how sure are we?" — doxygen ×2 + tree-sitter, every edge carrying `source` + `confidence` |
|
|
90
|
+
| Callbacks | "who reaches it through a function pointer?" — registration→dispatch resolution |
|
|
91
|
+
| Shared-key dataflow | "writing this key affects whom?" — data-model / queue edges the call graph cannot see |
|
|
92
|
+
| **Threads** | "which thread does this run on?" — spawn-site harvest + membership closure |
|
|
93
|
+
| **Dispatch semantics** | inline vs queued vs keyed; edge-triggered; **which hops cross a thread boundary** |
|
|
94
|
+
| **Terminus** | where a chain leaves the repo through an externally-registered callback |
|
|
95
|
+
| Requirements | "what implements / tests REQ-X?" — `@req` traceability |
|
|
96
|
+
| Liveness | "does this code even run?" — reachability over the non-fuzzy call graph |
|
|
97
|
+
|
|
98
|
+
The point of the semantic layers (threads, dispatch mode, terminus) is **causal
|
|
99
|
+
chains**: reconstructing "message arrives → who handles it → where it goes →
|
|
100
|
+
which thread hops → where it leaves the library → what requirements it touches"
|
|
101
|
+
as a single query, not a manual archaeology session.
|
|
102
|
+
|
|
103
|
+
## Measured
|
|
104
|
+
|
|
105
|
+
Answering from the index is compared against an agent with only `Read`/`Grep`/`Glob`/`Bash` —
|
|
106
|
+
same repository, same model, same sitting, same frozen questions. Four repositories, both arms,
|
|
107
|
+
two model tiers. Every transcript, metric and per-mark grade is committed beside its rubric in
|
|
108
|
+
[`acceptance/targets/`](https://github.com/tvanfossen/clew/tree/main/acceptance/targets); the results are in each target's `result.md`.
|
|
109
|
+
|
|
110
|
+
Run it on yours: [`acceptance/targets/TEMPLATE/`](https://github.com/tvanfossen/clew/tree/main/acceptance/targets/TEMPLATE) has the rubric
|
|
111
|
+
template and the runbook.
|
|
112
|
+
|
|
113
|
+
## What semver covers
|
|
114
|
+
|
|
115
|
+
**Covered** — a breaking change here means a major bump:
|
|
116
|
+
|
|
117
|
+
- the `clew` CLI verbs and their flags (`init`, `propose`, `export`, and the
|
|
118
|
+
build invocation)
|
|
119
|
+
- the MCP **tool names and their wire keys** — a consumer reads `edge_class`,
|
|
120
|
+
`confidence`, `source` and friends by name
|
|
121
|
+
- `clew.query`'s public functions and the dataclasses they return
|
|
122
|
+
|
|
123
|
+
**Not covered**, and deliberately so:
|
|
124
|
+
|
|
125
|
+
- **the SQLite schema.** `CLEW_BUILD_VERSION` exists precisely because the
|
|
126
|
+
schema moves; a consumer detects a stale index by comparing it, not by assuming
|
|
127
|
+
stability. Query through `clew.query`, not with your own SQL.
|
|
128
|
+
|
|
129
|
+
`CLEW_BUILD_VERSION`, `read_build_signature` and `index_unusable_reason` are
|
|
130
|
+
re-exported from `clew.query` and are therefore **covered**, so the documented
|
|
131
|
+
way to check freshness does not reach into an internal:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from clew.query import CLEW_BUILD_VERSION, read_build_signature
|
|
135
|
+
|
|
136
|
+
if read_build_signature("clew.db") != CLEW_BUILD_VERSION:
|
|
137
|
+
... # rebuild: this index predates the current pipeline
|
|
138
|
+
```
|
|
139
|
+
- `clew.*` internals — anything not re-exported from `query`
|
|
140
|
+
- the `.idxcache` format
|
|
141
|
+
|
|
142
|
+
## Install
|
|
143
|
+
|
|
144
|
+
The package is always required — the server *is* the `clew-mcp` command it installs, and no
|
|
145
|
+
plugin or registration step can supply that. Install it so the command is on PATH whatever
|
|
146
|
+
virtualenv happens to be active:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pipx install clew-trace # or: pip install --user clew-trace
|
|
150
|
+
sudo apt install doxygen # REQUIRED, and not pip-installable
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
A plain `pip install` inside a project venv also works, but only while that venv is on the PATH
|
|
154
|
+
your editor launches the server with — which is the usual reason a correctly-registered server
|
|
155
|
+
shows as failed to connect.
|
|
156
|
+
|
|
157
|
+
Then register it, EITHER as a Claude Code plugin:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
/plugin marketplace add tvanfossen/clew
|
|
161
|
+
/plugin install clew@clew
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
or by hand, which also doctors the environment and names what is missing:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
clew init # writes ./.mcp.json (--scope global for user-level)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Pick one. Both register a server named `clew`, so doing both leaves two sources for one entry —
|
|
171
|
+
run `claude mcp remove clew` first if you have already used `clew init`.
|
|
172
|
+
|
|
173
|
+
`doxygen` is a C++ binary, so it cannot be a Python dependency — `clew init` checks for it and
|
|
174
|
+
tells you if it is missing. Everything else, including the MCP SDK, comes with the package.
|
|
175
|
+
|
|
176
|
+
### Working on clew itself
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
python3 -m venv .venv
|
|
180
|
+
.venv/bin/pip install -e .[dev] # pipeline + doxygen-guard + gates
|
|
181
|
+
sudo apt install doxygen # external binary (plantuml optional)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Build a database
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
.venv/bin/python -m clew \
|
|
188
|
+
--output clew.db \
|
|
189
|
+
--repo-root /path/to/repo
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
That is the whole common case. The Doxyfile is discovered, and a repo that ships none gets
|
|
193
|
+
one synthesized over its declared scope.
|
|
194
|
+
|
|
195
|
+
**Six arguments, and only six:**
|
|
196
|
+
|
|
197
|
+
| Flag | For |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `--output` | where the database is written |
|
|
200
|
+
| `--repo-root` | the repository to index |
|
|
201
|
+
| `--declare FILE` | a declaration document stated for this build (see below) |
|
|
202
|
+
| `--exclude PATH…` | paths to leave out; recorded into the index and replayed by later builds |
|
|
203
|
+
| `--rebuild` | ignore every cached entry, then re-warm the cache |
|
|
204
|
+
| `--verbose` | more on stderr; changes nothing that reaches the index |
|
|
205
|
+
|
|
206
|
+
Everything a target repo can *declare* lives in one place — its own
|
|
207
|
+
`.clew.yaml` — and `--declare` states the same document for a repository you do
|
|
208
|
+
not own and must leave byte-identical:
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
# a --declare document, or <repo>/.clew.yaml
|
|
212
|
+
index_scope: {roots: [src, include], excludes: [src/generated]}
|
|
213
|
+
preprocessor: {predefined: [MY_FEATURE_C], config_header: auto}
|
|
214
|
+
requirements: docs/requirements.yaml
|
|
215
|
+
thread_patterns: {spawns: [{name: osThreadNew, entry_arg_index: 0}]}
|
|
216
|
+
shared_key_patterns: {writers: [{name_prefix: Store_Set}], readers: [{name_prefix: Store_Get}]}
|
|
217
|
+
locks: {locks: [{name: MyGuard, form: raii}]}
|
|
218
|
+
dispatch: {interfaces: [...]}
|
|
219
|
+
mqtt_dispatch: {subscribe_functions: [...]}
|
|
220
|
+
data_model: tools/dm_full.toml
|
|
221
|
+
kconfig: {path: Kconfig}
|
|
222
|
+
event_tags: {emits: produce, handles: consume}
|
|
223
|
+
entry_patterns: ["%trampoline%", "on_%"]
|
|
224
|
+
enrich: docs/architecture_topics.yaml
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Nothing about a target repo is hardcoded: every section above is a declared override of
|
|
228
|
+
a built-in default. The identical mapping is the `options` argument of `build_index()` and
|
|
229
|
+
of the MCP `index(action="refresh")` tool, so an agent with no shell can state any of it.
|
|
230
|
+
|
|
231
|
+
The repo's `.doxygen-guard.yaml` — which supplies the declared `@req` id pattern and the
|
|
232
|
+
catalog column mapping — is **discovered** from `--repo-root`: its root, then the path the
|
|
233
|
+
doxygen-guard pre-commit hook names in its own `--config` argument, then
|
|
234
|
+
`conf/ | config/ | .config/`.
|
|
235
|
+
|
|
236
|
+
### Commit what you stated
|
|
237
|
+
|
|
238
|
+
`--declare` reads a document in; `export` writes one out, so a discovery made against a
|
|
239
|
+
repository you do not own can be committed to one you do:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
.venv/bin/python -m clew export --index clew.db # to stdout
|
|
243
|
+
.venv/bin/python -m clew export --index clew.db --output .clew.yaml
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
It emits **only what was stated** — never the built-in defaults. That is deliberate: a file
|
|
247
|
+
asserting you declared every default would freeze those defaults into your repo, so a later
|
|
248
|
+
improvement to one would be shadowed by a value nobody remembers committing. An index that
|
|
249
|
+
recorded no statement exports a document that says so.
|
|
250
|
+
|
|
251
|
+
## Without an agent
|
|
252
|
+
|
|
253
|
+
The same four capabilities are importable, returning JSON-serializable dataclasses. This is what
|
|
254
|
+
the MCP server is a view over, so no query logic exists twice.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
from clew.query import dossier, search
|
|
258
|
+
|
|
259
|
+
dossier("clew.db", "sensor_poll") # identity, reqs, both edge directions, locks, threads, liveness
|
|
260
|
+
search("clew.db", "retry backoff") # find the name when you do not have one
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Layout
|
|
264
|
+
|
|
265
|
+
| Path | What |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `clew/` | The pipeline (`python -m clew`) |
|
|
268
|
+
| `clew/query/` | The stable query API the MCP server is a view over |
|
|
269
|
+
| `tests/` | `.venv/bin/python -m pytest tests/ -q` (add `--integration` for the tier that builds real repos) |
|
|
270
|
+
| `acceptance/targets/<t>/<version>/` | Frozen rubrics and the committed results of every grid run against them |
|
|
271
|
+
| `acceptance/bench/` | The harness. Acceptance-only by design — deliberately NOT in the pre-commit gate |
|
|
272
|
+
|
|
273
|
+
## Dogfooding
|
|
274
|
+
|
|
275
|
+
clew runs its own gate: `.venv/bin/pre-commit run --all-files` → ruff (lint +
|
|
276
|
+
format), doxygen-guard (`@brief`/`@version`/`@return` presence on the shipped
|
|
277
|
+
package), and the full test suite. Its own catalog lives in `requirements.yaml`.
|
|
278
|
+
|
|
279
|
+
## doxygen-guard
|
|
280
|
+
|
|
281
|
+
[doxygen-guard](https://github.com/tvanfossen/doxygen-guard) is a separate tool, consumed here as
|
|
282
|
+
a pip dependency and pinned by `rev:` in `.pre-commit-config.yaml`. Its scope is
|
|
283
|
+
validation, traceability and change impact; nothing here extends it, and `clew` does not require
|
|
284
|
+
it — see the top of this file.
|