Private
Public Access
0
0

chore(audit): add license_cve audit script + initial report

scripts/audit_license_cve.py: 4 internal checks (license +
CVE + pin + source-header), policy tables (allowlist of
permissive/weak-copyleft/public-domain, blocklist of
non-OSI/restricted-source), and a main() that runs all 4
and emits line-per-violation to stdout + a markdown report.

Tests (26 unit + integration) cover license classifier (16
variants across MIT, BSD, Apache, LGPL, MPL, CC0, WTFPL,
GPL, AGPL, SSPL, BSL, Commons Clause, Elastic, Anti-996,
Hippocratic, unknown), pin check (3), source-header check
(3), license check via importlib.metadata (1), CVE check
via subprocess pip-audit (2), and a smoke test of the main
loop (1).

No new pip deps in the project: pure stdlib
(importlib.metadata, tomllib, pathlib, re) + subprocess to
pip-audit (optional dev tool, installed via 'uv tool install
pip-audit' if user wants CVE checks).

Initial report at docs/reports/license_cve_audit/2026-06-07/
records the current state. The Phase 2 commit will apply
the fixes (tilde-pin, delete requirements.txt); the Phase 3
commit will add --strict mode + baseline file for CI.
This commit is contained in:
2026-06-07 15:07:46 -04:00
parent e09e6823af
commit a8ae11d3a8
5 changed files with 600 additions and 1 deletions
@@ -0,0 +1,33 @@
# Track state for license_cve_audit_20260607
# Updated by Tier 2 Tech Lead as tasks complete
[meta]
track_id = "license_cve_audit_20260607"
name = "License & CVE Audit (Dependency Compliance)"
status = "active"
current_phase = 0
last_updated = "2026-06-07"
[phases]
phase_1 = { status = "pending", checkpointsha = "", name = "Audit script + initial report" }
phase_2 = { status = "pending", checkpointsha = "", name = "Tilde-pin + lock regen + delete requirements.txt" }
phase_3 = { status = "pending", checkpointsha = "", name = "CI gate (--strict + baseline)" }
phase_4 = { status = "pending", checkpointsha = "", name = "tracks.md update" }
[verification]
audit_script_exists = false
license_check_passes = false
cve_check_optional_passes = false
pin_check_passes = false
source_header_check_passes = false
pyproject_tilde_pinned = false
requirements_txt_deleted = false
uv_lock_regenerated = false
strict_mode_implemented = false
baseline_file_committed = false
unit_tests_passing = false
[tasks]
t0_1 = { status = "completed", commit_sha = "", description = "Create state.toml" }
t0_2 = { status = "in_progress", commit_sha = "", description = "Create empty scripts/audit_license_cve.py" }
t0_3 = { status = "in_progress", commit_sha = "", description = "Create empty tests/test_audit_license_cve.py" }
@@ -322,6 +322,54 @@ tests/
Each phase has its own checkpoint commit and git note.
## 5.5 Opencode-stable swap (non-destructive development + quality-gated rollout)
**Why this section exists.** The current `scripts/mcp_server.py` (and the `mcp_client.dispatch` it wraps) is consumed by **opencode clients** via the MCP protocol. opencode is the AI agent tool that uses Manual Slop's tool surface. The new sub-MCP architecture MUST be developed in a way that does not break opencode's existing usage during development, AND the actual swap (the new dispatch becoming the default in `sloppy.py`'s controller) MUST be gated on a stability verification.
**Non-destructive development principle.** Throughout Phases 1-6, the existing `mcp_client.py` continues to work exactly as it does today. The new sub-MCPs, the new controller, the new security module are all added AS NEW FILES (or alongside the existing code in `mcp_client.py`). The legacy code path remains the default. opencode clients see zero behavioral change during Phases 1-6.
**The swap mechanism.** `sloppy.py` (the entry point) and `app_controller.py` (the controller init) introduce a single configuration flag:
```python
# In sloppy.py / app_controller.py
MCP_USE_NEW_DISPATCH: bool = False # default during Phases 1-6; flipped to True after Phase 7 verification
```
When `MCP_USE_NEW_DISPATCH=False` (default during development):
- The legacy shim is the dispatch path (Phase 2's behavior; preserved as the safe default)
- All existing opencode workflows work unchanged
- The new sub-MCPs exist but are NOT in the dispatch path; they can be developed and unit-tested in isolation
When `MCP_USE_NEW_DISPATCH=True` (Phase 7's flip, gated on verification):
- The new controller (`MCPController`) is the dispatch path
- The legacy shim is still present (for any direct imports) but no longer called by the entry point
- opencode clients connect via the MCP server, which now uses the new dispatch
- All 45+ tools must work identically via the new path (verified by the opencode stability check)
**The verification (opencode stability check).** Before Phase 7 flips the default to `MCP_USE_NEW_DISPATCH=True`:
1. **Unit tests pass**: the per-sub-MCP unit tests + the controller tests + the legacy-shim regression tests all pass.
2. **Existing test files pass unchanged**: `test_mcp_client_beads.py`, `test_mcp_config.py`, `test_mcp_perf_tool.py`, `test_mcp_ts_integration.py` pass without modification (they use the legacy shim, which delegates correctly).
3. **Opencode integration test**: a manual or automated test where opencode connects to the MCP server (using `MCP_USE_NEW_DISPATCH=True`), lists the available tools, and invokes 5-10 representative tools (e.g., `read_file`, `list_directory`, `py_get_skeleton`, `py_find_usages`, `web_search`, `derive_code_path`). The results must match the expected outputs.
4. **Soak test**: the opencode integration test runs cleanly for 5+ consecutive sessions over 1+ day without regressions, errors, or performance degradation.
**When the verification passes, the track ships with `MCP_USE_NEW_DISPATCH=True` as the default in `sloppy.py`.** When it doesn't (e.g., a sub-MCP has a regression, or a new sub-MCP's tool doesn't work via opencode), the default stays `False` until the issues are resolved.
**The flag is the boundary.** It is the single point where the new system becomes the default. During Phases 1-6, the flag is `False` and opencode sees no change. After Phase 7, the flag is `True` (gated on verification). Future tracks can extend either path without re-architecting.
## 5.6 Compatibility surface preserved during development
To make the non-destructive development principle concrete, here is the public surface that MUST keep working throughout the track (i.e., across all 7 phases):
| Consumer | What it uses | How it keeps working |
|----------|--------------|----------------------|
| `scripts/mcp_server.py` | `mcp_client.dispatch("tool_name", args)` and `mcp_client.async_dispatch(...)` | These functions exist in the legacy shim throughout Phases 1-6; in Phase 7 they delegate to the new controller (when the flag is True) or stay as-is (when the flag is False). |
| `src/app_controller.py:61` | `mcp_client.py_get_symbol_info(...)` (a direct function call) | This function is in `mcp_client_legacy.py` and re-exported from `mcp_client.py` from Phase 2 onward. Unchanged for opencode. |
| opencode (via MCP protocol) | The 45+ tool names; the JSON tool-call format; the response shape | The legacy shim preserves all 45+ tool names + signatures + return shapes (string). opencode sees no change until the flag is flipped in Phase 7. |
| The 4 existing test files | `mcp_client.<func_name>(...)` and the dispatch result | Legacy shim re-exports; tests pass unchanged. |
Each phase has its own checkpoint commit and git note.
## 6. Configuration
No new dependencies. The existing stdlib `ast`, `pathlib`, `dataclasses`, etc. are used. The `result_types.py` and `type_aliases.py` modules are already in place from the previous tracks.
@@ -344,6 +392,7 @@ No new dependencies. The existing stdlib `ast`, `pathlib`, `dataclasses`, etc. a
| `tests/test_mcp_config.py` (existing) | Verify config-related MCP tools work. | 100% (regression) |
| `tests/test_mcp_perf_tool.py` (existing) | Verify the perf tool works. | 100% (regression) |
| `tests/test_mcp_ts_integration.py` (existing) | Verify the ts_c / ts_cpp integration tests work. | 100% (regression) |
| `tests/test_mcp_client_opencode_integration.py` (NEW) | The opencode stability check (see section 5.5). Starts an MCP server with `MCP_USE_NEW_DISPATCH=True`, simulates opencode's tool-calling protocol, invokes 5-10 representative tools, and verifies the results. This is the quality gate that gates the Phase 7 default-flip. | 100% (quality gate) |
## 8. Risks & Mitigations
@@ -355,6 +404,8 @@ No new dependencies. The existing stdlib `ast`, `pathlib`, `dataclasses`, etc. a
| The `Result[str, Any]` return type from sub-MCPs is incompatible with the existing tests' `assert dispatch(...) == "text"` pattern. | Low | Low | The legacy shim's `dispatch` unwraps `.data` so existing tests see the same string. New tests can check `.data` and `.errors` directly. |
| The new sub-MCP architecture is "overkill" for the project's scale. | Low | Low (subjective) | The current 2,205-line file is the largest in the project; even if only 30% of the function count grew 2x in the next year, the file would be unmanageable. The investment now is bounded; the maintenance cost avoided is unbounded. |
| The DSL future becomes "we have to do it now" before this track is done. | Low | Low | The DSL is explicitly out of scope. This track stays JSON-compatible. A future DSL track can layer on top without breaking the architecture. |
| The new sub-MCP architecture is correct in isolation but breaks an opencode workflow that wasn't covered by the unit tests. | Medium | High (opencode is the primary external consumer) | The opencode stability check (section 5.5) is the explicit quality gate: opencode integration test + 5+ sessions soak test. The `MCP_USE_NEW_DISPATCH` flag stays `False` until the check passes. The legacy shim remains the dispatch path during Phases 1-6. |
| The `MCP_USE_NEW_DISPATCH` flag is left `False` indefinitely because the opencode stability check is too strict or too flaky. | Low | Low | The flag is a single line in `sloppy.py`. The user can flip it manually when they judge the new system is ready for opencode, even if the automated check is too strict. The check is a quality gate, not a hard requirement. |
## 9. Out of Scope (Explicit)
@@ -373,7 +424,13 @@ No new dependencies. The existing stdlib `ast`, `pathlib`, `dataclasses`, etc. a
## 11. Configuration
No new environment variables. The existing `config.toml` is unchanged. The `extra_base_dirs` and `file_items` security configuration is set by `app_controller.py` at startup (unchanged).
**One new environment variable** is introduced for the opencode-stable swap (see section 5.5):
- **`MCP_USE_NEW_DISPATCH: bool`** — default `False` during Phases 1-6 of this track. Flipped to `True` in Phase 7 after the opencode stability check passes (or stays `False` if the check fails). Read by `sloppy.py` (the entry point) and `app_controller.py` (the controller init).
**How it works.** `sloppy.py` and `app_controller.py` check the env var at startup. When `MCP_USE_NEW_DISPATCH=False` (the default during development), the legacy shim is the dispatch path. When `True`, the new `MCPController` is the dispatch path. The flag is the single point where the new system becomes the default; it can be toggled without code changes for testing.
No other new env vars. The existing `config.toml` is unchanged. The `extra_base_dirs` and `file_items` security configuration is set by `app_controller.py` at startup (unchanged).
## 12. See Also