serenecode 0.4.0__tar.gz → 0.5.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.
- {serenecode-0.4.0 → serenecode-0.5.0}/CLAUDE.md +1 -1
- {serenecode-0.4.0 → serenecode-0.5.0}/PKG-INFO +25 -10
- {serenecode-0.4.0 → serenecode-0.5.0}/README.md +24 -9
- serenecode-0.5.0/SPEC.md +290 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/docs/VERIFICATION_LEVELS.md +2 -2
- {serenecode-0.4.0 → serenecode-0.5.0}/pyproject.toml +1 -1
- serenecode-0.5.0/src/serenecode/adapters/coverage_adapter.py +718 -0
- serenecode-0.5.0/src/serenecode/adapters/coverage_suggestions.py +519 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/crosshair_adapter.py +65 -472
- serenecode-0.5.0/src/serenecode/adapters/hypothesis_adapter.py +841 -0
- serenecode-0.5.0/src/serenecode/adapters/hypothesis_strategies.py +965 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/local_fs.py +1 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/module_loader.py +2 -0
- serenecode-0.5.0/src/serenecode/checker/compositional.py +972 -0
- serenecode-0.5.0/src/serenecode/checker/compositional_integration.py +934 -0
- serenecode-0.5.0/src/serenecode/checker/compositional_parsing.py +729 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/checker/spec_traceability.py +278 -282
- serenecode-0.5.0/src/serenecode/checker/structural.py +1000 -0
- serenecode-0.5.0/src/serenecode/checker/structural_helpers.py +999 -0
- serenecode-0.5.0/src/serenecode/checker/structural_quality.py +997 -0
- serenecode-0.5.0/src/serenecode/checker/symbolic.py +165 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/cli.py +71 -447
- serenecode-0.5.0/src/serenecode/cli_helpers.py +471 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/config.py +159 -35
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/contracts/predicates.py +3 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/core/exceptions.py +2 -0
- serenecode-0.5.0/src/serenecode/core/module_health.py +527 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/core/pipeline.py +315 -473
- serenecode-0.5.0/src/serenecode/core/pipeline_helpers.py +223 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/init.py +21 -35
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/mcp/schemas.py +7 -5
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/mcp/server.py +44 -12
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/mcp/tools.py +131 -456
- serenecode-0.5.0/src/serenecode/mcp/tools_spec.py +458 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/models.py +16 -2
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/coverage_analyzer.py +2 -2
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/file_system.py +1 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/reporter.py +203 -172
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/source_discovery.py +1 -0
- serenecode-0.5.0/src/serenecode/support/crosshair_parsing.py +435 -0
- serenecode-0.5.0/src/serenecode/support/hypothesis_refinement.py +240 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/templates/content.py +52 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_cli_branches.py +38 -1
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_init_command.py +6 -5
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_adapter_internals.py +16 -16
- serenecode-0.5.0/tests/integration/test_coverage_suggestions.py +24 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_crosshair_adapter_helpers.py +2 -0
- serenecode-0.5.0/tests/integration/test_crosshair_parsing.py +19 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_hypothesis_adapter_helpers.py +8 -6
- serenecode-0.5.0/tests/integration/test_hypothesis_refinement.py +16 -0
- serenecode-0.5.0/tests/integration/test_hypothesis_strategies.py +11 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_schemas.py +1 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_server.py +4 -1
- serenecode-0.5.0/tests/integration/test_tools_spec.py +11 -0
- serenecode-0.5.0/tests/unit/checker/test_compositional_integration.py +11 -0
- serenecode-0.5.0/tests/unit/checker/test_compositional_parsing.py +10 -0
- serenecode-0.5.0/tests/unit/checker/test_module_health.py +323 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_structural.py +16 -12
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_structural_helpers.py +10 -6
- serenecode-0.5.0/tests/unit/checker/test_structural_quality.py +23 -0
- serenecode-0.5.0/tests/unit/contracts/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/contracts/test_predicates_hypothesis.py +1 -1
- serenecode-0.5.0/tests/unit/mcp/__init__.py +0 -0
- serenecode-0.5.0/tests/unit/mcp/test_tool_module_health.py +89 -0
- serenecode-0.5.0/tests/unit/test_cli_helpers.py +27 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_config.py +30 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_models.py +39 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_pipeline.py +11 -7
- serenecode-0.5.0/tests/unit/test_pipeline_helpers.py +20 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_reporter.py +2 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_templates_content.py +6 -0
- serenecode-0.4.0/AGENTS.md +0 -76
- serenecode-0.4.0/FEATURE_SPEC.md +0 -222
- serenecode-0.4.0/IMPLEMENTATION_PLAN.md +0 -395
- serenecode-0.4.0/SERENECODE.md +0 -719
- serenecode-0.4.0/src/serenecode/adapters/coverage_adapter.py +0 -1188
- serenecode-0.4.0/src/serenecode/adapters/hypothesis_adapter.py +0 -2023
- serenecode-0.4.0/src/serenecode/checker/compositional.py +0 -2559
- serenecode-0.4.0/src/serenecode/checker/structural.py +0 -2909
- serenecode-0.4.0/src/serenecode/checker/symbolic.py +0 -178
- {serenecode-0.4.0 → serenecode-0.5.0}/.gitignore +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/LICENSE +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/docs/SECURITY.md +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/DOSAGE_CALC_SPEC.md +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-regular/dosage_calc.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-regular/test_dosage_calc.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/CLAUDE.md +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/SERENECODE.md +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/SPEC.md +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/pyproject.toml +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/src/dosage/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/src/dosage/core/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/src/dosage/core/dosage.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/src/dosage/core/models.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/src/dosage/core/safety.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/tests/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/tests/unit/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/tests/unit/test_dosage.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/tests/unit/test_models.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/tests/unit/test_safety.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/examples/dosage-serenecode/uv.lock +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/serenecode.jpg +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/mypy_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/unavailable_dead_code_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/adapters/vulture_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/checker/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/checker/coverage.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/checker/properties.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/checker/types.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/contracts/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/core/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/mcp/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/mcp/resources.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/dead_code_analyzer.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/property_tester.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/symbolic_checker.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/ports/type_checker.py +0 -0
- {serenecode-0.4.0/tests → serenecode-0.5.0/src/serenecode/support}/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/src/serenecode/templates/__init__.py +0 -0
- {serenecode-0.4.0/tests/e2e → serenecode-0.5.0/tests}/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/conftest.py +0 -0
- {serenecode-0.4.0/tests/integration → serenecode-0.5.0/tests/e2e}/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_check_command.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_cli.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_init.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_mcp_command.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_report_command.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/e2e/test_status_command.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/edge_cases/aliased_import.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/edge_cases/async_functions.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/edge_cases/empty_module.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/edge_cases/from_import.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/invalid/broken_postcondition.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/invalid/io_in_core.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/invalid/missing_contracts.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/invalid/missing_invariant.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/invalid/missing_types.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/valid/class_with_invariant.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/valid/full_module.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/fixtures/valid/simple_function.py +0 -0
- {serenecode-0.4.0/tests/unit → serenecode-0.5.0/tests/integration}/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_checkers_real_code.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_coverage_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_crosshair_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_example_projects.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_file_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_hypothesis_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_local_fs.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_module_loader.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_mypy_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_resources.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_tools.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_unavailable_dead_code_adapter.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/integration/test_vulture_adapter.py +0 -0
- {serenecode-0.4.0/tests/unit/checker → serenecode-0.5.0/tests/unit}/__init__.py +0 -0
- {serenecode-0.4.0/tests/unit/contracts → serenecode-0.5.0/tests/unit/checker}/__init__.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_compositional.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_compositional_helpers.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_coverage.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_properties.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_spec_traceability.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_structural_hypothesis.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_symbolic.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/checker/test_types.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/contracts/test_predicates.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_api.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_models_hypothesis.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/tests/unit/test_source_discovery.py +0 -0
- {serenecode-0.4.0 → serenecode-0.5.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Serenecode
|
|
2
2
|
|
|
3
|
-
All code in this project MUST follow the standards
|
|
3
|
+
All code in this project MUST follow the same standards SereneCode ships to users: the embedded templates in `src/serenecode/templates/content.py` (default / strict / minimal) define the conventions the structural checker enforces. Read the relevant template before writing or modifying any code. Every public function must have icontract preconditions and postconditions. Every class with state must have invariants. Follow the architectural patterns specified there.
|
|
4
4
|
|
|
5
5
|
Pre-existing `*_SPEC.md` or PRD files are narrative inputs; only project-root `SPEC.md` with REQ/INT identifiers satisfies SereneCode traceability (`serenecode check --spec`).
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: serenecode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Verification framework for AI-generated Python — test coverage, property testing, and symbolic execution
|
|
5
5
|
Project-URL: Homepage, https://github.com/helgster77/serenecode
|
|
6
6
|
Project-URL: Repository, https://github.com/helgster77/serenecode
|
|
@@ -51,7 +51,7 @@ SereneCode turns the question from "did the model ship code?" to "does it match
|
|
|
51
51
|
|
|
52
52
|
**MCP:** register the server once (Claude Code, Cursor, Cline, Continue, or any MCP client) and the agent can pull structured JSON—findings, suggestions, counterexamples—while the cursor is still on the line, instead of discovering issues only after merge.
|
|
53
53
|
|
|
54
|
-
> **This framework was bootstrapped with AI under its own rules.**
|
|
54
|
+
> **This framework was bootstrapped with AI under its own rules.** Convention templates in `src/serenecode/templates/content.py` were defined before the first line of code, and the codebase has been developed under those conventions from the start — including the MCP server, which the same AI agents now use to verify their own work mid-edit. The current tree passes its own `serenecode check src --level 6 --allow-code-execution` end-to-end via the bare CLI (counts vary with the tree; expect hundreds of functions with a mix of passed, exempt, and advisory notes; wall time on the order of minutes for a full L1–L6 run), an internal strict-config Level 6 self-check in the test suite (`pytest tests/integration/test_example_projects.py::test_serenecode_repo_passes_strict_level_6`, which exercises L4–L6 against `strict_config` over the full source tree), `mypy src examples/dosage-serenecode/src`, the shipped dosage example's own `serenecode check src --level 6 --allow-code-execution`, and the full `pytest` suite (on the order of 1,450+ passing tests; a small number are skipped by design). The verification output is transparent about scope: exempt modules (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types) are reported as "exempt" rather than silently omitted; dead-code and module health findings are **advisories** and appear in the summary unless you use `--fail-on-advisory` to fail CI when any remain.
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -85,6 +85,8 @@ After enough hours pair-programming with coding agents, the same handful of mist
|
|
|
85
85
|
|
|
86
86
|
- **Tests that pass but verify nothing.** A `def test_foo(): foo()` with no `assert` runs successfully and counts as "covered" — but it only checks that the function doesn't raise. L1's no-assertions-in-tests check fires on any `test_*` function with no `assert`, `pytest.raises`, `pytest.fail`, or `self.assertX` call.
|
|
87
87
|
|
|
88
|
+
- **Module bloat and god classes.** Agents accumulate code in a single module without splitting. A 2000-line file with 30 methods on one class compiles and tests pass, but it's unmaintainable. Module health checks flag files, functions, and classes that exceed configurable thresholds — advisory warnings when they're getting large, hard errors when they exceed the maximum. The agent gets concrete split suggestions derived from AST analysis: which classes could be standalone modules, which function groups share a prefix, and where banner comments suggest logical boundaries.
|
|
89
|
+
|
|
88
90
|
- **Architectural drift.** Asked to "add a feature," the agent puts I/O in core, business logic in adapters, and circular imports between them. The system still works in tests because everything is loaded together — but the layering rule that made the code testable in the first place is gone. L6 compositional checks enforce dependency direction, interface compliance, and contract presence at module boundaries.
|
|
89
91
|
|
|
90
92
|
None of these failures are unique to AI; humans make them too. What's unique is the *rate* at which an agent produces them and the *confidence* with which the agent reports the work as done. The structural checker, the contracts, the property tester, and the symbolic search exist to make each pattern impossible to ship without an explicit, reviewed override.
|
|
@@ -162,7 +164,7 @@ This creates SERENECODE.md (project conventions including spec traceability) and
|
|
|
162
164
|
|
|
163
165
|
A lightweight AST-based checker that validates code follows SERENECODE.md conventions in seconds. Missing a postcondition? No class invariant? No test file for a module? Caught before you waste time on heavy verification.
|
|
164
166
|
|
|
165
|
-
L1 also catches AI-failure-mode patterns that compile and look correct but represent real bugs: stub residue (`pass`/`...`/`raise NotImplementedError` left as a function body), mutable default arguments, bare `assert` in non-test source, `print()` in core, dangerous calls (`eval`, `exec`, `pickle.loads`, `os.system`, `subprocess` with `shell=True`), `TODO`/`FIXME`/`XXX`/`HACK` markers in tracked files, tests with no assertions, silent exception handlers, tautological postconditions, and likely dead code.
|
|
167
|
+
L1 also catches AI-failure-mode patterns that compile and look correct but represent real bugs: stub residue (`pass`/`...`/`raise NotImplementedError` left as a function body), mutable default arguments, bare `assert` in non-test source, `print()` in core, dangerous calls (`eval`, `exec`, `pickle.loads`, `os.system`, `subprocess` with `shell=True`), `TODO`/`FIXME`/`XXX`/`HACK` markers in tracked files, tests with no assertions, silent exception handlers, tautological postconditions, and likely dead code. L1 additionally runs **module health checks**: files exceeding a configurable line threshold, functions that are too long, functions with too many parameters, and classes with too many methods generate warnings (advisory) or errors depending on severity. These target the common AI agent drift pattern of accumulating code in a single module without splitting. Dead-code and module health findings are advisory review items; each rule has a per-rule opt-out comment for legitimate exceptions. See SERENECODE.md "Code Quality Standards" and "Module Health" for the full list. Skip all module health checks with `--skip-module-health`.
|
|
166
168
|
|
|
167
169
|
```bash
|
|
168
170
|
serenecode check src/ --structural # structural conventions + dead-code review
|
|
@@ -228,8 +230,9 @@ The same `serenecode mcp` stdio server works in Claude Code, Cursor, Cline, Cont
|
|
|
228
230
|
| `serenecode_integration_status` | Implementation/verification status of one INT |
|
|
229
231
|
| `serenecode_orphans` | REQs with no implementation or no test |
|
|
230
232
|
| `serenecode_dead_code` | Likely dead-code findings that require user review |
|
|
233
|
+
| `serenecode_module_health` | Module health metrics (file size, function lengths, parameter counts, class sizes) for a single file — no verification needed |
|
|
231
234
|
|
|
232
|
-
Tool results mirror the CLI pipeline: structured payloads include **`passed`**, levels, **`verdict`**, and a **summary** with counts (including **`advisory_count`** for dead
|
|
235
|
+
Tool results mirror the CLI pipeline: structured payloads include **`passed`**, levels, **`verdict`**, and a **summary** with counts (including **`advisory_count`** for advisory findings like dead code and module health warnings). Paths and `project_root` are resolved on the host — they are not sandboxed to one workspace; see [docs/SECURITY.md](docs/SECURITY.md).
|
|
233
236
|
|
|
234
237
|
**Read-only resources** the agent can fetch without "calling" anything: `serenecode://config` (active SerenecodeConfig as JSON), `serenecode://findings/last-run` (most recent CheckResponse from this server session), `serenecode://exempt-modules` (the exempt path patterns for the active config), `serenecode://reqs` (parsed REQ-xxx list from the project's SPEC.md), and `serenecode://integrations` (parsed INT-xxx metadata from the project's SPEC.md).
|
|
235
238
|
|
|
@@ -310,13 +313,13 @@ The library API (`serenecode.check`) and the MCP server (`serenecode_check`, `se
|
|
|
310
313
|
|
|
311
314
|
SereneCode isn't just a tool that *tells* you to write verified code. It *is* verified code.
|
|
312
315
|
|
|
313
|
-
|
|
316
|
+
Those convention templates were the first artifacts — before any Python was written. The framework has been developed under those conventions with AI as a first-class contributor, and the repository continuously checks itself with:
|
|
314
317
|
|
|
315
|
-
- `pytest` across the full suite (on the order of 1,
|
|
318
|
+
- `pytest` across the full suite (on the order of 1,450+ passing tests; a small number skipped by design)
|
|
316
319
|
- `mypy --strict` across `src/` and `examples/dosage-serenecode/src/`
|
|
317
320
|
- SereneCode's own structural, type, property, symbolic, and compositional passes
|
|
318
321
|
|
|
319
|
-
On the current tree, `serenecode check src --level 6 --allow-code-execution` runs the full L1–L6 pipeline against the framework's own source; exact counts of passed / exempt / advisory rows change as the tree grows (wall time often several minutes for a deep run). A separate integration test, `test_serenecode_repo_passes_strict_level_6`, runs the same `src/` tree through `run_pipeline` with `strict_config()` and `start_level=4`, which strips path-based exemptions and forces adapters, CLI, MCP, and similar code through L4–L6. The exempt items in a **default-config** run still include adapter modules, port `Protocol`s, CLI/MCP composition roots, and functions whose parameter types are poor fits for Hypothesis/CrossHair. Exempt and advisory rows stay visible in the output — they are not silently omitted.
|
|
322
|
+
On the current tree, `serenecode check src --level 6 --allow-code-execution` runs the full L1–L6 pipeline against the framework's own source; exact counts of passed / exempt / advisory rows change as the tree grows (wall time often several minutes for a deep run). A separate integration test, `test_serenecode_repo_passes_strict_level_6`, runs the same `src/` tree through `run_pipeline` with `strict_config()` and `start_level=4`, which strips path-based exemptions and forces adapters, CLI, MCP, and similar code through L4–L6. The exempt items in a **default-config** run still include adapter modules, port `Protocol`s, CLI/MCP composition roots, and functions whose parameter types are poor fits for Hypothesis/CrossHair. Advisory items include dead-code findings and module health warnings (file length, function length, parameter count, class method count). Exempt and advisory rows stay visible in the output — they are not silently omitted.
|
|
320
323
|
|
|
321
324
|
At Level 5, CrossHair and Z3 search for counterexamples across the codebase's symbolic-friendly contracted top-level functions. Functions with non-primitive parameters (custom dataclasses, Protocol implementations, Callable types) are reported as exempt because the solver cannot generate inputs for them. Level 6 adds structural compositional analysis: dependency direction, circular dependency detection, interface compliance, contract presence at module boundaries, aliased cross-module call resolution, and architectural invariants. Interface compliance follows explicit `Protocol` inheritance and checks substitutability, including extra required parameters and incompatible return annotations. Together, they provide both deep per-function verification and system-level structural guarantees — but the structural checks at L6 verify contract *presence*, not logical *sufficiency* across call chains.
|
|
322
325
|
|
|
@@ -365,7 +368,8 @@ serenecode check [<path>] [--level 1-6] [--allow-code-execution] # run ve
|
|
|
365
368
|
[--project-root DIR] # repo root for imports + config
|
|
366
369
|
[--format human|json] # output format
|
|
367
370
|
[--structural] [--verify] # L1 only / L3-6 only
|
|
368
|
-
[--
|
|
371
|
+
[--skip-module-health] # skip file/function/param/class size checks
|
|
372
|
+
[--fail-on-advisory] # exit 11 if advisories remain
|
|
369
373
|
[--per-condition-timeout N] # L5 CrossHair budgets
|
|
370
374
|
[--per-path-timeout N] [--module-timeout N] # (defaults: 30/10/300s)
|
|
371
375
|
[--coverage-timeout N] # L3 pytest/coverage subprocess (default 600s)
|
|
@@ -379,7 +383,7 @@ serenecode mcp [--allow-code-execution] # boot t
|
|
|
379
383
|
|
|
380
384
|
**Environment (optional):** `SERENECODE_MAX_WORKERS` overrides `--workers`; `SERENECODE_COVERAGE_TIMEOUT` overrides `--coverage-timeout`. **`SERENECODE_DEBUG=1`** logs subprocess environment **key names** (not values) when tools spawn mypy, pytest, CrossHair, etc. Details: [docs/SECURITY.md](docs/SECURITY.md).
|
|
381
385
|
|
|
382
|
-
**Exit codes:** 0 = passed (and no `--fail-on-advisory` violation), 1–6 = first failing verification level (structural … compositional), 10 = internal error or deep verification refused without `--allow-code-execution`, **11 =
|
|
386
|
+
**Exit codes:** 0 = passed (and no `--fail-on-advisory` violation), 1–6 = first failing verification level (structural … compositional), 10 = internal error or deep verification refused without `--allow-code-execution`, **11 = advisories remain with `--fail-on-advisory`** (dead code, module health warnings; checks otherwise passed).
|
|
383
387
|
|
|
384
388
|
---
|
|
385
389
|
|
|
@@ -391,7 +395,7 @@ SereneCode is honest about what it can and can't do:
|
|
|
391
395
|
|
|
392
396
|
**Contracts are only as good as you write them.** A function with weak postconditions will pass verification even if the implementation is subtly wrong. SereneCode checks that contracts exist and hold, but can't check that they fully capture your intent. Tautological contracts like `lambda self: True` are now flagged by the conventions and should not be used — they provide no verification value.
|
|
393
397
|
|
|
394
|
-
**Exempt items are visible, not hidden.** Modules exempt from structural checking (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types, adapter code) are reported as "exempt" in the output rather than being silently omitted. This makes the verification scope transparent: the tool reports passed, failed, skipped, and
|
|
398
|
+
**Exempt items are visible, not hidden.** Modules exempt from structural checking (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types, adapter code) are reported as "exempt" in the output rather than being silently omitted. Advisory items (dead code, module health warnings) are also visible and counted separately. This makes the verification scope transparent: the tool reports passed, failed, skipped, exempt, and advisory counts separately so you can see exactly what was and wasn't deeply verified.
|
|
395
399
|
|
|
396
400
|
**Runtime checks can be disabled.** icontract decorators are checked on every call by default, but can be disabled via environment variables for performance in production. This is a feature, not a bug — but it means runtime guarantees depend on configuration.
|
|
397
401
|
|
|
@@ -419,6 +423,7 @@ CLI / Library API / MCP ← composition roots (interactive init, spec valida
|
|
|
419
423
|
│ ├──▸ Structural Checker (ast)
|
|
420
424
|
│ ├──▸ Spec Traceability (REQ-xxx / INT-xxx → Implements/Verifies)
|
|
421
425
|
│ ├──▸ Dead-Code Review (likely unused code → ask before removal)
|
|
426
|
+
│ ├──▸ Module Health (file/function/class size → advisory or error)
|
|
422
427
|
│ ├──▸ Test Existence (test_<module>.py discovery)
|
|
423
428
|
│ ├──▸ Type Checker (mypy)
|
|
424
429
|
│ ├──▸ Coverage Analyzer (coverage.py)
|
|
@@ -433,6 +438,16 @@ CLI / Library API / MCP ← composition roots (interactive init, spec valida
|
|
|
433
438
|
|
|
434
439
|
Core logic is pure. All I/O goes through Protocol-defined ports. The verification engine itself is verifiable.
|
|
435
440
|
|
|
441
|
+
## Comparison: AWS Kiro and Spec Kit / Speckit
|
|
442
|
+
|
|
443
|
+
SereneCode overlaps with these tools on **spec-first, AI-assisted development**—all three push against unstructured “vibe coding.” The difference is **what each product optimizes for**.
|
|
444
|
+
|
|
445
|
+
**AWS Kiro** is an agentic AI IDE from AWS (Bedrock-powered), with spec-driven flows, autonomous agents, “powers,” and multi-repo work inside that environment. SereneCode is **not an IDE**: it is a **Python verification toolkit** (CLI + MCP) that plugs into editors you already use. Kiro optimizes **how you build in their stack**; SereneCode optimizes **repeatable assurance on Python code**—traceability from root `SPEC.md` IDs to implementation and tests, `icontract` contracts, mypy, coverage, Hypothesis, bounded CrossHair search, and compositional checks. The two are **complementary** if you use Kiro to author code but still want repo-local, machine-checkable verification on a Python tree.
|
|
446
|
+
|
|
447
|
+
**Spec Kit** (GitHub’s methodology) and ecosystem tools such as **Speckit** focus on **structured specification and phased delivery**—for example constitution, specify, plan, tasks, and implementation—with agents and templates driving the workflow. “Executable” there often means **process and artifact structure** that steers implementation. SereneCode adds a different meaning of executable: **contracts and checkers that run against your Python**, plus optional solvers and property tests, with explicit **REQ-xxx / INT-xxx** traceability to `Implements:` / `Verifies:` in code and tests. Spec Kit is typically **stack-agnostic**; SereneCode is **Python-specific** by design.
|
|
448
|
+
|
|
449
|
+
If you already use Spec Kit–style phases, SereneCode fits **after** the spec is stable enough to land as root `SPEC.md`—as the **verification layer** that answers whether the code and tests actually match what you declared.
|
|
450
|
+
|
|
436
451
|
## Security and trust
|
|
437
452
|
|
|
438
453
|
Serenecode runs on **your machine** and, with `--allow-code-execution` (CLI or MCP), **imports and executes** project code—similar trust to running `pytest` or `python -m` on that tree. It is **not** a sandbox. Subprocesses receive a filtered environment to limit credential leakage; see [docs/SECURITY.md](docs/SECURITY.md) for the threat model, MCP behavior, `SERENECODE_DEBUG`, and CI exit code **11** with `--fail-on-advisory`.
|
|
@@ -15,7 +15,7 @@ SereneCode turns the question from "did the model ship code?" to "does it match
|
|
|
15
15
|
|
|
16
16
|
**MCP:** register the server once (Claude Code, Cursor, Cline, Continue, or any MCP client) and the agent can pull structured JSON—findings, suggestions, counterexamples—while the cursor is still on the line, instead of discovering issues only after merge.
|
|
17
17
|
|
|
18
|
-
> **This framework was bootstrapped with AI under its own rules.**
|
|
18
|
+
> **This framework was bootstrapped with AI under its own rules.** Convention templates in `src/serenecode/templates/content.py` were defined before the first line of code, and the codebase has been developed under those conventions from the start — including the MCP server, which the same AI agents now use to verify their own work mid-edit. The current tree passes its own `serenecode check src --level 6 --allow-code-execution` end-to-end via the bare CLI (counts vary with the tree; expect hundreds of functions with a mix of passed, exempt, and advisory notes; wall time on the order of minutes for a full L1–L6 run), an internal strict-config Level 6 self-check in the test suite (`pytest tests/integration/test_example_projects.py::test_serenecode_repo_passes_strict_level_6`, which exercises L4–L6 against `strict_config` over the full source tree), `mypy src examples/dosage-serenecode/src`, the shipped dosage example's own `serenecode check src --level 6 --allow-code-execution`, and the full `pytest` suite (on the order of 1,450+ passing tests; a small number are skipped by design). The verification output is transparent about scope: exempt modules (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types) are reported as "exempt" rather than silently omitted; dead-code and module health findings are **advisories** and appear in the summary unless you use `--fail-on-advisory` to fail CI when any remain.
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -49,6 +49,8 @@ After enough hours pair-programming with coding agents, the same handful of mist
|
|
|
49
49
|
|
|
50
50
|
- **Tests that pass but verify nothing.** A `def test_foo(): foo()` with no `assert` runs successfully and counts as "covered" — but it only checks that the function doesn't raise. L1's no-assertions-in-tests check fires on any `test_*` function with no `assert`, `pytest.raises`, `pytest.fail`, or `self.assertX` call.
|
|
51
51
|
|
|
52
|
+
- **Module bloat and god classes.** Agents accumulate code in a single module without splitting. A 2000-line file with 30 methods on one class compiles and tests pass, but it's unmaintainable. Module health checks flag files, functions, and classes that exceed configurable thresholds — advisory warnings when they're getting large, hard errors when they exceed the maximum. The agent gets concrete split suggestions derived from AST analysis: which classes could be standalone modules, which function groups share a prefix, and where banner comments suggest logical boundaries.
|
|
53
|
+
|
|
52
54
|
- **Architectural drift.** Asked to "add a feature," the agent puts I/O in core, business logic in adapters, and circular imports between them. The system still works in tests because everything is loaded together — but the layering rule that made the code testable in the first place is gone. L6 compositional checks enforce dependency direction, interface compliance, and contract presence at module boundaries.
|
|
53
55
|
|
|
54
56
|
None of these failures are unique to AI; humans make them too. What's unique is the *rate* at which an agent produces them and the *confidence* with which the agent reports the work as done. The structural checker, the contracts, the property tester, and the symbolic search exist to make each pattern impossible to ship without an explicit, reviewed override.
|
|
@@ -126,7 +128,7 @@ This creates SERENECODE.md (project conventions including spec traceability) and
|
|
|
126
128
|
|
|
127
129
|
A lightweight AST-based checker that validates code follows SERENECODE.md conventions in seconds. Missing a postcondition? No class invariant? No test file for a module? Caught before you waste time on heavy verification.
|
|
128
130
|
|
|
129
|
-
L1 also catches AI-failure-mode patterns that compile and look correct but represent real bugs: stub residue (`pass`/`...`/`raise NotImplementedError` left as a function body), mutable default arguments, bare `assert` in non-test source, `print()` in core, dangerous calls (`eval`, `exec`, `pickle.loads`, `os.system`, `subprocess` with `shell=True`), `TODO`/`FIXME`/`XXX`/`HACK` markers in tracked files, tests with no assertions, silent exception handlers, tautological postconditions, and likely dead code.
|
|
131
|
+
L1 also catches AI-failure-mode patterns that compile and look correct but represent real bugs: stub residue (`pass`/`...`/`raise NotImplementedError` left as a function body), mutable default arguments, bare `assert` in non-test source, `print()` in core, dangerous calls (`eval`, `exec`, `pickle.loads`, `os.system`, `subprocess` with `shell=True`), `TODO`/`FIXME`/`XXX`/`HACK` markers in tracked files, tests with no assertions, silent exception handlers, tautological postconditions, and likely dead code. L1 additionally runs **module health checks**: files exceeding a configurable line threshold, functions that are too long, functions with too many parameters, and classes with too many methods generate warnings (advisory) or errors depending on severity. These target the common AI agent drift pattern of accumulating code in a single module without splitting. Dead-code and module health findings are advisory review items; each rule has a per-rule opt-out comment for legitimate exceptions. See SERENECODE.md "Code Quality Standards" and "Module Health" for the full list. Skip all module health checks with `--skip-module-health`.
|
|
130
132
|
|
|
131
133
|
```bash
|
|
132
134
|
serenecode check src/ --structural # structural conventions + dead-code review
|
|
@@ -192,8 +194,9 @@ The same `serenecode mcp` stdio server works in Claude Code, Cursor, Cline, Cont
|
|
|
192
194
|
| `serenecode_integration_status` | Implementation/verification status of one INT |
|
|
193
195
|
| `serenecode_orphans` | REQs with no implementation or no test |
|
|
194
196
|
| `serenecode_dead_code` | Likely dead-code findings that require user review |
|
|
197
|
+
| `serenecode_module_health` | Module health metrics (file size, function lengths, parameter counts, class sizes) for a single file — no verification needed |
|
|
195
198
|
|
|
196
|
-
Tool results mirror the CLI pipeline: structured payloads include **`passed`**, levels, **`verdict`**, and a **summary** with counts (including **`advisory_count`** for dead
|
|
199
|
+
Tool results mirror the CLI pipeline: structured payloads include **`passed`**, levels, **`verdict`**, and a **summary** with counts (including **`advisory_count`** for advisory findings like dead code and module health warnings). Paths and `project_root` are resolved on the host — they are not sandboxed to one workspace; see [docs/SECURITY.md](docs/SECURITY.md).
|
|
197
200
|
|
|
198
201
|
**Read-only resources** the agent can fetch without "calling" anything: `serenecode://config` (active SerenecodeConfig as JSON), `serenecode://findings/last-run` (most recent CheckResponse from this server session), `serenecode://exempt-modules` (the exempt path patterns for the active config), `serenecode://reqs` (parsed REQ-xxx list from the project's SPEC.md), and `serenecode://integrations` (parsed INT-xxx metadata from the project's SPEC.md).
|
|
199
202
|
|
|
@@ -274,13 +277,13 @@ The library API (`serenecode.check`) and the MCP server (`serenecode_check`, `se
|
|
|
274
277
|
|
|
275
278
|
SereneCode isn't just a tool that *tells* you to write verified code. It *is* verified code.
|
|
276
279
|
|
|
277
|
-
|
|
280
|
+
Those convention templates were the first artifacts — before any Python was written. The framework has been developed under those conventions with AI as a first-class contributor, and the repository continuously checks itself with:
|
|
278
281
|
|
|
279
|
-
- `pytest` across the full suite (on the order of 1,
|
|
282
|
+
- `pytest` across the full suite (on the order of 1,450+ passing tests; a small number skipped by design)
|
|
280
283
|
- `mypy --strict` across `src/` and `examples/dosage-serenecode/src/`
|
|
281
284
|
- SereneCode's own structural, type, property, symbolic, and compositional passes
|
|
282
285
|
|
|
283
|
-
On the current tree, `serenecode check src --level 6 --allow-code-execution` runs the full L1–L6 pipeline against the framework's own source; exact counts of passed / exempt / advisory rows change as the tree grows (wall time often several minutes for a deep run). A separate integration test, `test_serenecode_repo_passes_strict_level_6`, runs the same `src/` tree through `run_pipeline` with `strict_config()` and `start_level=4`, which strips path-based exemptions and forces adapters, CLI, MCP, and similar code through L4–L6. The exempt items in a **default-config** run still include adapter modules, port `Protocol`s, CLI/MCP composition roots, and functions whose parameter types are poor fits for Hypothesis/CrossHair. Exempt and advisory rows stay visible in the output — they are not silently omitted.
|
|
286
|
+
On the current tree, `serenecode check src --level 6 --allow-code-execution` runs the full L1–L6 pipeline against the framework's own source; exact counts of passed / exempt / advisory rows change as the tree grows (wall time often several minutes for a deep run). A separate integration test, `test_serenecode_repo_passes_strict_level_6`, runs the same `src/` tree through `run_pipeline` with `strict_config()` and `start_level=4`, which strips path-based exemptions and forces adapters, CLI, MCP, and similar code through L4–L6. The exempt items in a **default-config** run still include adapter modules, port `Protocol`s, CLI/MCP composition roots, and functions whose parameter types are poor fits for Hypothesis/CrossHair. Advisory items include dead-code findings and module health warnings (file length, function length, parameter count, class method count). Exempt and advisory rows stay visible in the output — they are not silently omitted.
|
|
284
287
|
|
|
285
288
|
At Level 5, CrossHair and Z3 search for counterexamples across the codebase's symbolic-friendly contracted top-level functions. Functions with non-primitive parameters (custom dataclasses, Protocol implementations, Callable types) are reported as exempt because the solver cannot generate inputs for them. Level 6 adds structural compositional analysis: dependency direction, circular dependency detection, interface compliance, contract presence at module boundaries, aliased cross-module call resolution, and architectural invariants. Interface compliance follows explicit `Protocol` inheritance and checks substitutability, including extra required parameters and incompatible return annotations. Together, they provide both deep per-function verification and system-level structural guarantees — but the structural checks at L6 verify contract *presence*, not logical *sufficiency* across call chains.
|
|
286
289
|
|
|
@@ -329,7 +332,8 @@ serenecode check [<path>] [--level 1-6] [--allow-code-execution] # run ve
|
|
|
329
332
|
[--project-root DIR] # repo root for imports + config
|
|
330
333
|
[--format human|json] # output format
|
|
331
334
|
[--structural] [--verify] # L1 only / L3-6 only
|
|
332
|
-
[--
|
|
335
|
+
[--skip-module-health] # skip file/function/param/class size checks
|
|
336
|
+
[--fail-on-advisory] # exit 11 if advisories remain
|
|
333
337
|
[--per-condition-timeout N] # L5 CrossHair budgets
|
|
334
338
|
[--per-path-timeout N] [--module-timeout N] # (defaults: 30/10/300s)
|
|
335
339
|
[--coverage-timeout N] # L3 pytest/coverage subprocess (default 600s)
|
|
@@ -343,7 +347,7 @@ serenecode mcp [--allow-code-execution] # boot t
|
|
|
343
347
|
|
|
344
348
|
**Environment (optional):** `SERENECODE_MAX_WORKERS` overrides `--workers`; `SERENECODE_COVERAGE_TIMEOUT` overrides `--coverage-timeout`. **`SERENECODE_DEBUG=1`** logs subprocess environment **key names** (not values) when tools spawn mypy, pytest, CrossHair, etc. Details: [docs/SECURITY.md](docs/SECURITY.md).
|
|
345
349
|
|
|
346
|
-
**Exit codes:** 0 = passed (and no `--fail-on-advisory` violation), 1–6 = first failing verification level (structural … compositional), 10 = internal error or deep verification refused without `--allow-code-execution`, **11 =
|
|
350
|
+
**Exit codes:** 0 = passed (and no `--fail-on-advisory` violation), 1–6 = first failing verification level (structural … compositional), 10 = internal error or deep verification refused without `--allow-code-execution`, **11 = advisories remain with `--fail-on-advisory`** (dead code, module health warnings; checks otherwise passed).
|
|
347
351
|
|
|
348
352
|
---
|
|
349
353
|
|
|
@@ -355,7 +359,7 @@ SereneCode is honest about what it can and can't do:
|
|
|
355
359
|
|
|
356
360
|
**Contracts are only as good as you write them.** A function with weak postconditions will pass verification even if the implementation is subtly wrong. SereneCode checks that contracts exist and hold, but can't check that they fully capture your intent. Tautological contracts like `lambda self: True` are now flagged by the conventions and should not be used — they provide no verification value.
|
|
357
361
|
|
|
358
|
-
**Exempt items are visible, not hidden.** Modules exempt from structural checking (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types, adapter code) are reported as "exempt" in the output rather than being silently omitted. This makes the verification scope transparent: the tool reports passed, failed, skipped, and
|
|
362
|
+
**Exempt items are visible, not hidden.** Modules exempt from structural checking (adapters, CLI, ports, MCP server, `__init__.py`) and functions excluded from deep verification (non-primitive parameter types, adapter code) are reported as "exempt" in the output rather than being silently omitted. Advisory items (dead code, module health warnings) are also visible and counted separately. This makes the verification scope transparent: the tool reports passed, failed, skipped, exempt, and advisory counts separately so you can see exactly what was and wasn't deeply verified.
|
|
359
363
|
|
|
360
364
|
**Runtime checks can be disabled.** icontract decorators are checked on every call by default, but can be disabled via environment variables for performance in production. This is a feature, not a bug — but it means runtime guarantees depend on configuration.
|
|
361
365
|
|
|
@@ -383,6 +387,7 @@ CLI / Library API / MCP ← composition roots (interactive init, spec valida
|
|
|
383
387
|
│ ├──▸ Structural Checker (ast)
|
|
384
388
|
│ ├──▸ Spec Traceability (REQ-xxx / INT-xxx → Implements/Verifies)
|
|
385
389
|
│ ├──▸ Dead-Code Review (likely unused code → ask before removal)
|
|
390
|
+
│ ├──▸ Module Health (file/function/class size → advisory or error)
|
|
386
391
|
│ ├──▸ Test Existence (test_<module>.py discovery)
|
|
387
392
|
│ ├──▸ Type Checker (mypy)
|
|
388
393
|
│ ├──▸ Coverage Analyzer (coverage.py)
|
|
@@ -397,6 +402,16 @@ CLI / Library API / MCP ← composition roots (interactive init, spec valida
|
|
|
397
402
|
|
|
398
403
|
Core logic is pure. All I/O goes through Protocol-defined ports. The verification engine itself is verifiable.
|
|
399
404
|
|
|
405
|
+
## Comparison: AWS Kiro and Spec Kit / Speckit
|
|
406
|
+
|
|
407
|
+
SereneCode overlaps with these tools on **spec-first, AI-assisted development**—all three push against unstructured “vibe coding.” The difference is **what each product optimizes for**.
|
|
408
|
+
|
|
409
|
+
**AWS Kiro** is an agentic AI IDE from AWS (Bedrock-powered), with spec-driven flows, autonomous agents, “powers,” and multi-repo work inside that environment. SereneCode is **not an IDE**: it is a **Python verification toolkit** (CLI + MCP) that plugs into editors you already use. Kiro optimizes **how you build in their stack**; SereneCode optimizes **repeatable assurance on Python code**—traceability from root `SPEC.md` IDs to implementation and tests, `icontract` contracts, mypy, coverage, Hypothesis, bounded CrossHair search, and compositional checks. The two are **complementary** if you use Kiro to author code but still want repo-local, machine-checkable verification on a Python tree.
|
|
410
|
+
|
|
411
|
+
**Spec Kit** (GitHub’s methodology) and ecosystem tools such as **Speckit** focus on **structured specification and phased delivery**—for example constitution, specify, plan, tasks, and implementation—with agents and templates driving the workflow. “Executable” there often means **process and artifact structure** that steers implementation. SereneCode adds a different meaning of executable: **contracts and checkers that run against your Python**, plus optional solvers and property tests, with explicit **REQ-xxx / INT-xxx** traceability to `Implements:` / `Verifies:` in code and tests. Spec Kit is typically **stack-agnostic**; SereneCode is **Python-specific** by design.
|
|
412
|
+
|
|
413
|
+
If you already use Spec Kit–style phases, SereneCode fits **after** the spec is stable enough to land as root `SPEC.md`—as the **verification layer** that answers whether the code and tests actually match what you declared.
|
|
414
|
+
|
|
400
415
|
## Security and trust
|
|
401
416
|
|
|
402
417
|
Serenecode runs on **your machine** and, with `--allow-code-execution` (CLI or MCP), **imports and executes** project code—similar trust to running `pytest` or `python -m` on that tree. It is **not** a sandbox. Subprocesses receive a filtered environment to limit credential leakage; see [docs/SECURITY.md](docs/SECURITY.md) for the threat model, MCP behavior, `SERENECODE_DEBUG`, and CI exit code **11** with `--fail-on-advisory`.
|
serenecode-0.5.0/SPEC.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Module Health Checks — Specification
|
|
2
|
+
|
|
3
|
+
**Purpose:** Extend SereneCode with structural checks that detect overgrown files, functions, classes, and parameter lists — common AI coding agent failure modes — and provide actionable refactoring guidance.
|
|
4
|
+
|
|
5
|
+
**Source:** Implementation plan derived from codebase exploration (2026-04-13).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Configuration
|
|
10
|
+
|
|
11
|
+
### REQ-001: ModuleHealthConfig dataclass
|
|
12
|
+
|
|
13
|
+
A `ModuleHealthConfig` frozen dataclass with the following fields, all enforced by class invariants:
|
|
14
|
+
|
|
15
|
+
- `enabled`: bool. When False, all module health checks are skipped.
|
|
16
|
+
- `file_length_warn`: int, lines above which a file-length advisory is emitted. Must be > 0.
|
|
17
|
+
- `file_length_error`: int, lines above which a file-length error is emitted. Must be > `file_length_warn`.
|
|
18
|
+
- `function_length_warn`: int, body lines above which a function-length advisory is emitted. Must be > 0.
|
|
19
|
+
- `function_length_error`: int, body lines above which a function-length error is emitted. Must be > `function_length_warn`.
|
|
20
|
+
- `parameter_count_warn`: int, non-receiver parameters above which an advisory is emitted. Must be > 0.
|
|
21
|
+
- `parameter_count_error`: int, non-receiver parameters above which an error is emitted. Must be > `parameter_count_warn`.
|
|
22
|
+
- `class_method_count_warn`: int, methods above which a class-size advisory is emitted. Must be > 0.
|
|
23
|
+
- `class_method_count_error`: int, methods above which a class-size error is emitted. Must be > `class_method_count_warn`.
|
|
24
|
+
|
|
25
|
+
### REQ-002: ModuleHealthConfig added to SerenecodeConfig
|
|
26
|
+
|
|
27
|
+
`SerenecodeConfig` gains a `module_health: ModuleHealthConfig` field. All existing composition roots (`default_config`, `strict_config`, `minimal_config`, `_apply_content_overrides`) must propagate this field.
|
|
28
|
+
|
|
29
|
+
### REQ-003: Template-specific default thresholds
|
|
30
|
+
|
|
31
|
+
Each template preset provides different thresholds:
|
|
32
|
+
|
|
33
|
+
| Metric | Default (warn / error) | Strict (warn / error) | Minimal (warn / error) |
|
|
34
|
+
|-----------------------|------------------------|-----------------------|------------------------|
|
|
35
|
+
| File length (lines) | 500 / 1000 | 400 / 700 | 750 / 1500 |
|
|
36
|
+
| Function length (lines) | 50 / 100 | 30 / 60 | 75 / 150 |
|
|
37
|
+
| Parameter count | 5 / 8 | 4 / 6 | 7 / 10 |
|
|
38
|
+
| Class method count | 15 / 25 | 10 / 18 | 20 / 35 |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Advisory Generalization
|
|
43
|
+
|
|
44
|
+
### REQ-004: ADVISORY_FINDING_TYPES constant
|
|
45
|
+
|
|
46
|
+
A module-level `frozenset[str]` in `models.py` enumerating all finding types that use the advisory pattern (EXEMPT status, visible in output, do not block verification unless `--fail-on-advisory`). Initial members: `"dead_code"`, `"file_length"`, `"function_length"`, `"parameter_count"`, `"class_method_count"`.
|
|
47
|
+
|
|
48
|
+
### REQ-005: Generalized advisory counting in make_check_result
|
|
49
|
+
|
|
50
|
+
`make_check_result()` must count advisory results by checking `detail.finding_type in ADVISORY_FINDING_TYPES` instead of hardcoding `"dead_code"`. The `advisory_count` field in `CheckSummary` must reflect all advisory types.
|
|
51
|
+
|
|
52
|
+
### REQ-006: Generalized advisory display in reporter
|
|
53
|
+
|
|
54
|
+
The human and HTML reporters must classify advisory results using `ADVISORY_FINDING_TYPES` membership instead of hardcoding `"dead_code"`. The summary label must read `"advisory"` (not `"advisory (dead code)"`).
|
|
55
|
+
|
|
56
|
+
### REQ-007: Generalized advisory inclusion in MCP wire format
|
|
57
|
+
|
|
58
|
+
The `to_check_response` projection in `schemas.py` must include EXEMPT results with any `finding_type in ADVISORY_FINDING_TYPES` in the wire findings, not only `"dead_code"`.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## File Length Check
|
|
63
|
+
|
|
64
|
+
### REQ-008: File length check counts total lines
|
|
65
|
+
|
|
66
|
+
`_check_file_length` counts lines as `len(source.splitlines())` for each source file. Test files (identified by `_is_test_file_path`) are excluded.
|
|
67
|
+
|
|
68
|
+
### REQ-009: File length error when exceeding error threshold
|
|
69
|
+
|
|
70
|
+
When line count exceeds `config.module_health.file_length_error`, a `FunctionResult` with `status=FAILED`, `function="<module>"`, `line=1`, `finding_type="file_length"` is emitted. The message must include the actual line count and the threshold.
|
|
71
|
+
|
|
72
|
+
### REQ-010: File length advisory when exceeding warn threshold
|
|
73
|
+
|
|
74
|
+
When line count exceeds `config.module_health.file_length_warn` but not the error threshold, a `FunctionResult` with `status=EXEMPT`, `finding_type="file_length"` is emitted (advisory pattern). The message must include the actual line count and both thresholds.
|
|
75
|
+
|
|
76
|
+
### REQ-011: File length check runs on exempt modules
|
|
77
|
+
|
|
78
|
+
Unlike structural policy checks, file length runs on all source files including modules exempt from contract/type checks. This is because exempt modules (adapters, CLI, MCP tools) are often the largest files.
|
|
79
|
+
|
|
80
|
+
### REQ-012: File length suggestions are agent-actionable
|
|
81
|
+
|
|
82
|
+
The `suggestion` field for file-length findings must include concrete refactoring strategies: extracting classes into standalone modules, grouping related functions by shared prefix or domain concept, and identifying comment-banner section boundaries.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Function Length Check
|
|
87
|
+
|
|
88
|
+
### REQ-013: Function length measured by line span
|
|
89
|
+
|
|
90
|
+
`_check_function_length` measures each function's length as `node.end_lineno - node.lineno + 1` using AST `end_lineno`. Both `FunctionDef` and `AsyncFunctionDef` at module level and as class methods are checked.
|
|
91
|
+
|
|
92
|
+
### REQ-014: Function length error when exceeding error threshold
|
|
93
|
+
|
|
94
|
+
When function length exceeds `config.module_health.function_length_error`, a `FunctionResult` with `status=FAILED`, `function=node.name`, `finding_type="function_length"` is emitted.
|
|
95
|
+
|
|
96
|
+
### REQ-015: Function length advisory when exceeding warn threshold
|
|
97
|
+
|
|
98
|
+
When function length exceeds `config.module_health.function_length_warn` but not the error threshold, an advisory `FunctionResult` with `status=EXEMPT`, `finding_type="function_length"` is emitted.
|
|
99
|
+
|
|
100
|
+
### REQ-016: Function length suggestions reference extraction patterns
|
|
101
|
+
|
|
102
|
+
The suggestion must mention: extracting comment-delimited sections, pulling nested loops/conditionals into helpers, and converting setup/teardown into context managers.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Parameter Count Check
|
|
107
|
+
|
|
108
|
+
### REQ-017: Parameter count excludes self and cls
|
|
109
|
+
|
|
110
|
+
`_check_parameter_count` counts non-receiver parameters (excluding `self`/`cls`) for each function. Both positional, keyword-only, `*args`, and `**kwargs` are counted.
|
|
111
|
+
|
|
112
|
+
### REQ-018: Parameter count error when exceeding error threshold
|
|
113
|
+
|
|
114
|
+
When parameter count exceeds `config.module_health.parameter_count_error`, a `FunctionResult` with `status=FAILED`, `finding_type="parameter_count"` is emitted.
|
|
115
|
+
|
|
116
|
+
### REQ-019: Parameter count advisory when exceeding warn threshold
|
|
117
|
+
|
|
118
|
+
When parameter count exceeds `config.module_health.parameter_count_warn` but not the error threshold, an advisory `FunctionResult` is emitted.
|
|
119
|
+
|
|
120
|
+
### REQ-020: Parameter count suggestions reference Parameter Object pattern
|
|
121
|
+
|
|
122
|
+
The suggestion must mention grouping related parameters into a dataclass, TypedDict, or config object using the Parameter Object pattern.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Class Method Count Check
|
|
127
|
+
|
|
128
|
+
### REQ-021: Class method count includes all def nodes in class body
|
|
129
|
+
|
|
130
|
+
`_check_class_method_count` counts direct `FunctionDef` and `AsyncFunctionDef` children of each top-level `ClassDef` (not nested classes).
|
|
131
|
+
|
|
132
|
+
### REQ-022: Class method count error when exceeding error threshold
|
|
133
|
+
|
|
134
|
+
When method count exceeds `config.module_health.class_method_count_error`, a `FunctionResult` with `status=FAILED`, `function=class_name`, `finding_type="class_method_count"` is emitted.
|
|
135
|
+
|
|
136
|
+
### REQ-023: Class method count advisory when exceeding warn threshold
|
|
137
|
+
|
|
138
|
+
When method count exceeds `config.module_health.class_method_count_warn` but not the error threshold, an advisory `FunctionResult` is emitted.
|
|
139
|
+
|
|
140
|
+
### REQ-024: Class method count suggestions reference SRP extraction
|
|
141
|
+
|
|
142
|
+
The suggestion must mention: extracting cohesive groups of methods sharing a prefix, methods accessing a subset of attributes, and methods that could be standalone functions.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Split Suggestions
|
|
147
|
+
|
|
148
|
+
### REQ-025: AST-based split point identification
|
|
149
|
+
|
|
150
|
+
A helper `_suggest_split_points` analyzes a file's AST and source to identify natural module boundaries:
|
|
151
|
+
|
|
152
|
+
- Top-level classes with their line span and method count.
|
|
153
|
+
- Groups of top-level functions sharing a common prefix (e.g., `parse_header`, `parse_body` -> `parse_*`).
|
|
154
|
+
- Banner comments (lines matching patterns like `# --- Section ---` or `# ====`) that suggest logical boundaries.
|
|
155
|
+
|
|
156
|
+
### REQ-026: Split suggestions included in file-length findings
|
|
157
|
+
|
|
158
|
+
When file-length advisory or error findings are emitted, the suggestion field must include the output of `_suggest_split_points` formatted as a bullet list of concrete split candidates with line ranges.
|
|
159
|
+
|
|
160
|
+
### REQ-027: Graceful fallback when no split points found
|
|
161
|
+
|
|
162
|
+
If `_suggest_split_points` identifies no clear boundaries, the file-length finding falls back to the generic refactoring suggestion without split points.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Pipeline Integration
|
|
167
|
+
|
|
168
|
+
### REQ-028: Module health checks run in Level 1 pipeline block
|
|
169
|
+
|
|
170
|
+
All four checks (`_check_file_length`, `_check_function_length`, `_check_parameter_count`, `_check_class_method_count`) are called within the Level 1 block of `run_pipeline`, after dead-code analysis. They are guarded by `config.module_health.enabled`.
|
|
171
|
+
|
|
172
|
+
### REQ-029: Module health checks apply at all verification levels
|
|
173
|
+
|
|
174
|
+
Since the checks are part of Level 1 and Level 1 runs for all levels 1-6 (unless `--verify` skips it), module health checks run by default at every verification level.
|
|
175
|
+
|
|
176
|
+
### REQ-030: Module health check results participate in early termination
|
|
177
|
+
|
|
178
|
+
If any module health check produces a FAILED result and `early_termination=True`, the pipeline stops at Level 1 (consistent with other Level 1 failures).
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## CLI
|
|
183
|
+
|
|
184
|
+
### REQ-031: --skip-module-health flag
|
|
185
|
+
|
|
186
|
+
The `serenecode check` command accepts a `--skip-module-health` boolean flag. When set, `config.module_health.enabled` is overridden to `False` via `dataclasses.replace` before the pipeline runs.
|
|
187
|
+
|
|
188
|
+
### REQ-032: --fail-on-advisory applies to module health advisories
|
|
189
|
+
|
|
190
|
+
The existing `--fail-on-advisory` flag must trigger exit code 11 for any advisory, including module health warnings. The help text and exit message must not hardcode "dead-code."
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## MCP Tool
|
|
195
|
+
|
|
196
|
+
### REQ-033: serenecode_module_health tool returns file metrics
|
|
197
|
+
|
|
198
|
+
A new MCP tool `tool_module_health(path: str)` reads a single Python file and returns a dict containing:
|
|
199
|
+
|
|
200
|
+
- `file`: the file path.
|
|
201
|
+
- `metrics`: `line_count`, `function_count`, `class_count`, `largest_function` (name, lines, line), `max_parameters` (name, count, line), `largest_class` (name, method_count, line).
|
|
202
|
+
- `status`: per-metric status (`"ok"`, `"warning"`, `"error"`) derived from `ModuleHealthConfig` thresholds.
|
|
203
|
+
- `split_suggestions`: output of `_suggest_split_points`.
|
|
204
|
+
|
|
205
|
+
### REQ-034: serenecode_module_health does not run the verification pipeline
|
|
206
|
+
|
|
207
|
+
The tool parses the AST and computes metrics directly, without calling `run_pipeline`. It does not require `--allow-code-execution`. It loads config via `_load_config` for threshold comparison.
|
|
208
|
+
|
|
209
|
+
### REQ-035: serenecode_module_health registered in MCP server
|
|
210
|
+
|
|
211
|
+
The tool is registered in `build_server()` with a description emphasizing proactive use during editing to monitor module structure.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Templates
|
|
216
|
+
|
|
217
|
+
### REQ-036: Module health documented in all templates
|
|
218
|
+
|
|
219
|
+
Each template in `content.py` (default, strict, minimal) includes a "Module Health" subsection under Code Quality Standards documenting the four metrics, their warn/error thresholds, the advisory/error behavior, and the `--skip-module-health` flag.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## INT-001: Pipeline integration flow
|
|
224
|
+
|
|
225
|
+
Kind: call
|
|
226
|
+
Source: run_pipeline
|
|
227
|
+
Target: check_file_length
|
|
228
|
+
|
|
229
|
+
**Components:** `run_pipeline` (pipeline.py), `_check_file_length`, `_check_function_length`, `_check_parameter_count`, `_check_class_method_count`, `ModuleHealthConfig` (config.py)
|
|
230
|
+
|
|
231
|
+
**Flow:**
|
|
232
|
+
1. `run_pipeline` enters Level 1 block.
|
|
233
|
+
2. After structural checks + dead-code analysis, checks `config.module_health.enabled`.
|
|
234
|
+
3. If enabled, calls all four `_check_*` functions, passing `source_files` and `config`.
|
|
235
|
+
4. Each function iterates source files, skips test files, parses AST as needed, applies thresholds.
|
|
236
|
+
5. Results (FAILED or EXEMPT advisory) are appended to `level_1_results`.
|
|
237
|
+
6. Early termination triggers if any FAILED result exists.
|
|
238
|
+
|
|
239
|
+
**Contracts at boundary:** Each check function has icontract preconditions on inputs and postconditions ensuring returned list contains only valid `FunctionResult` objects.
|
|
240
|
+
|
|
241
|
+
## INT-002: Advisory result propagation
|
|
242
|
+
|
|
243
|
+
Kind: call
|
|
244
|
+
Source: make_check_result
|
|
245
|
+
Target: ADVISORY_FINDING_TYPES
|
|
246
|
+
|
|
247
|
+
**Components:** `make_check_result` (models.py), `format_human` / `format_html` (reporter.py), `to_check_response` (schemas.py), `ADVISORY_FINDING_TYPES` (models.py)
|
|
248
|
+
|
|
249
|
+
**Flow:**
|
|
250
|
+
1. Module health checks emit `FunctionResult` with `status=EXEMPT` and `finding_type in ADVISORY_FINDING_TYPES`.
|
|
251
|
+
2. `make_check_result` counts these as `advisory_count` via set membership check.
|
|
252
|
+
3. Reporter classifies them as advisory (visible, distinct from plain exempt) via same set.
|
|
253
|
+
4. MCP schemas include them in wire findings via same set.
|
|
254
|
+
5. CLI `--fail-on-advisory` triggers exit 11 when `advisory_count > 0`.
|
|
255
|
+
|
|
256
|
+
**Invariant:** `advisory_count` is always consistent across all three consumers because they share the single `ADVISORY_FINDING_TYPES` constant.
|
|
257
|
+
|
|
258
|
+
## INT-003: CLI config override for --skip-module-health
|
|
259
|
+
|
|
260
|
+
Kind: call
|
|
261
|
+
Source: check
|
|
262
|
+
Target: run_pipeline
|
|
263
|
+
|
|
264
|
+
**Components:** `check` (cli.py), `SerenecodeConfig` (config.py), `ModuleHealthConfig` (config.py), `run_pipeline` (pipeline.py)
|
|
265
|
+
|
|
266
|
+
**Flow:**
|
|
267
|
+
1. CLI parses `--skip-module-health` flag.
|
|
268
|
+
2. If set, uses `dataclasses.replace` to create a new `SerenecodeConfig` with `module_health.enabled=False`.
|
|
269
|
+
3. Modified config is passed to `run_pipeline`.
|
|
270
|
+
4. Pipeline checks `config.module_health.enabled` and skips all four check functions.
|
|
271
|
+
|
|
272
|
+
**Postcondition:** When `--skip-module-health` is set, no `FunctionResult` with `finding_type in {"file_length", "function_length", "parameter_count", "class_method_count"}` appears in the output.
|
|
273
|
+
|
|
274
|
+
## INT-004: MCP module_health tool standalone analysis
|
|
275
|
+
|
|
276
|
+
Kind: call
|
|
277
|
+
Source: tool_module_health
|
|
278
|
+
Target: _load_config
|
|
279
|
+
|
|
280
|
+
**Components:** `tool_module_health` (tools.py), `_load_config` (tools.py), `_suggest_split_points` (pipeline.py), `ModuleHealthConfig` (config.py)
|
|
281
|
+
|
|
282
|
+
**Flow:**
|
|
283
|
+
1. Tool receives file path, reads source via `LocalFileReader`.
|
|
284
|
+
2. Loads config via `_load_config` (cached, mtime-aware).
|
|
285
|
+
3. Parses AST, computes metrics (line count, function sizes, parameter counts, class sizes).
|
|
286
|
+
4. Compares each metric against `ModuleHealthConfig` thresholds to derive status.
|
|
287
|
+
5. Calls `_suggest_split_points` on source + AST for split candidates.
|
|
288
|
+
6. Returns structured dict with metrics, status, and suggestions.
|
|
289
|
+
|
|
290
|
+
**Postcondition:** Response always contains all metric fields even when file is empty or has no functions/classes (values are 0 / empty).
|
|
@@ -19,11 +19,11 @@ SereneCode checks stack from fast structural rules (Level 1) through types, cove
|
|
|
19
19
|
|
|
20
20
|
## Spec ergonomics
|
|
21
21
|
|
|
22
|
-
- **Narrative vs traceability:** PRDs, `README` sections, and `*_SPEC.md` files are inputs. REQ/INT traceability and `serenecode check --spec` apply only to project-root **SPEC.md**, which must include a `**Source:** …` line (see SERENECODE.md). Run `serenecode doctor` to see whether SPEC.md and narrative-looking files were detected at the project root.
|
|
22
|
+
- **Narrative vs traceability:** PRDs, `README` sections, and `*_SPEC.md` files are inputs. REQ/INT traceability and `serenecode check --spec` apply only to project-root **SPEC.md**, which must include a `**Source:** …` line (see the Spec Traceability section in your project's `SERENECODE.md` from `serenecode init`, or the embedded templates in `src/serenecode/templates/content.py`). Run `serenecode doctor` to see whether SPEC.md and narrative-looking files were detected at the project root.
|
|
23
23
|
- Use **one primary target per comma segment**; avoid stuffing unrelated names into a single `Target` line unless you intend AND semantics.
|
|
24
24
|
- Align **dotted names** with how types appear in code (`from pkg import X as Y` is easier to reason about when `Target` uses the same simple name the implementation calls).
|
|
25
25
|
|
|
26
26
|
## Related reading
|
|
27
27
|
|
|
28
28
|
- [SECURITY.md](SECURITY.md) — trust model for `--allow-code-execution` (required for Levels 3–6 as implemented today).
|
|
29
|
-
-
|
|
29
|
+
- Your project's `SERENECODE.md` (from `serenecode init`) — conventions the structural checker enforces; source templates live in `src/serenecode/templates/content.py`.
|