specsmith 0.18.0.dev710__tar.gz → 0.19.0.dev726__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.18.0.dev710/src/specsmith.egg-info → specsmith-0.19.0.dev726}/PKG-INFO +1 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/pyproject.toml +1 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/__init__.py +1 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/broker.py +11 -2
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/runner.py +17 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/auditor.py +82 -12
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/cli.py +96 -8
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/commands/issues_policy.py +12 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/governance_logic.py +29 -11
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/__init__.py +2 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m001_governance_yaml.py +4 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/project_metrics.py +28 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/sync.py +41 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/wi_store.py +25 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726/src/specsmith.egg-info}/PKG-INFO +1 -1
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith.egg-info/SOURCES.txt +6 -1
- specsmith-0.19.0.dev726/tests/test_audit_governance.py +82 -0
- specsmith-0.19.0.dev726/tests/test_broker_scope.py +66 -0
- specsmith-0.19.0.dev726/tests/test_metrics_flush.py +53 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_nexus.py +79 -0
- specsmith-0.19.0.dev726/tests/test_sync_reconcile.py +96 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_wi_lifecycle.py +20 -4
- specsmith-0.19.0.dev726/tests/test_wi_lifecycle_extended.py +87 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/LICENSE +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/README.md +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/setup.cfg +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/belief.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/certainty.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/py.typed +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/recovery.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/session.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/epistemic/trace.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/__main__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/context_seed.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/model_router.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/token_pricing.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/approvals.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/architect.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/auth.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/block_export.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/channel.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/commands/reporting.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/compressor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/config.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/context_orchestrator.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/context_window.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/credits.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/dashboard.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/differ.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/doctor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/drive.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/editor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/efficiency.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb/_license.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb/sqlite_store.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb_sweep.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/esdb_writer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/executor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/exporter.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/governance_yaml.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/governed_pr.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/history_search.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/importer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/instinct.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/languages.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/ledger.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/local_model.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/mcp_server.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m007_yaml_first.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m008_esdb_full_coverage.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m009_esdb_first.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m010_post_esdb_cleanup.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/patent.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/paths.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/phase.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/plugins.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/policy.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/profiles.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/quality_report.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/recover.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/releaser.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/requirements.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/risk.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/serve.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/session.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/session_init.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/session_store.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/ai_agents.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/data_engineering.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/governance.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/platform_engineering.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/software_engineering.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/specsmith_skills.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills/web_backend.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/tools.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/trace.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/transcripts.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/updater.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/validator.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/workspace.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_CMD_001.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_agent_run_feedback.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_ai_client.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_architect_interview.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_architect_v2.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_architecture.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_auditor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_benchmark_harness.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_channel.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_cleanup_cmd.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_cli.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_cli_regressions.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_compliance.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_compliance_governance.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_compressor.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_deprecation_registry.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_dispatch.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_dual_write.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_efficiency.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_epistemic.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_backend_switch.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_enforcement.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_integration.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_license.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_sqlite.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_status_integrity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_status_output.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_sweep.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_verify_chain_cli.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_esdb_writer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_golden_path.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_importer.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_init_modes.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_integrations.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_intelligence.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_issue_policy_features.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_issues_263_264.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_local_model.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_markdown_deprecation.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_mcp_client.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_mcp_server.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_migration_direction.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_model_router.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_new_modules.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_parallel_agents.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_permissions.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_project_metrics.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_quality_report.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_rate_limits.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_releaser.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_repl.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_repl_extended.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_reporting_commands.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_req_248_262.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_req_358_359.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_resume_command.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_scaffolder.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_schema_migrations.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_smoke.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_sqlite_parity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_suggester.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_token_cost.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_tools.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_trace_vault_esdb.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_typing_guardrails.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_validator.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_vcs.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/tests/test_warp_parity.py +0 -0
- {specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/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.
|
|
3
|
+
Version: 0.19.0.dev726
|
|
4
4
|
Summary: AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider.
|
|
5
5
|
Author: Layer1Labs Silicon, Inc.
|
|
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.
|
|
7
|
+
version = "0.19.0.dev726"
|
|
8
8
|
description = "AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -8,4 +8,4 @@ from importlib.metadata import version as _pkg_version
|
|
|
8
8
|
try:
|
|
9
9
|
__version__: str = _pkg_version("specsmith")
|
|
10
10
|
except PackageNotFoundError: # running from source without install
|
|
11
|
-
__version__ = "0.
|
|
11
|
+
__version__ = "0.19.0" # fallback: keep in sync with pyproject.toml
|
|
@@ -89,12 +89,19 @@ _DESTRUCTIVE_PATTERNS = (
|
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
_RELEASE_PATTERNS = (
|
|
92
|
-
|
|
92
|
+
# Explicit release execution verbs — never matches tooling edits (REQ-430)
|
|
93
93
|
re.compile(r"\bship\b", re.IGNORECASE),
|
|
94
94
|
re.compile(r"\bpublish\s+(to\s+)?pypi\b", re.IGNORECASE),
|
|
95
95
|
re.compile(r"\btag\s+v?\d", re.IGNORECASE),
|
|
96
96
|
re.compile(r"\bbump\s+(the\s+)?version\b", re.IGNORECASE),
|
|
97
|
+
# "cut/do/make/create/publish a release" — execution intent only
|
|
98
|
+
re.compile(r"\b(cut|do|make|create|publish)\s+(a\s+|the\s+)?release\b", re.IGNORECASE),
|
|
99
|
+
# "release vX.Y" or "release 1.2.3" — version-tagged releases
|
|
100
|
+
re.compile(r"\brelease\s+v?\d+[\d\.]*\b", re.IGNORECASE),
|
|
97
101
|
)
|
|
102
|
+
# NOTE: bare "\brelease\b" was intentionally removed (REQ-430). Edits to
|
|
103
|
+
# release.yml, packaging scripts, release pipelines, and release docs must
|
|
104
|
+
# fall through to _CHANGE_PATTERNS so scope-matching can accept them.
|
|
98
105
|
|
|
99
106
|
_REFACTOR_PATTERNS = (
|
|
100
107
|
re.compile(r"\b(refactor|rewrite|extract|reorganise|reorganize|restructure)\b", re.IGNORECASE),
|
|
@@ -109,7 +116,7 @@ _CHANGE_PATTERNS = (
|
|
|
109
116
|
re.compile(r"\b(fix|repair|patch)\b", re.IGNORECASE),
|
|
110
117
|
re.compile(r"\b(add|implement|create|introduce|build)\b", re.IGNORECASE),
|
|
111
118
|
re.compile(r"\b(rename)\b", re.IGNORECASE),
|
|
112
|
-
re.compile(r"\b(update|migrate|upgrade)\b", re.IGNORECASE),
|
|
119
|
+
re.compile(r"\b(update|migrate|upgrade|edit)\b", re.IGNORECASE),
|
|
113
120
|
re.compile(r"\b(remove|delete)\s+(the\s+)?(unused|stale|legacy)\b", re.IGNORECASE),
|
|
114
121
|
)
|
|
115
122
|
|
|
@@ -324,6 +331,7 @@ def infer_scope(
|
|
|
324
331
|
repo_index_path: Path | None = None,
|
|
325
332
|
*,
|
|
326
333
|
top_k: int = 3,
|
|
334
|
+
min_score: float = 0.15,
|
|
327
335
|
) -> ScopeProposal:
|
|
328
336
|
"""Infer the project scope affected by a natural-language utterance.
|
|
329
337
|
|
|
@@ -347,6 +355,7 @@ def infer_scope(
|
|
|
347
355
|
denom = max(1, min(len(tokens), len(req_tokens)))
|
|
348
356
|
scored.append((overlap / denom, req))
|
|
349
357
|
scored.sort(key=lambda x: -x[0])
|
|
358
|
+
scored = [(s, r) for s, r in scored if s >= min_score]
|
|
350
359
|
top_reqs = [r for _, r in scored[:top_k]]
|
|
351
360
|
|
|
352
361
|
# File matches from .repo-index/files.json (best-effort, optional).
|
|
@@ -608,6 +608,7 @@ class AgentRunner:
|
|
|
608
608
|
|
|
609
609
|
# Write token_metric to ESDB (REQ-410) — best-effort, never blocks.
|
|
610
610
|
if tokens_in or tokens_out:
|
|
611
|
+
model_name = str(getattr(result, "provider", "") if result else "")
|
|
611
612
|
try:
|
|
612
613
|
from specsmith.esdb_writer import write_token_metric
|
|
613
614
|
|
|
@@ -616,12 +617,27 @@ class AgentRunner:
|
|
|
616
617
|
input_tokens=tokens_in,
|
|
617
618
|
output_tokens=tokens_out,
|
|
618
619
|
cost_usd=cost_usd,
|
|
619
|
-
model=
|
|
620
|
+
model=model_name,
|
|
620
621
|
command_source=activity or "chat",
|
|
621
622
|
work_item_id=str(getattr(self._state, "active_work_item_id", "") or ""),
|
|
622
623
|
)
|
|
623
624
|
except Exception: # noqa: BLE001
|
|
624
625
|
pass
|
|
626
|
+
# Flush token usage to session_metrics.jsonl (REQ-436)
|
|
627
|
+
try:
|
|
628
|
+
from specsmith.project_metrics import flush_session_metrics # noqa: PLC0415
|
|
629
|
+
|
|
630
|
+
flush_session_metrics(
|
|
631
|
+
Path(self.project_dir),
|
|
632
|
+
work_item_id=str(getattr(self._state, "active_work_item_id", "") or ""),
|
|
633
|
+
model=model_name,
|
|
634
|
+
input_tokens=tokens_in,
|
|
635
|
+
output_tokens=tokens_out,
|
|
636
|
+
cost_usd=cost_usd,
|
|
637
|
+
command=activity or "chat",
|
|
638
|
+
)
|
|
639
|
+
except Exception: # noqa: BLE001
|
|
640
|
+
pass
|
|
625
641
|
|
|
626
642
|
if result is not None:
|
|
627
643
|
self._history.append({"role": "user", "text": utterance})
|
|
@@ -316,6 +316,76 @@ def check_governance_files(root: Path) -> list[AuditResult]:
|
|
|
316
316
|
return results
|
|
317
317
|
|
|
318
318
|
|
|
319
|
+
def check_governance_yaml_content(root: Path) -> list[AuditResult]:
|
|
320
|
+
"""Validate .specsmith/governance/*.yaml files are not empty/fallback-only."""
|
|
321
|
+
results: list[AuditResult] = []
|
|
322
|
+
gov_dir = root / ".specsmith" / "governance"
|
|
323
|
+
if not gov_dir.is_dir():
|
|
324
|
+
return results
|
|
325
|
+
|
|
326
|
+
import yaml
|
|
327
|
+
|
|
328
|
+
for yaml_path in sorted(gov_dir.glob("*.yaml")):
|
|
329
|
+
stem = yaml_path.stem
|
|
330
|
+
check_name = f"governance-yaml:{stem}"
|
|
331
|
+
try:
|
|
332
|
+
loaded = yaml.safe_load(yaml_path.read_text(encoding="utf-8")) or {}
|
|
333
|
+
except Exception as exc: # noqa: BLE001
|
|
334
|
+
results.append(
|
|
335
|
+
AuditResult(
|
|
336
|
+
name=check_name,
|
|
337
|
+
passed=False,
|
|
338
|
+
message=f"{yaml_path.name} failed to parse as YAML: {exc}",
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
continue
|
|
342
|
+
|
|
343
|
+
section = loaded.get(stem) if isinstance(loaded, dict) else None
|
|
344
|
+
if not isinstance(section, list):
|
|
345
|
+
results.append(
|
|
346
|
+
AuditResult(
|
|
347
|
+
name=check_name,
|
|
348
|
+
passed=False,
|
|
349
|
+
message=(f"{yaml_path.name} missing expected top-level list key '{stem}'"),
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
continue
|
|
353
|
+
|
|
354
|
+
if not section:
|
|
355
|
+
results.append(
|
|
356
|
+
AuditResult(
|
|
357
|
+
name=check_name,
|
|
358
|
+
passed=False,
|
|
359
|
+
message=f"{yaml_path.name} has an empty '{stem}' list",
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
continue
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
len(section) == 1
|
|
366
|
+
and isinstance(section[0], dict)
|
|
367
|
+
and set(section[0].keys()) == {"note"}
|
|
368
|
+
):
|
|
369
|
+
results.append(
|
|
370
|
+
AuditResult(
|
|
371
|
+
name=check_name,
|
|
372
|
+
passed=False,
|
|
373
|
+
message=(f"{yaml_path.name} only contains migration fallback note (m001)"),
|
|
374
|
+
)
|
|
375
|
+
)
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
results.append(
|
|
379
|
+
AuditResult(
|
|
380
|
+
name=check_name,
|
|
381
|
+
passed=True,
|
|
382
|
+
message=f"{yaml_path.name} contains structured '{stem}' entries",
|
|
383
|
+
)
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
return results
|
|
387
|
+
|
|
388
|
+
|
|
319
389
|
# ---------------------------------------------------------------------------
|
|
320
390
|
# Requirement ↔ Test consistency
|
|
321
391
|
# ---------------------------------------------------------------------------
|
|
@@ -347,22 +417,13 @@ def check_req_test_consistency(root: Path) -> list[AuditResult]:
|
|
|
347
417
|
test_candidates = _get_test_spec_paths(root)
|
|
348
418
|
test_path = next((p for p in test_candidates if p.exists()), None)
|
|
349
419
|
|
|
350
|
-
if test_path is None:
|
|
351
|
-
results.append(
|
|
352
|
-
AuditResult(
|
|
353
|
-
name="req-test-consistency",
|
|
354
|
-
passed=True,
|
|
355
|
-
message="Skipped: test spec file not found",
|
|
356
|
-
)
|
|
357
|
-
)
|
|
358
|
-
return results
|
|
359
|
-
|
|
360
420
|
# In YAML-first mode load REQ IDs from the JSON machine state so we don't
|
|
361
421
|
# depend on docs/REQUIREMENTS.md being present or up to date (#174).
|
|
362
422
|
import contextlib as _contextlib
|
|
363
423
|
import json as _json_local
|
|
364
424
|
|
|
365
425
|
_reqs_json = root / ".specsmith" / "requirements.json"
|
|
426
|
+
_tests_json = root / ".specsmith" / "testcases.json"
|
|
366
427
|
_yaml_req_ids: set[str] | None = None
|
|
367
428
|
if _reqs_json.is_file():
|
|
368
429
|
with _contextlib.suppress(OSError, ValueError):
|
|
@@ -378,9 +439,18 @@ def check_req_test_consistency(root: Path) -> list[AuditResult]:
|
|
|
378
439
|
)
|
|
379
440
|
)
|
|
380
441
|
return results
|
|
442
|
+
if test_path is None and not _tests_json.is_file():
|
|
443
|
+
results.append(
|
|
444
|
+
AuditResult(
|
|
445
|
+
name="req-test-consistency",
|
|
446
|
+
passed=True,
|
|
447
|
+
message="Skipped: test spec file and testcases.json not found",
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
return results
|
|
381
451
|
|
|
382
452
|
req_text = req_path.read_text(encoding="utf-8") if req_path.exists() else ""
|
|
383
|
-
test_text = test_path.read_text(encoding="utf-8")
|
|
453
|
+
test_text = test_path.read_text(encoding="utf-8") if test_path else ""
|
|
384
454
|
|
|
385
455
|
# Only check coverage for non-Draft requirements.
|
|
386
456
|
# Draft requirements are stubs (e.g. auto-generated by import) and don't
|
|
@@ -405,7 +475,6 @@ def check_req_test_consistency(root: Path) -> list[AuditResult]:
|
|
|
405
475
|
|
|
406
476
|
# In YAML-first mode, also collect coverage from testcases.json requirement_id fields
|
|
407
477
|
# and from the test_ids list embedded in requirements.json records (#REQ-379 followup).
|
|
408
|
-
_tests_json = root / ".specsmith" / "testcases.json"
|
|
409
478
|
if _tests_json.is_file():
|
|
410
479
|
with _contextlib.suppress(OSError, ValueError):
|
|
411
480
|
_test_records = _json_local.loads(_tests_json.read_text(encoding="utf-8"))
|
|
@@ -1458,6 +1527,7 @@ def run_audit(root: Path) -> AuditReport:
|
|
|
1458
1527
|
"""Run all audit checks and return a report."""
|
|
1459
1528
|
report = AuditReport()
|
|
1460
1529
|
report.results.extend(check_governance_files(root))
|
|
1530
|
+
report.results.extend(check_governance_yaml_content(root))
|
|
1461
1531
|
report.results.extend(check_req_test_consistency(root))
|
|
1462
1532
|
report.results.extend(check_ledger_health(root))
|
|
1463
1533
|
report.results.extend(check_context_size(root))
|
|
@@ -760,6 +760,19 @@ def audit(fix: bool, project_dir: str) -> None:
|
|
|
760
760
|
"Overrides .specsmith/config.yml epistemic.confidence_threshold for this call."
|
|
761
761
|
),
|
|
762
762
|
)
|
|
763
|
+
@click.option(
|
|
764
|
+
"--req",
|
|
765
|
+
"hint_req_ids",
|
|
766
|
+
multiple=True,
|
|
767
|
+
default=(),
|
|
768
|
+
metavar="REQ-NNN",
|
|
769
|
+
help=(
|
|
770
|
+
"Inject an explicit requirement ID into the utterance (repeatable). "
|
|
771
|
+
"Useful for release/destructive intents where the broker asks for clarification: "
|
|
772
|
+
"'specsmith preflight \"update release.yml\" --req REQ-051' "
|
|
773
|
+
"appends the ID so scope-matching can accept it (REQ-431)."
|
|
774
|
+
),
|
|
775
|
+
)
|
|
763
776
|
def preflight_cmd(
|
|
764
777
|
utterance: str,
|
|
765
778
|
project_dir: str,
|
|
@@ -768,6 +781,7 @@ def preflight_cmd(
|
|
|
768
781
|
stress: bool,
|
|
769
782
|
predict_only: bool,
|
|
770
783
|
escalate_threshold: float | None,
|
|
784
|
+
hint_req_ids: tuple[str, ...],
|
|
771
785
|
) -> None:
|
|
772
786
|
"""Classify a natural-language utterance under Specsmith governance (REQ-085).
|
|
773
787
|
|
|
@@ -784,6 +798,15 @@ def preflight_cmd(
|
|
|
784
798
|
from specsmith.governance_logic import run_preflight
|
|
785
799
|
|
|
786
800
|
root = Path(project_dir).resolve()
|
|
801
|
+
|
|
802
|
+
# REQ-431: --req flags inject explicit REQ IDs into the utterance so the
|
|
803
|
+
# governance_logic explicit-ID extractor (line ~149) picks them up. This
|
|
804
|
+
# lets the user escape release/destructive needs_clarification dead-ends by
|
|
805
|
+
# naming a known requirement without having to reword the whole utterance.
|
|
806
|
+
if hint_req_ids:
|
|
807
|
+
req_suffix = " ".join(r.upper() for r in hint_req_ids if r.strip())
|
|
808
|
+
utterance = f"{utterance} [{req_suffix}]"
|
|
809
|
+
|
|
787
810
|
# REQ-418: delegate the decision to the authoritative broker so explicit
|
|
788
811
|
# REQ-NNN ids and .specsmith/requirements.json drive the result. run_preflight
|
|
789
812
|
# also applies the REQ-098 floor, allocates/persists the work item (unless
|
|
@@ -1129,6 +1152,20 @@ def verify_cmd(
|
|
|
1129
1152
|
if equilibrium and confidence >= threshold
|
|
1130
1153
|
else classify_retry_strategy(fake_report, fake_decision)
|
|
1131
1154
|
)
|
|
1155
|
+
# Wire equilibrium back to WI lifecycle (REQ-434)
|
|
1156
|
+
if equilibrium and work_item_id:
|
|
1157
|
+
try:
|
|
1158
|
+
from specsmith.governance_logic import run_verify as _gov_verify
|
|
1159
|
+
|
|
1160
|
+
_gov_verify(
|
|
1161
|
+
diff=payload_in.get("diff", ""),
|
|
1162
|
+
files_changed=files_changed,
|
|
1163
|
+
test_results=test_results,
|
|
1164
|
+
project_dir=str(root),
|
|
1165
|
+
work_item_id=work_item_id,
|
|
1166
|
+
)
|
|
1167
|
+
except Exception: # noqa: BLE001
|
|
1168
|
+
pass # best-effort; never block verify output
|
|
1132
1169
|
|
|
1133
1170
|
out = {
|
|
1134
1171
|
"equilibrium": equilibrium,
|
|
@@ -4011,9 +4048,15 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
4011
4048
|
pass
|
|
4012
4049
|
|
|
4013
4050
|
# ── ESDB ──────────────────────────────────────────────────────────────────
|
|
4014
|
-
|
|
4051
|
+
# Three possible states (REQ-429):
|
|
4052
|
+
# "chronomemory" — commercial ChronoStore backend, WAL present
|
|
4053
|
+
# "sqlite" — free SQLite backend (chronomemory absent or WAL missing)
|
|
4054
|
+
# "n/a" — no ESDB artefact found at all
|
|
4055
|
+
esdb_ok: bool = True
|
|
4056
|
+
esdb_records: int = 0
|
|
4057
|
+
esdb_backend: str = "n/a"
|
|
4015
4058
|
try:
|
|
4016
|
-
from chronomemory import ChronoStore
|
|
4059
|
+
from chronomemory import ChronoStore # noqa: PLC0415
|
|
4017
4060
|
|
|
4018
4061
|
wal = root / ".chronomemory" / "events.wal"
|
|
4019
4062
|
if wal.exists():
|
|
@@ -4023,9 +4066,24 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
4023
4066
|
# value that is not the literal False as valid.
|
|
4024
4067
|
esdb_ok = store.chain_valid() is not False
|
|
4025
4068
|
esdb_records = store.record_count()
|
|
4069
|
+
esdb_backend = "chronomemory"
|
|
4026
4070
|
except Exception: # noqa: BLE001
|
|
4027
4071
|
pass
|
|
4028
4072
|
|
|
4073
|
+
if esdb_backend == "n/a":
|
|
4074
|
+
# chronomemory not available or WAL absent — try SQLite (free default)
|
|
4075
|
+
try:
|
|
4076
|
+
from specsmith.esdb.sqlite_store import SqliteStore # noqa: PLC0415
|
|
4077
|
+
|
|
4078
|
+
db = root / ".specsmith" / "esdb.sqlite3"
|
|
4079
|
+
if db.exists():
|
|
4080
|
+
with SqliteStore(root) as store:
|
|
4081
|
+
esdb_ok = store.chain_valid()
|
|
4082
|
+
esdb_records = store.record_count()
|
|
4083
|
+
esdb_backend = "sqlite"
|
|
4084
|
+
except Exception: # noqa: BLE001
|
|
4085
|
+
pass
|
|
4086
|
+
|
|
4029
4087
|
# ── Recent work items + last preflight from LEDGER.md ─────────────────────
|
|
4030
4088
|
recent_wis: list[str] = []
|
|
4031
4089
|
last_preflight = ""
|
|
@@ -4060,8 +4118,9 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
4060
4118
|
"audit_failed": audit_failed,
|
|
4061
4119
|
"req_count": req_count,
|
|
4062
4120
|
"test_count": test_count,
|
|
4063
|
-
"
|
|
4064
|
-
"
|
|
4121
|
+
"esdb_backend": esdb_backend,
|
|
4122
|
+
"esdb_records": esdb_records if esdb_backend != "n/a" else None,
|
|
4123
|
+
"esdb_chain_valid": esdb_ok if esdb_backend != "n/a" else None,
|
|
4065
4124
|
"recent_wis": recent_wis,
|
|
4066
4125
|
"last_preflight": last_preflight,
|
|
4067
4126
|
"anchor": f"SPECSMITH-ANCHOR-{ts}",
|
|
@@ -4078,6 +4137,14 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
4078
4137
|
vbar = "\u2551" # ║
|
|
4079
4138
|
health_icon = "\u2713" if health_ok else "\u2717"
|
|
4080
4139
|
esdb_icon = "\u2713" if esdb_ok else "\u2717"
|
|
4140
|
+
if esdb_backend == "chronomemory":
|
|
4141
|
+
esdb_field = f"ESDB: {esdb_records} records ({esdb_icon} chain)"
|
|
4142
|
+
elif esdb_backend == "sqlite":
|
|
4143
|
+
# Compact format keeps the row within the 57-char box interior.
|
|
4144
|
+
# " REQs : NNN TESTs: NNN " = 30 chars, leaving 27 for esdb_field.
|
|
4145
|
+
esdb_field = f"ESDB: SQLite {esdb_records} recs {esdb_icon}"
|
|
4146
|
+
else:
|
|
4147
|
+
esdb_field = "ESDB: N/A"
|
|
4081
4148
|
wi_str = ", ".join(recent_wis) if recent_wis else "none seen"
|
|
4082
4149
|
|
|
4083
4150
|
def _arow(rich: str, plain: str, wide: int = 0) -> str:
|
|
@@ -4115,10 +4182,7 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
4115
4182
|
)
|
|
4116
4183
|
console.print(_arow(r4_rich, r4_plain))
|
|
4117
4184
|
|
|
4118
|
-
r5 =
|
|
4119
|
-
f" REQs : {req_count} TESTs: {test_count}"
|
|
4120
|
-
f" ESDB: {esdb_records} records ({esdb_icon} chain)"
|
|
4121
|
-
)
|
|
4185
|
+
r5 = f" REQs : {req_count} TESTs: {test_count} {esdb_field}"
|
|
4122
4186
|
console.print(_arow(r5, r5))
|
|
4123
4187
|
|
|
4124
4188
|
r6 = f" WIs : {wi_str}"
|
|
@@ -9225,6 +9289,30 @@ def wi_show_cmd(wi_id: str, project_dir: str, as_json: bool) -> None:
|
|
|
9225
9289
|
console.print(f" Reason : {item.closed_reason}")
|
|
9226
9290
|
|
|
9227
9291
|
|
|
9292
|
+
@wi_group.command(name="link-test")
|
|
9293
|
+
@click.argument("wi_id")
|
|
9294
|
+
@click.option(
|
|
9295
|
+
"--test",
|
|
9296
|
+
"test_ids",
|
|
9297
|
+
multiple=True,
|
|
9298
|
+
required=True,
|
|
9299
|
+
metavar="TEST-NNN",
|
|
9300
|
+
help="Test case ID to link (repeatable).",
|
|
9301
|
+
)
|
|
9302
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
9303
|
+
def wi_link_test_cmd(wi_id: str, test_ids: tuple[str, ...], project_dir: str) -> None:
|
|
9304
|
+
"""Link one or more test case IDs to a work item."""
|
|
9305
|
+
from specsmith.wi_store import WorkItemStore
|
|
9306
|
+
|
|
9307
|
+
root = Path(project_dir).resolve()
|
|
9308
|
+
store = WorkItemStore(root)
|
|
9309
|
+
item = store.add_test_case_ids(wi_id.upper(), list(test_ids))
|
|
9310
|
+
if item is None:
|
|
9311
|
+
console.print(f"[red]Work item {wi_id!r} not found.[/red]")
|
|
9312
|
+
raise SystemExit(1)
|
|
9313
|
+
console.print(f"[green]\u2713[/green] {item.id} linked tests: {', '.join(test_ids)}")
|
|
9314
|
+
|
|
9315
|
+
|
|
9228
9316
|
@wi_group.command(name="close")
|
|
9229
9317
|
@click.argument("wi_id")
|
|
9230
9318
|
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
@@ -93,6 +93,8 @@ def register_issue_policy_commands(main: click.Group, console: Any) -> None:
|
|
|
93
93
|
project_dir: str,
|
|
94
94
|
as_json: bool,
|
|
95
95
|
) -> None:
|
|
96
|
+
from specsmith.wi_store import _now_iso
|
|
97
|
+
|
|
96
98
|
record = record_approval(
|
|
97
99
|
Path(project_dir).resolve(),
|
|
98
100
|
approval_type=approval_type,
|
|
@@ -101,6 +103,16 @@ def register_issue_policy_commands(main: click.Group, console: Any) -> None:
|
|
|
101
103
|
scope=scope,
|
|
102
104
|
requirement_ids=list(requirements),
|
|
103
105
|
)
|
|
106
|
+
# Update human_review_status to 'approved' (REQ-434)
|
|
107
|
+
try:
|
|
108
|
+
store = WorkItemStore(Path(project_dir).resolve())
|
|
109
|
+
item = store.get(work_item_id)
|
|
110
|
+
if item is not None:
|
|
111
|
+
item.human_review_status = "approved"
|
|
112
|
+
item.updated_at = _now_iso()
|
|
113
|
+
store.upsert(item)
|
|
114
|
+
except Exception: # noqa: BLE001
|
|
115
|
+
pass
|
|
104
116
|
if as_json:
|
|
105
117
|
click.echo(json.dumps(record.to_dict(), indent=2))
|
|
106
118
|
else:
|
|
@@ -119,6 +119,7 @@ def run_preflight(
|
|
|
119
119
|
utterance,
|
|
120
120
|
_req_md,
|
|
121
121
|
repo_index_path=_repo_idx,
|
|
122
|
+
min_score=0.15,
|
|
122
123
|
)
|
|
123
124
|
|
|
124
125
|
requirement_ids = [r.req_id for r in scope.matched_requirements]
|
|
@@ -202,12 +203,25 @@ def run_preflight(
|
|
|
202
203
|
)
|
|
203
204
|
confidence_target = 0.9
|
|
204
205
|
elif intent == Intent.RELEASE:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
# Accept if scope is known or explicit REQ IDs are present (REQ-431).
|
|
207
|
+
# This handles release-tooling edits that survived the narrowed classifier
|
|
208
|
+
# (e.g. the user cited REQ-050 or REQ-051 explicitly, or the utterance
|
|
209
|
+
# matched a release-management requirement via token overlap).
|
|
210
|
+
if requirement_ids or scope.is_known:
|
|
211
|
+
decision_str = "accepted"
|
|
212
|
+
instruction = (
|
|
213
|
+
"Release-adjacent change under known governance scope. "
|
|
214
|
+
"Proceed under Specsmith verification."
|
|
215
|
+
)
|
|
216
|
+
confidence_target = 0.85
|
|
217
|
+
else:
|
|
218
|
+
decision_str = "needs_clarification"
|
|
219
|
+
instruction = (
|
|
220
|
+
"Release operations require explicit confirmation. "
|
|
221
|
+
"Specify the target version and channel, or cite a release-management "
|
|
222
|
+
"requirement via --req REQ-050 / --req REQ-051."
|
|
223
|
+
)
|
|
224
|
+
confidence_target = 0.9
|
|
211
225
|
elif intent == Intent.REFACTOR:
|
|
212
226
|
# Refactoring accepted only if scope is matched; unmatched refactors
|
|
213
227
|
# get needs_clarification because scope creep is the primary risk.
|
|
@@ -251,11 +265,13 @@ def run_preflight(
|
|
|
251
265
|
if cfg_threshold is not None and cfg_threshold > confidence_target:
|
|
252
266
|
confidence_target = cfg_threshold
|
|
253
267
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
268
|
+
# Allocate a WI for accepted decisions AND for needs_clarification on
|
|
269
|
+
# RELEASE/DESTRUCTIVE intents (REQ-431) so `specsmith approve --work-item`
|
|
270
|
+
# can proceed after the user provides the requested context out-of-band.
|
|
271
|
+
_alloc_wi = decision_str == "accepted" or (
|
|
272
|
+
decision_str == "needs_clarification" and intent in (Intent.RELEASE, Intent.DESTRUCTIVE)
|
|
258
273
|
)
|
|
274
|
+
work_item_id = f"WI-{uuid.uuid4().hex[:8].upper()}" if (_alloc_wi and not predict_only) else ""
|
|
259
275
|
|
|
260
276
|
payload: dict[str, Any] = {
|
|
261
277
|
"decision": decision_str,
|
|
@@ -402,7 +418,9 @@ def run_verify(
|
|
|
402
418
|
try:
|
|
403
419
|
from specsmith.wi_store import WorkItemStore
|
|
404
420
|
|
|
405
|
-
WorkItemStore(root)
|
|
421
|
+
store = WorkItemStore(root)
|
|
422
|
+
store.mark_implemented(work_item_id)
|
|
423
|
+
store.set_files_touched(work_item_id, files_changed)
|
|
406
424
|
except Exception: # noqa: BLE001
|
|
407
425
|
pass
|
|
408
426
|
|
|
@@ -40,6 +40,7 @@ class MigrationResult:
|
|
|
40
40
|
message: str = ""
|
|
41
41
|
files_created: list[str] = field(default_factory=list)
|
|
42
42
|
files_modified: list[str] = field(default_factory=list)
|
|
43
|
+
warnings: list[str] = field(default_factory=list)
|
|
43
44
|
error: str = ""
|
|
44
45
|
|
|
45
46
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -51,6 +52,7 @@ class MigrationResult:
|
|
|
51
52
|
"message": self.message,
|
|
52
53
|
"files_created": self.files_created,
|
|
53
54
|
"files_modified": self.files_modified,
|
|
55
|
+
"warnings": self.warnings,
|
|
54
56
|
"error": self.error,
|
|
55
57
|
}
|
|
56
58
|
|
{specsmith-0.18.0.dev710 → specsmith-0.19.0.dev726}/src/specsmith/migrations/m001_governance_yaml.py
RENAMED
|
@@ -49,7 +49,10 @@ class GovernanceYamlMigration(Migration):
|
|
|
49
49
|
yaml_path = gov_dir / yaml_name
|
|
50
50
|
|
|
51
51
|
if not md_path.exists():
|
|
52
|
-
|
|
52
|
+
result.warnings.append(
|
|
53
|
+
f"Missing source_md for {yaml_name}: docs/governance/{md_name}"
|
|
54
|
+
)
|
|
55
|
+
continue
|
|
53
56
|
|
|
54
57
|
if yaml_path.exists():
|
|
55
58
|
continue # Already migrated — skip
|
|
@@ -364,3 +364,31 @@ class MetricsStore:
|
|
|
364
364
|
"since": since,
|
|
365
365
|
"until": until,
|
|
366
366
|
}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def flush_session_metrics(
|
|
370
|
+
root: Path,
|
|
371
|
+
*,
|
|
372
|
+
work_item_id: str = "",
|
|
373
|
+
model: str = "",
|
|
374
|
+
input_tokens: int = 0,
|
|
375
|
+
output_tokens: int = 0,
|
|
376
|
+
cost_usd: float = 0.0,
|
|
377
|
+
command: str = "chat",
|
|
378
|
+
) -> None:
|
|
379
|
+
"""Append a MetricsRecord from LLM usage data captured in runner.py (REQ-436).
|
|
380
|
+
Best-effort: never raises."""
|
|
381
|
+
try:
|
|
382
|
+
store = MetricsStore(root)
|
|
383
|
+
rec = MetricsRecord.new(
|
|
384
|
+
command=command,
|
|
385
|
+
passed=True,
|
|
386
|
+
work_item_id=work_item_id,
|
|
387
|
+
model=model,
|
|
388
|
+
input_tokens=input_tokens,
|
|
389
|
+
output_tokens=output_tokens,
|
|
390
|
+
cost_usd=cost_usd,
|
|
391
|
+
)
|
|
392
|
+
store.append(rec)
|
|
393
|
+
except Exception: # noqa: BLE001
|
|
394
|
+
pass
|
|
@@ -24,7 +24,7 @@ from __future__ import annotations
|
|
|
24
24
|
import contextlib
|
|
25
25
|
import json
|
|
26
26
|
import re
|
|
27
|
-
from dataclasses import dataclass
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
28
|
from pathlib import Path
|
|
29
29
|
from typing import Any, Protocol, cast
|
|
30
30
|
|
|
@@ -182,6 +182,11 @@ def parse_tests_md(text: str) -> list[dict[str, Any]]:
|
|
|
182
182
|
# ---------------------------------------------------------------------------
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
@dataclass
|
|
186
|
+
class SyncWarning:
|
|
187
|
+
message: str
|
|
188
|
+
|
|
189
|
+
|
|
185
190
|
@dataclass
|
|
186
191
|
class SyncResult:
|
|
187
192
|
reqs_before: int
|
|
@@ -191,6 +196,7 @@ class SyncResult:
|
|
|
191
196
|
reqs_changed: bool
|
|
192
197
|
tests_changed: bool
|
|
193
198
|
dry_run: bool
|
|
199
|
+
warnings: list[SyncWarning] = field(default_factory=list)
|
|
194
200
|
|
|
195
201
|
@property
|
|
196
202
|
def changed(self) -> bool:
|
|
@@ -436,6 +442,7 @@ def run_sync(root: Path, *, dry_run: bool = False) -> SyncResult:
|
|
|
436
442
|
tests_md_path = root / "docs" / "TESTS.md"
|
|
437
443
|
reqs_json_path = state_dir / "requirements.json"
|
|
438
444
|
tests_json_path = state_dir / "testcases.json"
|
|
445
|
+
sync_warnings: list[SyncWarning] = []
|
|
439
446
|
if not dry_run:
|
|
440
447
|
normalize_esdb_gitignore_policy(root)
|
|
441
448
|
|
|
@@ -443,6 +450,8 @@ def run_sync(root: Path, *, dry_run: bool = False) -> SyncResult:
|
|
|
443
450
|
# ── YAML-first mode ─────────────────────────────────────────────────
|
|
444
451
|
new_reqs = load_yaml_requirements(root)
|
|
445
452
|
new_tests = load_yaml_tests(root)
|
|
453
|
+
yaml_req_ids = {str(r["id"]) for r in new_reqs if str(r.get("id", "")).strip()}
|
|
454
|
+
yaml_test_ids = {str(t["id"]) for t in new_tests if str(t.get("id", "")).strip()}
|
|
446
455
|
|
|
447
456
|
# REQ-358: If YAML mode has no YAML files but REQUIREMENTS.md has content,
|
|
448
457
|
# fall back to Markdown parsing rather than silently producing an empty state.
|
|
@@ -499,6 +508,36 @@ def run_sync(root: Path, *, dry_run: bool = False) -> SyncResult:
|
|
|
499
508
|
}
|
|
500
509
|
for t in new_tests
|
|
501
510
|
]
|
|
511
|
+
if reqs_md_path.exists():
|
|
512
|
+
reqs_md_ids = {
|
|
513
|
+
r["id"]
|
|
514
|
+
for r in parse_requirements_md(reqs_md_path.read_text(encoding="utf-8"))
|
|
515
|
+
if str(r.get("id", "")).strip()
|
|
516
|
+
}
|
|
517
|
+
for req_id in sorted(reqs_md_ids - yaml_req_ids):
|
|
518
|
+
sync_warnings.append(
|
|
519
|
+
SyncWarning(
|
|
520
|
+
message=(
|
|
521
|
+
f"{req_id} found in docs/REQUIREMENTS.md but missing from "
|
|
522
|
+
"docs/requirements/*.yml — add it to a YAML file and re-run sync."
|
|
523
|
+
)
|
|
524
|
+
)
|
|
525
|
+
)
|
|
526
|
+
if tests_md_path.exists():
|
|
527
|
+
tests_md_ids = {
|
|
528
|
+
t["id"]
|
|
529
|
+
for t in parse_tests_md(tests_md_path.read_text(encoding="utf-8"))
|
|
530
|
+
if str(t.get("id", "")).strip()
|
|
531
|
+
}
|
|
532
|
+
for test_id in sorted(tests_md_ids - yaml_test_ids):
|
|
533
|
+
sync_warnings.append(
|
|
534
|
+
SyncWarning(
|
|
535
|
+
message=(
|
|
536
|
+
f"{test_id} found in docs/TESTS.md but missing from "
|
|
537
|
+
"docs/tests/*.yml — add it to a YAML file and re-run sync."
|
|
538
|
+
)
|
|
539
|
+
)
|
|
540
|
+
)
|
|
502
541
|
else:
|
|
503
542
|
# ── Legacy Markdown mode (DEPRECATED — REQ-373) ──────────────────────
|
|
504
543
|
# Markdown mode will be removed in a future release.
|
|
@@ -614,6 +653,7 @@ def run_sync(root: Path, *, dry_run: bool = False) -> SyncResult:
|
|
|
614
653
|
reqs_changed=reqs_changed,
|
|
615
654
|
tests_changed=tests_changed,
|
|
616
655
|
dry_run=dry_run,
|
|
656
|
+
warnings=sync_warnings,
|
|
617
657
|
)
|
|
618
658
|
|
|
619
659
|
|