specsmith 0.11.5.dev450__tar.gz → 0.11.5.dev451__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.
- {specsmith-0.11.5.dev450/src/specsmith.egg-info → specsmith-0.11.5.dev451}/PKG-INFO +1 -1
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/pyproject.toml +1 -1
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/requirements.py +35 -3
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/sync.py +2 -1
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451/src/specsmith.egg-info}/PKG-INFO +1 -1
- specsmith-0.11.5.dev451/tests/test_auditor.py +201 -0
- specsmith-0.11.5.dev450/tests/test_auditor.py +0 -89
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/LICENSE +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/README.md +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/setup.cfg +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/belief.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/certainty.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/py.typed +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/recovery.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/session.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/epistemic/trace.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/__main__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/context_seed.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/runner.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/architect.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/auditor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/auth.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/block_export.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/channel.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/cli.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/compressor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/config.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/context_orchestrator.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/context_window.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/credits.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/differ.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/doctor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/drive.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/editor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/esdb/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/executor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/exporter.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/governance_logic.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/governance_yaml.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/history_search.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/importer.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/instinct.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/languages.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/ledger.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/patent.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/paths.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/phase.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/plugins.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/profiles.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/releaser.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/serve.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/session.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/session_init.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/session_store.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/governance.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/tools.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/trace.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/updater.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/validator.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/workspace.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith.egg-info/SOURCES.txt +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_CMD_001.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_ai_client.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_channel.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_cli.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_compliance.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_compressor.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_dispatch.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_epistemic.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_importer.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_integrations.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_intelligence.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_mcp_client.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_new_modules.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_nexus.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_permissions.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_rate_limits.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_req_248_262.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_scaffolder.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_smoke.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_suggester.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_tools.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_validator.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_vcs.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_warp_parity.py +0 -0
- {specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/tests/test_warp_parity_followup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.11.5.
|
|
3
|
+
Version: 0.11.5.dev451
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.11.5.
|
|
7
|
+
version = "0.11.5.dev451"
|
|
8
8
|
description = "Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -9,15 +9,18 @@ from pathlib import Path
|
|
|
9
9
|
|
|
10
10
|
# Flexible patterns that handle both two-part (REQ-001) and three-part
|
|
11
11
|
# (REQ-CLI-001, REG-012) identifiers used across projects.
|
|
12
|
+
# Letter suffixes (e.g. TEST-NN-002a, TEST-NN-002b) are supported via [a-z]* —
|
|
13
|
+
# without this, the \b word boundary after \d+ would not match when a letter
|
|
14
|
+
# follows digits, causing the ID to be silently skipped (#183).
|
|
12
15
|
_FLEX_REQ = r"REQ-(?:[A-Z][A-Z0-9_]*-)?\d+"
|
|
13
|
-
_FLEX_TEST = r"TEST-(?:[A-Z][A-Z0-9_]*-)?\d+"
|
|
16
|
+
_FLEX_TEST = r"TEST-(?:[A-Z][A-Z0-9_]*-)?\d+[a-z]*"
|
|
14
17
|
|
|
15
18
|
_REQ_PATTERN = re.compile(r"\b(" + _FLEX_REQ + r")\b")
|
|
16
19
|
_TEST_COVERS_PATTERN = re.compile(
|
|
17
20
|
r"(?:Covers|\*\*Requirement(?:\s+ID)?:?\*\*|Requirement(?:\s+ID)?):?\s*"
|
|
18
21
|
r"(" + _FLEX_REQ + r"(?:\s*,\s*" + _FLEX_REQ + r")*)"
|
|
19
22
|
)
|
|
20
|
-
_TEST_ID_PATTERN = re.compile(r"\b(
|
|
23
|
+
_TEST_ID_PATTERN = re.compile(r"\b(TEST-(?:[A-Z][A-Z0-9_]*-)?\d+[a-z]*)\b")
|
|
21
24
|
|
|
22
25
|
# Heading detectors for REQUIREMENTS.md (two styles supported):
|
|
23
26
|
# Style A: ## REQ-001 or ## REQ-CLI-001
|
|
@@ -111,9 +114,18 @@ def add_req(
|
|
|
111
114
|
|
|
112
115
|
|
|
113
116
|
def trace_reqs(root: Path) -> list[dict[str, object]]:
|
|
114
|
-
"""Map each REQ to its covering TESTs.
|
|
117
|
+
"""Map each REQ to its covering TESTs.
|
|
118
|
+
|
|
119
|
+
In YAML-first mode, reads .specsmith/testcases.json directly — this avoids
|
|
120
|
+
regex-based ID parsing entirely and correctly handles letter-suffix IDs
|
|
121
|
+
(e.g. TEST-NN-002a, TEST-NN-002b) that were previously misidentified (#183).
|
|
122
|
+
Falls back to TESTS.md regex parsing in legacy Markdown mode.
|
|
123
|
+
"""
|
|
124
|
+
import json as _json
|
|
125
|
+
|
|
115
126
|
req_path = root / "docs" / "REQUIREMENTS.md"
|
|
116
127
|
test_path = root / "docs" / "TESTS.md"
|
|
128
|
+
testcases_json = root / ".specsmith" / "testcases.json"
|
|
117
129
|
|
|
118
130
|
req_ids: list[str] = []
|
|
119
131
|
if req_path.exists():
|
|
@@ -121,6 +133,26 @@ def trace_reqs(root: Path) -> list[dict[str, object]]:
|
|
|
121
133
|
|
|
122
134
|
covered_by: dict[str, list[str]] = {r: [] for r in req_ids}
|
|
123
135
|
|
|
136
|
+
# YAML mode: prefer machine-readable testcases.json — exact IDs, no regex.
|
|
137
|
+
if testcases_json.is_file():
|
|
138
|
+
try:
|
|
139
|
+
records = _json.loads(testcases_json.read_text(encoding="utf-8"))
|
|
140
|
+
for record in records:
|
|
141
|
+
if (
|
|
142
|
+
isinstance(record, dict)
|
|
143
|
+
and isinstance(record.get("requirement_id"), str)
|
|
144
|
+
and isinstance(record.get("id"), str)
|
|
145
|
+
):
|
|
146
|
+
rid = record["requirement_id"]
|
|
147
|
+
if rid in covered_by:
|
|
148
|
+
covered_by[rid].append(record["id"])
|
|
149
|
+
return [
|
|
150
|
+
{"req": r, "tests": tests, "covered": len(tests) > 0}
|
|
151
|
+
for r, tests in covered_by.items()
|
|
152
|
+
]
|
|
153
|
+
except (OSError, ValueError):
|
|
154
|
+
pass # Fall through to TESTS.md regex parsing
|
|
155
|
+
|
|
124
156
|
if test_path.exists():
|
|
125
157
|
test_text = test_path.read_text(encoding="utf-8")
|
|
126
158
|
current_test = ""
|
|
@@ -41,7 +41,8 @@ _DIRECT_HEADING = re.compile(r"^#{1,3}\s+(" + _FLEX_REQ_ID + r")\b")
|
|
|
41
41
|
_ID_FIELD = re.compile(r"^-\s+\*\*ID:\*\*\s+(" + _FLEX_REQ_ID + r")")
|
|
42
42
|
_FIELD_LINE = re.compile(r"^-\s+\*\*(.+?):\*\*\s+(.+)")
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
# Letter suffixes (e.g. TEST-NN-002a) are supported via [a-z]* — fixes #183.
|
|
45
|
+
_FLEX_TEST_ID = r"TEST-(?:[A-Z][A-Z0-9_]*-)?\d+[a-z]*"
|
|
45
46
|
_TEST_NUMBERED_HEADING = re.compile(r"^#{1,3}\s+(?:TEST-[A-Z0-9_-]+\s+)?(.+?)\s*$")
|
|
46
47
|
_TEST_ID_FIELD = re.compile(r"^-\s+\*\*ID:\*\*\s+(" + _FLEX_TEST_ID + r")")
|
|
47
48
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.11.5.
|
|
3
|
+
Version: 0.11.5.dev451
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
+
"""Tests for specsmith auditor."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from specsmith.auditor import run_audit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def governed_project(tmp_path: Path) -> Path:
|
|
16
|
+
"""Create a minimal governed project for audit testing."""
|
|
17
|
+
(tmp_path / "AGENTS.md").write_text("# AGENTS\nShort hub.\n", encoding="utf-8")
|
|
18
|
+
(tmp_path / "LEDGER.md").write_text("# Ledger\n\n## Session 1\nDone.\n", encoding="utf-8")
|
|
19
|
+
|
|
20
|
+
gov = tmp_path / "docs" / "governance"
|
|
21
|
+
gov.mkdir(parents=True)
|
|
22
|
+
for f in (
|
|
23
|
+
"RULES.md",
|
|
24
|
+
"SESSION-PROTOCOL.md",
|
|
25
|
+
"LIFECYCLE.md",
|
|
26
|
+
"ROLES.md",
|
|
27
|
+
"CONTEXT-BUDGET.md",
|
|
28
|
+
"VERIFICATION.md",
|
|
29
|
+
"DRIFT-METRICS.md",
|
|
30
|
+
):
|
|
31
|
+
(gov / f).write_text(f"# {f}\nContent.\n", encoding="utf-8")
|
|
32
|
+
|
|
33
|
+
docs = tmp_path / "docs"
|
|
34
|
+
(docs / "REQUIREMENTS.md").write_text("# Reqs\n", encoding="utf-8")
|
|
35
|
+
(docs / "TESTS.md").write_text("# Tests\n", encoding="utf-8")
|
|
36
|
+
(docs / "ARCHITECTURE.md").write_text("# Arch\n", encoding="utf-8")
|
|
37
|
+
(tmp_path / "CONTRIBUTING.md").write_text("# Contributing\n", encoding="utf-8")
|
|
38
|
+
(tmp_path / "LICENSE").write_text("MIT License\n", encoding="utf-8")
|
|
39
|
+
# Scaffold config (at root for backward compat — audit now accepts either location)
|
|
40
|
+
(tmp_path / "scaffold.yml").write_text(
|
|
41
|
+
"name: test-project\ntype: cli-python\nspec_version: 0.10.1\nvcs_platform: github\n",
|
|
42
|
+
encoding="utf-8",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return tmp_path
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestAuditHealthy:
|
|
49
|
+
def test_passes_on_governed_project(self, governed_project: Path) -> None:
|
|
50
|
+
report = run_audit(governed_project)
|
|
51
|
+
assert report.healthy
|
|
52
|
+
assert report.failed == 0
|
|
53
|
+
|
|
54
|
+
def test_reports_pass_count(self, governed_project: Path) -> None:
|
|
55
|
+
report = run_audit(governed_project)
|
|
56
|
+
assert report.passed > 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TestAuditDetectsIssues:
|
|
60
|
+
def test_missing_agents_md(self, tmp_path: Path) -> None:
|
|
61
|
+
(tmp_path / "LEDGER.md").write_text("# Ledger\n", encoding="utf-8")
|
|
62
|
+
report = run_audit(tmp_path)
|
|
63
|
+
failed_names = [r.name for r in report.results if not r.passed]
|
|
64
|
+
assert "file-exists:AGENTS.md" in failed_names
|
|
65
|
+
|
|
66
|
+
def test_missing_ledger(self, tmp_path: Path) -> None:
|
|
67
|
+
(tmp_path / "AGENTS.md").write_text("# Agents\n", encoding="utf-8")
|
|
68
|
+
report = run_audit(tmp_path)
|
|
69
|
+
failed_names = [r.name for r in report.results if not r.passed]
|
|
70
|
+
assert "file-exists:LEDGER.md" in failed_names
|
|
71
|
+
|
|
72
|
+
def test_oversized_ledger(self, governed_project: Path) -> None:
|
|
73
|
+
big_ledger = "# Ledger\n" + "\n".join(f"Line {i}" for i in range(600))
|
|
74
|
+
(governed_project / "LEDGER.md").write_text(big_ledger, encoding="utf-8")
|
|
75
|
+
report = run_audit(governed_project)
|
|
76
|
+
size_results = [r for r in report.results if r.name == "ledger-size"]
|
|
77
|
+
assert len(size_results) == 1
|
|
78
|
+
assert not size_results[0].passed
|
|
79
|
+
|
|
80
|
+
def test_req_test_coverage(self, governed_project: Path) -> None:
|
|
81
|
+
reqs = "# Reqs\n\nREQ-CLI-001: do things\nREQ-CLI-002: more things\n"
|
|
82
|
+
tests = "# Tests\n\nTEST-CLI-001\nCovers: REQ-CLI-001\n"
|
|
83
|
+
(governed_project / "docs" / "REQUIREMENTS.md").write_text(reqs, encoding="utf-8")
|
|
84
|
+
(governed_project / "docs" / "TESTS.md").write_text(tests, encoding="utf-8")
|
|
85
|
+
report = run_audit(governed_project)
|
|
86
|
+
coverage = [r for r in report.results if r.name == "req-test-coverage"]
|
|
87
|
+
assert len(coverage) == 1
|
|
88
|
+
assert not coverage[0].passed
|
|
89
|
+
assert "REQ-CLI-002" in coverage[0].message
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
# Regression: issue #183 — letter-suffix TEST IDs in req trace
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TestReqTraceLetterSuffixRegression:
|
|
98
|
+
"""Regression tests for #183 — req trace misidentifies TEST-NN-002a/b.
|
|
99
|
+
|
|
100
|
+
Root cause: _TEST_ID_PATTERN used \\d+\\b which fails to match when a
|
|
101
|
+
letter immediately follows digits (\\b requires a non-word char boundary,
|
|
102
|
+
but letters are word chars). This caused current_test to remain pointing
|
|
103
|
+
at the last successfully-parsed ID (TEST-NN-020), so both TEST-NN-002a and
|
|
104
|
+
TEST-NN-002b coverage lines were erroneously attributed to TEST-NN-020.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def test_letter_suffix_ids_captured_from_tests_md(self, tmp_path: Path) -> None:
|
|
108
|
+
"""trace_reqs must return TEST-NN-002a / TEST-NN-002b, not TEST-NN-020 twice."""
|
|
109
|
+
from specsmith.requirements import trace_reqs
|
|
110
|
+
|
|
111
|
+
docs = tmp_path / "docs"
|
|
112
|
+
docs.mkdir()
|
|
113
|
+
(docs / "REQUIREMENTS.md").write_text(
|
|
114
|
+
"# Requirements\n\n## REQ-NN-001\n- **Status**: defined\n\n"
|
|
115
|
+
"## REQ-NN-002\n- **Status**: defined\n",
|
|
116
|
+
encoding="utf-8",
|
|
117
|
+
)
|
|
118
|
+
# Simulate the buggy scenario: TEST-NN-020 appears BEFORE TEST-NN-002a/b
|
|
119
|
+
# so it would stick as current_test if letter suffixes aren't parsed.
|
|
120
|
+
(docs / "TESTS.md").write_text(
|
|
121
|
+
"# Tests\n\n"
|
|
122
|
+
"## TEST-NN-020\n- **Requirement ID**: REQ-NN-001\n\n"
|
|
123
|
+
"## TEST-NN-002a\n- **Requirement ID**: REQ-NN-002\n\n"
|
|
124
|
+
"## TEST-NN-002b\n- **Requirement ID**: REQ-NN-002\n",
|
|
125
|
+
encoding="utf-8",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
traces = trace_reqs(tmp_path)
|
|
129
|
+
by_req = {t["req"]: t["tests"] for t in traces}
|
|
130
|
+
|
|
131
|
+
# REQ-NN-001 should map to TEST-NN-020 only
|
|
132
|
+
assert by_req.get("REQ-NN-001") == ["TEST-NN-020"]
|
|
133
|
+
# REQ-NN-002 must map to TEST-NN-002a and TEST-NN-002b — NOT TEST-NN-020 twice
|
|
134
|
+
assert "TEST-NN-002a" in by_req.get("REQ-NN-002", [])
|
|
135
|
+
assert "TEST-NN-002b" in by_req.get("REQ-NN-002", [])
|
|
136
|
+
assert "TEST-NN-020" not in by_req.get("REQ-NN-002", [])
|
|
137
|
+
|
|
138
|
+
def test_no_duplicate_ids_in_trace(self, tmp_path: Path) -> None:
|
|
139
|
+
"""REQ-NN-002 must not have duplicate entries in its test list."""
|
|
140
|
+
from specsmith.requirements import trace_reqs
|
|
141
|
+
|
|
142
|
+
docs = tmp_path / "docs"
|
|
143
|
+
docs.mkdir()
|
|
144
|
+
(docs / "REQUIREMENTS.md").write_text(
|
|
145
|
+
"# Requirements\n\n## REQ-NN-002\n- **Status**: defined\n",
|
|
146
|
+
encoding="utf-8",
|
|
147
|
+
)
|
|
148
|
+
(docs / "TESTS.md").write_text(
|
|
149
|
+
"# Tests\n\n"
|
|
150
|
+
"## TEST-NN-002a\n- **Requirement ID**: REQ-NN-002\n\n"
|
|
151
|
+
"## TEST-NN-002b\n- **Requirement ID**: REQ-NN-002\n",
|
|
152
|
+
encoding="utf-8",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
traces = trace_reqs(tmp_path)
|
|
156
|
+
tests = traces[0]["tests"]
|
|
157
|
+
assert len(tests) == len(set(tests)), f"Duplicate test IDs: {tests}"
|
|
158
|
+
|
|
159
|
+
def test_yaml_mode_uses_testcases_json(self, tmp_path: Path) -> None:
|
|
160
|
+
"""In YAML mode, trace_reqs reads testcases.json directly (no regex)."""
|
|
161
|
+
import json
|
|
162
|
+
|
|
163
|
+
from specsmith.requirements import trace_reqs
|
|
164
|
+
|
|
165
|
+
docs = tmp_path / "docs"
|
|
166
|
+
docs.mkdir()
|
|
167
|
+
(docs / "REQUIREMENTS.md").write_text(
|
|
168
|
+
"# Requirements\n\n## REQ-NN-002\n- **Status**: defined\n",
|
|
169
|
+
encoding="utf-8",
|
|
170
|
+
)
|
|
171
|
+
state = tmp_path / ".specsmith"
|
|
172
|
+
state.mkdir()
|
|
173
|
+
(state / "testcases.json").write_text(
|
|
174
|
+
json.dumps(
|
|
175
|
+
[
|
|
176
|
+
{"id": "TEST-NN-002a", "requirement_id": "REQ-NN-002"},
|
|
177
|
+
{"id": "TEST-NN-002b", "requirement_id": "REQ-NN-002"},
|
|
178
|
+
]
|
|
179
|
+
),
|
|
180
|
+
encoding="utf-8",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
traces = trace_reqs(tmp_path)
|
|
184
|
+
by_req = {t["req"]: t["tests"] for t in traces}
|
|
185
|
+
assert sorted(by_req["REQ-NN-002"]) == ["TEST-NN-002a", "TEST-NN-002b"]
|
|
186
|
+
|
|
187
|
+
def test_sync_parse_tests_md_preserves_letter_suffix(self, tmp_path: Path) -> None:
|
|
188
|
+
"""sync.parse_tests_md must not truncate TEST-NN-002a to TEST-NN-002."""
|
|
189
|
+
from specsmith.sync import parse_tests_md
|
|
190
|
+
|
|
191
|
+
text = (
|
|
192
|
+
"## TEST-NN-002a\n"
|
|
193
|
+
"- **Requirement ID**: REQ-NN-002\n\n"
|
|
194
|
+
"## TEST-NN-002b\n"
|
|
195
|
+
"- **Requirement ID**: REQ-NN-002\n"
|
|
196
|
+
)
|
|
197
|
+
records = parse_tests_md(text)
|
|
198
|
+
ids = [r["id"] for r in records]
|
|
199
|
+
assert "TEST-NN-002a" in ids
|
|
200
|
+
assert "TEST-NN-002b" in ids
|
|
201
|
+
assert "TEST-NN-002" not in ids # must not be truncated
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
-
"""Tests for specsmith auditor."""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import pytest
|
|
10
|
-
|
|
11
|
-
from specsmith.auditor import run_audit
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def governed_project(tmp_path: Path) -> Path:
|
|
16
|
-
"""Create a minimal governed project for audit testing."""
|
|
17
|
-
(tmp_path / "AGENTS.md").write_text("# AGENTS\nShort hub.\n", encoding="utf-8")
|
|
18
|
-
(tmp_path / "LEDGER.md").write_text("# Ledger\n\n## Session 1\nDone.\n", encoding="utf-8")
|
|
19
|
-
|
|
20
|
-
gov = tmp_path / "docs" / "governance"
|
|
21
|
-
gov.mkdir(parents=True)
|
|
22
|
-
for f in (
|
|
23
|
-
"RULES.md",
|
|
24
|
-
"SESSION-PROTOCOL.md",
|
|
25
|
-
"LIFECYCLE.md",
|
|
26
|
-
"ROLES.md",
|
|
27
|
-
"CONTEXT-BUDGET.md",
|
|
28
|
-
"VERIFICATION.md",
|
|
29
|
-
"DRIFT-METRICS.md",
|
|
30
|
-
):
|
|
31
|
-
(gov / f).write_text(f"# {f}\nContent.\n", encoding="utf-8")
|
|
32
|
-
|
|
33
|
-
docs = tmp_path / "docs"
|
|
34
|
-
(docs / "REQUIREMENTS.md").write_text("# Reqs\n", encoding="utf-8")
|
|
35
|
-
(docs / "TESTS.md").write_text("# Tests\n", encoding="utf-8")
|
|
36
|
-
(docs / "ARCHITECTURE.md").write_text("# Arch\n", encoding="utf-8")
|
|
37
|
-
(tmp_path / "CONTRIBUTING.md").write_text("# Contributing\n", encoding="utf-8")
|
|
38
|
-
(tmp_path / "LICENSE").write_text("MIT License\n", encoding="utf-8")
|
|
39
|
-
# Scaffold config (at root for backward compat — audit now accepts either location)
|
|
40
|
-
(tmp_path / "scaffold.yml").write_text(
|
|
41
|
-
"name: test-project\ntype: cli-python\nspec_version: 0.10.1\nvcs_platform: github\n",
|
|
42
|
-
encoding="utf-8",
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
return tmp_path
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class TestAuditHealthy:
|
|
49
|
-
def test_passes_on_governed_project(self, governed_project: Path) -> None:
|
|
50
|
-
report = run_audit(governed_project)
|
|
51
|
-
assert report.healthy
|
|
52
|
-
assert report.failed == 0
|
|
53
|
-
|
|
54
|
-
def test_reports_pass_count(self, governed_project: Path) -> None:
|
|
55
|
-
report = run_audit(governed_project)
|
|
56
|
-
assert report.passed > 0
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class TestAuditDetectsIssues:
|
|
60
|
-
def test_missing_agents_md(self, tmp_path: Path) -> None:
|
|
61
|
-
(tmp_path / "LEDGER.md").write_text("# Ledger\n", encoding="utf-8")
|
|
62
|
-
report = run_audit(tmp_path)
|
|
63
|
-
failed_names = [r.name for r in report.results if not r.passed]
|
|
64
|
-
assert "file-exists:AGENTS.md" in failed_names
|
|
65
|
-
|
|
66
|
-
def test_missing_ledger(self, tmp_path: Path) -> None:
|
|
67
|
-
(tmp_path / "AGENTS.md").write_text("# Agents\n", encoding="utf-8")
|
|
68
|
-
report = run_audit(tmp_path)
|
|
69
|
-
failed_names = [r.name for r in report.results if not r.passed]
|
|
70
|
-
assert "file-exists:LEDGER.md" in failed_names
|
|
71
|
-
|
|
72
|
-
def test_oversized_ledger(self, governed_project: Path) -> None:
|
|
73
|
-
big_ledger = "# Ledger\n" + "\n".join(f"Line {i}" for i in range(600))
|
|
74
|
-
(governed_project / "LEDGER.md").write_text(big_ledger, encoding="utf-8")
|
|
75
|
-
report = run_audit(governed_project)
|
|
76
|
-
size_results = [r for r in report.results if r.name == "ledger-size"]
|
|
77
|
-
assert len(size_results) == 1
|
|
78
|
-
assert not size_results[0].passed
|
|
79
|
-
|
|
80
|
-
def test_req_test_coverage(self, governed_project: Path) -> None:
|
|
81
|
-
reqs = "# Reqs\n\nREQ-CLI-001: do things\nREQ-CLI-002: more things\n"
|
|
82
|
-
tests = "# Tests\n\nTEST-CLI-001\nCovers: REQ-CLI-001\n"
|
|
83
|
-
(governed_project / "docs" / "REQUIREMENTS.md").write_text(reqs, encoding="utf-8")
|
|
84
|
-
(governed_project / "docs" / "TESTS.md").write_text(tests, encoding="utf-8")
|
|
85
|
-
report = run_audit(governed_project)
|
|
86
|
-
coverage = [r for r in report.results if r.name == "req-test-coverage"]
|
|
87
|
-
assert len(coverage) == 1
|
|
88
|
-
assert not coverage[0].passed
|
|
89
|
-
assert "REQ-CLI-002" in coverage[0].message
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/dispatch/dispatcher.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/execution_profiles.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/model_intelligence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/agent/provider_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.11.5.dev450 → specsmith-0.11.5.dev451}/src/specsmith/datasources/patentsview.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|