Module Contracts
SpecFact modules integrate through a protocol-first interface and inversion-of-control loading.
Modules docs handoff: this page remains in the core docs set because it documents runtime-facing contracts and core isolation rules. Bundle-specific contract implementation guidance lives in the canonical modules docs site, currently published at
https://modules.specfact.io/.
ModuleIOContract
ModuleIOContract defines four operations:
import_to_bundle(source: Path, config: dict) -> ProjectBundleexport_from_bundle(bundle: ProjectBundle, target: Path, config: dict) -> Nonesync_with_bundle(bundle: ProjectBundle, external_source: str, config: dict) -> ProjectBundlevalidate_bundle(bundle: ProjectBundle, rules: dict) -> ValidationReport
Implementations should use runtime contracts (@icontract) and runtime type validation (@beartype).
ValidationReport
ValidationReport provides structured validation output:
status:passed | failed | warningsviolations: list of maps withseverity,message,locationsummary: counts (total_checks,passed,failed,warnings)
Inversion of Control
Core runtime must not import external bundle package namespaces directly.
- Allowed:
- core runtime ->
CommandRegistry - bundle packages ->
specfact_clishared contracts, adapters, models, utils
- core runtime ->
- Forbidden:
- core runtime ->
specfact_backlog.*,specfact_project.*,specfact_codebase.*, or other external bundle package namespaces - bundle packages reaching back into unpublished/private core internals outside supported contracts
- core runtime ->
Module discovery and loading are done through registry-driven lazy loading.
Migration and Compatibility
During the migration from hard-wired command paths:
- Official workflow bundle logic now belongs in
nold-ai/specfact-cli-modules. - Legacy files under
src/specfact_cli/commands/*.pyare shims for backward compatibility. - Only
appre-export behavior is guaranteed from shim modules. - Lean-core runtime code in
specfact-clishould depend on shared contracts and loader interfaces, not on bundle implementation modules. - Bundle packages should import stable runtime surfaces from
specfact_cliand expose their own entrypoints from package-local namespaces.
This enables module-level evolution while keeping core interfaces stable.
Example Implementation
@beartype
@require(lambda source: source.exists())
@ensure(lambda result: isinstance(result, ProjectBundle))
def import_to_bundle(source: Path, config: dict[str, Any]) -> ProjectBundle:
return ProjectBundle.load_from_directory(source)
Guidance for 3rd-Party Modules
- Declare module metadata in
module-package.yaml. - Implement as many protocol operations as your module supports.
- Declare
schema_versionwhen you depend on a specific bundle IO schema. - Keep module logic isolated from core; rely on registry entrypoints.
- Follow the same ownership split used by
specfact-cli-modules: bundle behavior lives outside the core runtime repository whenever possible.