diff --git a/src/copilot_usage/docs/architecture.md b/src/copilot_usage/docs/architecture.md index 689586a1..d495dee0 100644 --- a/src/copilot_usage/docs/architecture.md +++ b/src/copilot_usage/docs/architecture.md @@ -39,6 +39,7 @@ Monorepo containing Python CLI utilities that share tooling, CI, and common depe | `report.py` | Rich-formatted terminal output — summary tables (with Model Calls and User Msgs columns), live view, premium request breakdown. Shows raw counts and `~`-prefixed premium cost estimates for live/active sessions; historical post-shutdown views display exact API-provided numbers. | | `render_detail.py` | Session detail rendering — extracted from report.py. Displays event timeline, per-event metadata, and session-level aggregates. | | `_formatting.py` | Shared formatting utilities — `format_duration()` and `format_tokens()` with doctest-verified examples. Used by report.py and render_detail.py. | +| `_fs_utils.py` | Shared filesystem/caching utilities — `lru_insert` (LRU eviction for module-level `OrderedDict` caches) and `safe_file_identity` (returns `(mtime_ns, size)` for robust cache-invalidation; returns `None` on any `OSError`). Used by `parser.py` and `vscode_parser.py`. | | `pricing.py` | Model pricing registry — multiplier lookup, tier categorization. Multipliers are used for `~`-prefixed cost estimates in live/active views (`render_live_sessions`, `render_cost_view`); historical post-shutdown views use exact API-provided numbers exclusively. | | `logging_config.py` | Loguru setup — stderr warnings only, no file output. Uses a `_PatcherRecord` TypedDict to type-check the emoji-injection patcher without importing the unresolvable `loguru.Record` type at runtime. Called once from CLI entry point. | | `vscode_parser.py` | VS Code Copilot Chat log parser — discovers log files per platform (macOS/Windows/Linux), parses `ccreq:` lines with regex, aggregates into `VSCodeLogSummary`. | diff --git a/tests/test_docs.py b/tests/test_docs.py index f15bf17b..41379ca8 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -145,6 +145,32 @@ def test_since_last_shutdown_documents_premium_cost_estimate() -> None: ) +def test_components_table_lists_all_modules() -> None: + """Every .py module in src/copilot_usage/ (excluding __init__.py and the + docs/ subdirectory) must appear in the ### Components table in + architecture.md.""" + pkg_dir = Path(__file__).parents[1] / "src" / "copilot_usage" + on_disk = {p.name for p in pkg_dir.glob("*.py") if p.name != "__init__.py"} + components_section_match = re.search( + r"^###\s+Components\b.*?(?=^#{1,6}\s+|\Z)", + _ARCH_MD, + re.MULTILINE | re.DOTALL, + ) + assert components_section_match, ( + "Could not find the '### Components' section in architecture.md" + ) + components_section = components_section_match.group(0) + # Extract backtick-quoted module names from rows in the Components table only. + in_table = set( + re.findall(r"^\|\s*`([^`]+\.py)`\s*\|", components_section, re.MULTILINE) + ) + missing = on_disk - in_table + assert not missing, ( + f"Modules missing from the ### Components table in " + f"architecture.md: {sorted(missing)}" + ) + + def test_architecture_detect_resume_lists_all_indicators() -> None: """The _detect_resume() description in architecture.md must mention the three true resume indicators and the separately-tracked turn_start event."""