Accepted
PH-18 Slice 18.3 requires that "each subpackage exposes a typed
module-level API; cross-subpackage calls go through public APIs
only." With the runtime now spanning ~119 source files across
twelve subpackages (ai/, cicd/, context/, core/,
forge* modules, memory/, persistence/, plugins/,
policy/, robustness/, runtime/, security/, verify/,
voice/), informal cross-imports into private modules accumulate
quickly without a documented rule.
-
Public surface — every subpackage with a non-trivial
__init__.py(>5 non-blank, non-comment lines) is treated as having a documented public API. Cross-subpackage callers import frommythic_vibe_cli.<subpackage>and rely on what that subpackage's__init__.pyre-exports. -
Private modules — anything inside a subpackage that isn't re-exported from
__init__.pyis private. Modules in other subpackages should not import from those private modules. -
Within-subpackage imports — relative imports (
from .x import y) and explicitfrom mythic_vibe_cli.<sub>.x import yfrom a sibling module inside the same subpackage are always allowed. The boundary applies only to cross- subpackage access. -
Top-level files (
app.py,commands.py,errors.py,exit_codes.py, etc.) are not subpackages — they are permitted to reach into any subpackage's private modules when explicit import-time wiring requires it. This pragmatic carve-out reflects the fact thatcommands.pyis the central dispatch layer and would need a parallel re- export tax otherwise. -
Subpackages with empty / placeholder
__init__.pyare not gated. The threshold (>5 non-blank, non-comment lines) is intentional — a fresh subpackage starts with a docstring and afrom __future__ import annotations; once it accumulates a real public surface, the audit starts enforcing the contract. -
Verification —
mythic_vibe_cli.robustness.api_auditwalks the runtime tree viaastand surfaces every cross-subpackage import that targets a private module. Reporting only — the audit never mutates source. Operators triage findings incrementally; legitimate cross-imports add the symbol to the target subpackage's__init__.pyand the finding goes away.
- Rule shape mirrors how Python stdlib distinguishes public
attributes (re-exported via
__all__or imported into the package's__init__.py) from private internals. - The audit's "non-trivial init" heuristic is informed by the existing repo state — half the subpackages already re-export their public API today; the other half do not.
- No third-party tooling is required (mypy strict-mode would catch some of these but not all; the AST walker complements type checking).
- New cross-subpackage usages must add the symbol to the
target subpackage's
__init__.pyfirst. - The current corpus has existing findings that are tracked but not blocked — remediation happens incrementally as each import becomes the sharpest edge of a refactor.
- The
__init__.pyin newly-created subpackages stays intentional: the moment they cross the >5-line threshold, the contract activates.
mythic-vibe simulate api-audit --json
# (slice 18.4 surfaces this; today the audit is callable from
# Python: from mythic_vibe_cli.robustness.api_audit import
# audit_api_surfaces; audit_api_surfaces(Path('.'))
pytest tests/test_robustness_api_audit.py