specsmith 0.17.1.dev667__tar.gz → 0.17.1.dev669__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.17.1.dev667/src/specsmith.egg-info → specsmith-0.17.1.dev669}/PKG-INFO +1 -1
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/pyproject.toml +1 -1
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/context_seed.py +134 -3
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/runner.py +17 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/cli.py +292 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/context_orchestrator.py +75 -4
- specsmith-0.17.1.dev669/src/specsmith/efficiency.py +293 -0
- specsmith-0.17.1.dev669/src/specsmith/esdb_sweep.py +225 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb_writer.py +202 -2
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ledger.py +17 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/__init__.py +2 -0
- specsmith-0.17.1.dev669/src/specsmith/migrations/m009_esdb_first.py +257 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/project_metrics.py +7 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/serve.py +11 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/agents.md.j2 +6 -2
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/trace.py +7 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs_commands.py +25 -1
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669/src/specsmith.egg-info}/PKG-INFO +1 -1
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/SOURCES.txt +7 -0
- specsmith-0.17.1.dev669/tests/test_dual_write.py +268 -0
- specsmith-0.17.1.dev669/tests/test_efficiency.py +246 -0
- specsmith-0.17.1.dev669/tests/test_esdb_sweep.py +267 -0
- specsmith-0.17.1.dev669/tests/test_resume_command.py +188 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/LICENSE +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/README.md +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/setup.cfg +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/belief.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/certainty.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/py.typed +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/recovery.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/session.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/trace.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/__main__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_router.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/approvals.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/architect.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/auditor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/auth.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/block_export.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/channel.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/issues_policy.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/reporting.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compressor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/config.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/context_window.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/credits.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/dashboard.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/differ.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/doctor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/drive.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/editor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/_license.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/sqlite_store.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/executor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/exporter.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_logic.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_yaml.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governed_pr.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/history_search.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/importer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/instinct.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/languages.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/local_model.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/mcp_server.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m007_yaml_first.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m008_esdb_full_coverage.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/patent.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/paths.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/phase.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/plugins.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/policy.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/profiles.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/quality_report.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/recover.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/releaser.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/requirements.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/risk.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session_init.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session_store.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/ai_agents.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/data_engineering.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/governance.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/platform_engineering.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/software_engineering.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/specsmith_skills.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/web_backend.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/sync.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/tools.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/transcripts.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/updater.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/validator.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/wi_store.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/workspace.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_CMD_001.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_run_feedback.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_ai_client.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architect_interview.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architect_v2.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architecture.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_auditor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_channel.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cleanup_cmd.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli_regressions.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compliance.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compliance_governance.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compressor.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_dispatch.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_epistemic.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_backend_switch.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_enforcement.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_integration.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_license.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_sqlite.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_verify_chain_cli.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_writer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_golden_path.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_importer.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_init_modes.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_integrations.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_intelligence.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issue_policy_features.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issues_263_264.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_local_model.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_markdown_deprecation.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_mcp_client.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_mcp_server.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_migration_direction.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_model_router.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_new_modules.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_nexus.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_permissions.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_project_metrics.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_quality_report.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_rate_limits.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_repl.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_reporting_commands.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_req_248_262.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_req_358_359.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_scaffolder.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_schema_migrations.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_smoke.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_suggester.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_tools.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_typing_guardrails.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_validator.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_vcs.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_warp_parity.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_warp_parity_followup.py +0 -0
- {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_wi_lifecycle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.17.1.
|
|
3
|
+
Version: 0.17.1.dev669
|
|
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.17.1.
|
|
7
|
+
version = "0.17.1.dev669"
|
|
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"
|
|
@@ -27,7 +27,9 @@ left off (extended to cover full epistemic continuity).
|
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
29
|
import json
|
|
30
|
+
import uuid
|
|
30
31
|
from collections.abc import Sequence
|
|
32
|
+
from datetime import datetime, timezone
|
|
31
33
|
from pathlib import Path
|
|
32
34
|
from typing import Any
|
|
33
35
|
|
|
@@ -62,6 +64,10 @@ def build_context_seed(
|
|
|
62
64
|
records (failed approaches, stale decisions, low-confidence hypotheses) are
|
|
63
65
|
excluded from the context window.
|
|
64
66
|
|
|
67
|
+
REQ-407: ESDB-first ledger snippet (falls back to LEDGER.md).
|
|
68
|
+
REQ-415: auto-tune seed parameters from EFF-CURRENT.
|
|
69
|
+
REQ-416: write context_usage record per build_context_seed() call.
|
|
70
|
+
|
|
65
71
|
Args:
|
|
66
72
|
project_dir: Project root containing ``.specsmith/``.
|
|
67
73
|
char_budget: Maximum total characters across all seed turns.
|
|
@@ -75,9 +81,21 @@ def build_context_seed(
|
|
|
75
81
|
Empty list if no prior context is available.
|
|
76
82
|
"""
|
|
77
83
|
root = Path(project_dir).resolve()
|
|
84
|
+
session_id = uuid.uuid4().hex[:16].upper()
|
|
78
85
|
turns: list[dict[str, Any]] = []
|
|
79
86
|
used = 0
|
|
80
87
|
|
|
88
|
+
# Auto-tune parameters from EFF-CURRENT (REQ-415)
|
|
89
|
+
eff = _load_efficiency_params(root)
|
|
90
|
+
if eff.get("degraded"):
|
|
91
|
+
# Reduce budget when context is degraded to force tighter seeds
|
|
92
|
+
char_budget = int(char_budget * 0.75)
|
|
93
|
+
if eff.get("context_char_efficiency") is not None:
|
|
94
|
+
# Scale max_ledger based on fill efficiency: high fill → fewer ledger lines
|
|
95
|
+
fill_eff = float(eff["context_char_efficiency"])
|
|
96
|
+
if fill_eff > 0.9:
|
|
97
|
+
max_ledger = max(10, max_ledger // 2)
|
|
98
|
+
|
|
81
99
|
# ── 1. Session state snapshot ─────────────────────────────────────
|
|
82
100
|
session_summary = _load_session_summary(root)
|
|
83
101
|
if session_summary:
|
|
@@ -95,7 +113,7 @@ def build_context_seed(
|
|
|
95
113
|
turns.append(t)
|
|
96
114
|
used += len(c)
|
|
97
115
|
|
|
98
|
-
# ── 3. Recent LEDGER entries
|
|
116
|
+
# ── 3. Recent LEDGER entries — ESDB-first (REQ-407) ─────────────────
|
|
99
117
|
ledger_block = _load_ledger_snippet(root, max_lines=max_ledger)
|
|
100
118
|
if ledger_block and used + len(ledger_block) <= char_budget:
|
|
101
119
|
turns.append(
|
|
@@ -106,7 +124,22 @@ def build_context_seed(
|
|
|
106
124
|
)
|
|
107
125
|
used += len(ledger_block)
|
|
108
126
|
|
|
109
|
-
# ── 4.
|
|
127
|
+
# ── 4. Efficiency degradation warning ────────────────────────────────
|
|
128
|
+
if eff.get("degraded"):
|
|
129
|
+
eq_score = eff.get("epistemic_quality_score", 0.0)
|
|
130
|
+
tpc = eff.get("tokens_per_correct_answer")
|
|
131
|
+
baseline = eff.get("baseline_tokens_per_pass")
|
|
132
|
+
warn_parts = ["[EFFICIENCY WARNING: context degraded"]
|
|
133
|
+
if tpc is not None and baseline is not None:
|
|
134
|
+
warn_parts.append(f"tokens/pass={tpc:.0f} vs baseline={baseline:.0f}")
|
|
135
|
+
if eq_score:
|
|
136
|
+
warn_parts.append(f"epistemic_quality={eq_score:.2f}")
|
|
137
|
+
warn_parts.append("— consider specsmith esdb sweep]")
|
|
138
|
+
warn_content = " ".join(warn_parts)
|
|
139
|
+
turns.append({"role": "system", "content": warn_content})
|
|
140
|
+
used += len(warn_content)
|
|
141
|
+
|
|
142
|
+
# ── 5. ESDB records — relevance-based by kind (REQ-399) ──────────────
|
|
110
143
|
esdb_block = _load_esdb_by_kind(
|
|
111
144
|
root,
|
|
112
145
|
max_preflight=max_preflight,
|
|
@@ -123,6 +156,17 @@ def build_context_seed(
|
|
|
123
156
|
),
|
|
124
157
|
}
|
|
125
158
|
)
|
|
159
|
+
used += len(esdb_block)
|
|
160
|
+
|
|
161
|
+
# ── 6. Write context_usage record (REQ-416) ──────────────────────────
|
|
162
|
+
_write_context_usage(
|
|
163
|
+
root,
|
|
164
|
+
session_id=session_id,
|
|
165
|
+
seed_chars=used,
|
|
166
|
+
seed_fill_pct=round(used / max(1, char_budget) * 100.0, 1),
|
|
167
|
+
turns_count=len(turns),
|
|
168
|
+
degraded=bool(eff.get("degraded")),
|
|
169
|
+
)
|
|
126
170
|
|
|
127
171
|
return turns
|
|
128
172
|
|
|
@@ -192,7 +236,34 @@ def _load_conversation_history(
|
|
|
192
236
|
|
|
193
237
|
|
|
194
238
|
def _load_ledger_snippet(root: Path, *, max_lines: int) -> str:
|
|
195
|
-
"""Return the last ``max_lines`` of
|
|
239
|
+
"""Return the last ``max_lines`` of ledger entries — ESDB-first (REQ-407).
|
|
240
|
+
|
|
241
|
+
Query order:
|
|
242
|
+
1. ESDB ``ledger_event`` records (sorted by timestamp desc).
|
|
243
|
+
2. Fall back to LEDGER.md file if ESDB has no ledger_event records.
|
|
244
|
+
"""
|
|
245
|
+
# 1. Try ESDB first
|
|
246
|
+
try:
|
|
247
|
+
from specsmith.esdb import SqliteStore
|
|
248
|
+
|
|
249
|
+
sqlite_path = root / ".specsmith" / "esdb.sqlite3"
|
|
250
|
+
if sqlite_path.exists():
|
|
251
|
+
with SqliteStore(root) as store:
|
|
252
|
+
records = store.query(kind="ledger_event", status="active")
|
|
253
|
+
if records:
|
|
254
|
+
# Sort newest first by timestamp in data, then take last max_lines
|
|
255
|
+
def _ts(r: Any) -> str:
|
|
256
|
+
return str(r.data.get("timestamp") or "")
|
|
257
|
+
|
|
258
|
+
sorted_recs = sorted(records, key=_ts, reverse=True)[:max_lines]
|
|
259
|
+
lines_out = [
|
|
260
|
+
f"{r.data.get('timestamp', '')[:16]} — {r.label[:120]}" for r in sorted_recs
|
|
261
|
+
]
|
|
262
|
+
return "\n".join(lines_out)
|
|
263
|
+
except Exception: # noqa: BLE001
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
# 2. Fall back to LEDGER.md file
|
|
196
267
|
try:
|
|
197
268
|
candidates = [root / "LEDGER.md", root / "docs" / "LEDGER.md"]
|
|
198
269
|
for path in candidates:
|
|
@@ -310,4 +381,64 @@ def _load_esdb_by_kind(
|
|
|
310
381
|
return "\n".join(lines)
|
|
311
382
|
|
|
312
383
|
|
|
384
|
+
def _load_efficiency_params(root: Path) -> dict[str, Any]:
|
|
385
|
+
"""Load EFF-CURRENT from ESDB and return its data dict (REQ-415).
|
|
386
|
+
|
|
387
|
+
Returns an empty dict on any error (best-effort).
|
|
388
|
+
"""
|
|
389
|
+
try:
|
|
390
|
+
from specsmith.esdb import SqliteStore
|
|
391
|
+
|
|
392
|
+
sqlite_path = root / ".specsmith" / "esdb.sqlite3"
|
|
393
|
+
if not sqlite_path.exists():
|
|
394
|
+
return {}
|
|
395
|
+
with SqliteStore(root) as store:
|
|
396
|
+
rec = store.get("EFF-CURRENT")
|
|
397
|
+
if rec is None:
|
|
398
|
+
return {}
|
|
399
|
+
data = dict(rec.data)
|
|
400
|
+
# Flatten epistemic_quality score for convenience
|
|
401
|
+
eq = data.get("epistemic_quality")
|
|
402
|
+
if isinstance(eq, dict):
|
|
403
|
+
data["epistemic_quality_score"] = eq.get("score", 0.0)
|
|
404
|
+
return data
|
|
405
|
+
except Exception: # noqa: BLE001
|
|
406
|
+
return {}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _write_context_usage(
|
|
410
|
+
root: Path,
|
|
411
|
+
*,
|
|
412
|
+
session_id: str,
|
|
413
|
+
seed_chars: int,
|
|
414
|
+
seed_fill_pct: float,
|
|
415
|
+
turns_count: int,
|
|
416
|
+
degraded: bool,
|
|
417
|
+
) -> None:
|
|
418
|
+
"""Write a context_usage record to ESDB (REQ-416, best-effort)."""
|
|
419
|
+
try:
|
|
420
|
+
from specsmith.esdb import SqliteRecord, open_default_store
|
|
421
|
+
|
|
422
|
+
rec_id = f"CTX-{session_id}"
|
|
423
|
+
record = SqliteRecord(
|
|
424
|
+
id=rec_id,
|
|
425
|
+
kind="context_usage",
|
|
426
|
+
status="active",
|
|
427
|
+
label=f"seed session={session_id} fill={seed_fill_pct:.1f}%",
|
|
428
|
+
confidence=1.0,
|
|
429
|
+
data={
|
|
430
|
+
"session_id": session_id,
|
|
431
|
+
"seed_chars": seed_chars,
|
|
432
|
+
"seed_fill_pct": seed_fill_pct,
|
|
433
|
+
"turns_count": turns_count,
|
|
434
|
+
"degraded_at_seed": degraded,
|
|
435
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
436
|
+
},
|
|
437
|
+
)
|
|
438
|
+
with open_default_store(root, warn=False) as store:
|
|
439
|
+
store.upsert(record)
|
|
440
|
+
except Exception: # noqa: BLE001
|
|
441
|
+
pass
|
|
442
|
+
|
|
443
|
+
|
|
313
444
|
__all__ = ["build_context_seed"]
|
|
@@ -604,6 +604,23 @@ class AgentRunner:
|
|
|
604
604
|
)
|
|
605
605
|
self._state.elapsed_minutes = round((time.time() - self._started_at) / 60.0, 2)
|
|
606
606
|
|
|
607
|
+
# Write token_metric to ESDB (REQ-410) — best-effort, never blocks.
|
|
608
|
+
if tokens_in or tokens_out:
|
|
609
|
+
try:
|
|
610
|
+
from specsmith.esdb_writer import write_token_metric
|
|
611
|
+
|
|
612
|
+
write_token_metric(
|
|
613
|
+
self.project_dir,
|
|
614
|
+
input_tokens=tokens_in,
|
|
615
|
+
output_tokens=tokens_out,
|
|
616
|
+
cost_usd=cost_usd,
|
|
617
|
+
model=str(getattr(result, "provider", "") if result else ""),
|
|
618
|
+
command_source=activity or "chat",
|
|
619
|
+
work_item_id=str(getattr(self._state, "active_work_item_id", "") or ""),
|
|
620
|
+
)
|
|
621
|
+
except Exception: # noqa: BLE001
|
|
622
|
+
pass
|
|
623
|
+
|
|
607
624
|
if result is not None:
|
|
608
625
|
self._history.append({"role": "user", "text": utterance})
|
|
609
626
|
self._history.append({"role": "agent", "text": result.summary})
|
|
@@ -2698,6 +2698,105 @@ def ledger_stats(project_dir: str) -> None:
|
|
|
2698
2698
|
console.print(f" {name}: {count} entries")
|
|
2699
2699
|
|
|
2700
2700
|
|
|
2701
|
+
@ledger.command(name="export")
|
|
2702
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
2703
|
+
@click.option(
|
|
2704
|
+
"--since",
|
|
2705
|
+
default="",
|
|
2706
|
+
help="Filter to entries since date prefix, e.g. '2026-06-01'.",
|
|
2707
|
+
)
|
|
2708
|
+
@click.option(
|
|
2709
|
+
"--format",
|
|
2710
|
+
"fmt",
|
|
2711
|
+
default="text",
|
|
2712
|
+
type=click.Choice(["text", "json"]),
|
|
2713
|
+
help="Output format (default: text).",
|
|
2714
|
+
)
|
|
2715
|
+
@click.option("--json", "as_json", is_flag=True, default=False, help="Shorthand for --format json.")
|
|
2716
|
+
@click.option(
|
|
2717
|
+
"--source",
|
|
2718
|
+
default="esdb",
|
|
2719
|
+
type=click.Choice(["esdb", "file", "both"]),
|
|
2720
|
+
help="Read from ESDB ledger_event records, LEDGER.md file, or both (default: esdb).",
|
|
2721
|
+
)
|
|
2722
|
+
def ledger_export(
|
|
2723
|
+
project_dir: str,
|
|
2724
|
+
since: str,
|
|
2725
|
+
fmt: str,
|
|
2726
|
+
as_json: bool,
|
|
2727
|
+
source: str,
|
|
2728
|
+
) -> None:
|
|
2729
|
+
"""Export ledger entries from ESDB or LEDGER.md.
|
|
2730
|
+
|
|
2731
|
+
ESDB source (default) queries ledger_event records inserted by recent
|
|
2732
|
+
specsmith add-entry calls, which is faster than parsing LEDGER.md.
|
|
2733
|
+
|
|
2734
|
+
REQ-409: specsmith ledger export command.
|
|
2735
|
+
"""
|
|
2736
|
+
import json as _json
|
|
2737
|
+
|
|
2738
|
+
if as_json:
|
|
2739
|
+
fmt = "json"
|
|
2740
|
+
|
|
2741
|
+
root = Path(project_dir).resolve()
|
|
2742
|
+
entries: list[dict[str, Any]] = []
|
|
2743
|
+
|
|
2744
|
+
if source in ("esdb", "both"):
|
|
2745
|
+
try:
|
|
2746
|
+
from specsmith.esdb import SqliteStore
|
|
2747
|
+
|
|
2748
|
+
sqlite_path = root / ".specsmith" / "esdb.sqlite3"
|
|
2749
|
+
if sqlite_path.exists():
|
|
2750
|
+
with SqliteStore(root) as store:
|
|
2751
|
+
records = store.query(kind="ledger_event", status="active")
|
|
2752
|
+
for r in records:
|
|
2753
|
+
ts = str(r.data.get("timestamp") or "")
|
|
2754
|
+
if since and ts[: len(since)] < since:
|
|
2755
|
+
continue
|
|
2756
|
+
entries.append(
|
|
2757
|
+
{
|
|
2758
|
+
"id": r.id,
|
|
2759
|
+
"timestamp": ts,
|
|
2760
|
+
"description": str(r.data.get("description") or r.label),
|
|
2761
|
+
"entry_type": str(r.data.get("entry_type") or ""),
|
|
2762
|
+
"author": str(r.data.get("author") or ""),
|
|
2763
|
+
"reqs": str(r.data.get("reqs") or ""),
|
|
2764
|
+
"status": str(r.data.get("status") or ""),
|
|
2765
|
+
"source": "esdb",
|
|
2766
|
+
}
|
|
2767
|
+
)
|
|
2768
|
+
except Exception as exc: # noqa: BLE001
|
|
2769
|
+
console.print(f"[yellow]ESDB read failed: {exc}[/yellow]")
|
|
2770
|
+
|
|
2771
|
+
if source in ("file", "both"):
|
|
2772
|
+
try:
|
|
2773
|
+
from specsmith.ledger import list_entries
|
|
2774
|
+
|
|
2775
|
+
file_entries = list_entries(root, since=since)
|
|
2776
|
+
for e in file_entries:
|
|
2777
|
+
entries.append({**e, "source": "LEDGER.md"})
|
|
2778
|
+
except Exception as exc: # noqa: BLE001
|
|
2779
|
+
console.print(f"[yellow]LEDGER.md read failed: {exc}[/yellow]")
|
|
2780
|
+
|
|
2781
|
+
if not entries:
|
|
2782
|
+
console.print("[yellow]No ledger entries found.[/yellow]")
|
|
2783
|
+
return
|
|
2784
|
+
|
|
2785
|
+
# Sort by timestamp
|
|
2786
|
+
entries.sort(key=lambda e: str(e.get("timestamp") or ""))
|
|
2787
|
+
|
|
2788
|
+
if fmt == "json":
|
|
2789
|
+
click.echo(_json.dumps(entries, indent=2, ensure_ascii=False))
|
|
2790
|
+
else:
|
|
2791
|
+
for e in entries:
|
|
2792
|
+
ts = str(e.get("timestamp") or "")[:16]
|
|
2793
|
+
desc = str(e.get("description") or e.get("heading") or "")
|
|
2794
|
+
author = str(e.get("author") or "")
|
|
2795
|
+
suffix = f" [dim]({author})[/dim]" if author else ""
|
|
2796
|
+
console.print(f" [dim]{ts}[/dim] {desc}{suffix}")
|
|
2797
|
+
console.print(f"\n [bold]{len(entries)} entry(ies)[/bold]")
|
|
2798
|
+
|
|
2799
|
+
|
|
2701
2800
|
main.add_command(ledger)
|
|
2702
2801
|
|
|
2703
2802
|
|
|
@@ -3348,6 +3447,120 @@ def save_cmd(project_dir: str, message: str, no_push: bool, force: bool, as_json
|
|
|
3348
3447
|
raise SystemExit(1)
|
|
3349
3448
|
|
|
3350
3449
|
|
|
3450
|
+
@main.command(name="inspect")
|
|
3451
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
3452
|
+
@click.option("--json", "as_json", is_flag=True, default=False)
|
|
3453
|
+
def inspect_cmd(project_dir: str, as_json: bool) -> None:
|
|
3454
|
+
"""Inspect project state: governance anchor, active WIs, efficiency, and context quality.
|
|
3455
|
+
|
|
3456
|
+
Designed to be called at the start of every session after checkpoint.
|
|
3457
|
+
Outputs a bordered block with project state suitable for agent context injection.
|
|
3458
|
+
|
|
3459
|
+
REQ-409: specsmith inspect command.
|
|
3460
|
+
"""
|
|
3461
|
+
import json as _json
|
|
3462
|
+
from datetime import datetime, timezone
|
|
3463
|
+
|
|
3464
|
+
root = Path(project_dir).resolve()
|
|
3465
|
+
lines: list[str] = []
|
|
3466
|
+
data: dict[str, Any] = {}
|
|
3467
|
+
|
|
3468
|
+
# Governance anchor
|
|
3469
|
+
try:
|
|
3470
|
+
from specsmith.auditor import run_audit
|
|
3471
|
+
|
|
3472
|
+
audit_report = run_audit(root)
|
|
3473
|
+
data["audit_healthy"] = audit_report.healthy
|
|
3474
|
+
data["audit_failed"] = audit_report.failed
|
|
3475
|
+
gov_status = "healthy" if audit_report.healthy else f"ISSUES ({audit_report.failed})"
|
|
3476
|
+
lines.append(f" Governance : {gov_status} ")
|
|
3477
|
+
except Exception: # noqa: BLE001
|
|
3478
|
+
data["audit_healthy"] = None
|
|
3479
|
+
lines.append(" Governance : unknown (audit failed)")
|
|
3480
|
+
|
|
3481
|
+
# Active work items
|
|
3482
|
+
try:
|
|
3483
|
+
from specsmith.wi_store import WorkItemStore
|
|
3484
|
+
|
|
3485
|
+
wi_store = WorkItemStore(root)
|
|
3486
|
+
active_wis = [wi for wi in wi_store.list_all() if wi.status in ("open", "implemented")]
|
|
3487
|
+
data["active_work_items"] = [w.id for w in active_wis]
|
|
3488
|
+
if active_wis:
|
|
3489
|
+
for wi in active_wis[:5]:
|
|
3490
|
+
lines.append(f" WI : [{wi.id}] {str(wi.intent or '')[:60]} ({wi.status})")
|
|
3491
|
+
else:
|
|
3492
|
+
lines.append(" WI : no active work items")
|
|
3493
|
+
except Exception: # noqa: BLE001
|
|
3494
|
+
data["active_work_items"] = []
|
|
3495
|
+
lines.append(" WI : (unavailable)")
|
|
3496
|
+
|
|
3497
|
+
# EFF-CURRENT
|
|
3498
|
+
try:
|
|
3499
|
+
from specsmith.esdb import SqliteStore
|
|
3500
|
+
|
|
3501
|
+
sqlite_path = root / ".specsmith" / "esdb.sqlite3"
|
|
3502
|
+
eff_data: dict[str, Any] = {}
|
|
3503
|
+
if sqlite_path.exists():
|
|
3504
|
+
with SqliteStore(root) as store:
|
|
3505
|
+
eff_rec = store.get("EFF-CURRENT")
|
|
3506
|
+
if eff_rec:
|
|
3507
|
+
eff_data = dict(eff_rec.data)
|
|
3508
|
+
|
|
3509
|
+
if eff_data:
|
|
3510
|
+
tpc = eff_data.get("tokens_per_correct_answer")
|
|
3511
|
+
degraded = eff_data.get("degraded", False)
|
|
3512
|
+
ctx_eff = eff_data.get("context_char_efficiency")
|
|
3513
|
+
eq = eff_data.get("epistemic_quality") or {}
|
|
3514
|
+
eq_score = float(eq.get("score", 0.0)) if isinstance(eq, dict) else 0.0
|
|
3515
|
+
data["efficiency"] = eff_data
|
|
3516
|
+
tok_str = f"{tpc:.0f}" if tpc else "n/a"
|
|
3517
|
+
deg_str = " ⚠ DEGRADED" if degraded else ""
|
|
3518
|
+
lines.append(
|
|
3519
|
+
f" Efficiency : tokens/pass={tok_str} ctx_fill={ctx_eff or 'n/a'}{deg_str}"
|
|
3520
|
+
)
|
|
3521
|
+
lines.append(f" Epistemic : score={eq_score:.2f} ({_band(eq_score)})")
|
|
3522
|
+
# 5-dim breakdown
|
|
3523
|
+
for dim, key in [
|
|
3524
|
+
("confidence", "confidence_density"),
|
|
3525
|
+
("recency", "recency_score"),
|
|
3526
|
+
("coherence", "coherence_score"),
|
|
3527
|
+
("closure", "closure_score"),
|
|
3528
|
+
("non-contr", "non_contradiction_score"),
|
|
3529
|
+
]:
|
|
3530
|
+
val = eq.get(key, 0.0) if isinstance(eq, dict) else 0.0
|
|
3531
|
+
lines.append(f" {dim:12s}: {val:.3f}")
|
|
3532
|
+
else:
|
|
3533
|
+
lines.append(" Efficiency : EFF-CURRENT not yet computed (run specsmith save)")
|
|
3534
|
+
except Exception: # noqa: BLE001
|
|
3535
|
+
lines.append(" Efficiency : (unavailable)")
|
|
3536
|
+
|
|
3537
|
+
data["timestamp"] = datetime.now(timezone.utc).isoformat()
|
|
3538
|
+
|
|
3539
|
+
if as_json:
|
|
3540
|
+
click.echo(_json.dumps(data, indent=2, ensure_ascii=False))
|
|
3541
|
+
return
|
|
3542
|
+
|
|
3543
|
+
width = 60
|
|
3544
|
+
border = "═" * width
|
|
3545
|
+
console.print(f"[bold]╬ {border}[/bold]")
|
|
3546
|
+
console.print(f" specsmith inspect \u2014 {root}")
|
|
3547
|
+
console.print(f" {datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%MZ')}")
|
|
3548
|
+
console.print(f"[bold]╪ {border}[/bold]")
|
|
3549
|
+
for line in lines:
|
|
3550
|
+
console.print(line)
|
|
3551
|
+
console.print(f"[bold]╩ {border}[/bold]")
|
|
3552
|
+
|
|
3553
|
+
|
|
3554
|
+
def _band(score: float) -> str:
|
|
3555
|
+
if score >= 0.85:
|
|
3556
|
+
return "excellent"
|
|
3557
|
+
if score >= 0.70:
|
|
3558
|
+
return "good"
|
|
3559
|
+
if score >= 0.50:
|
|
3560
|
+
return "fair"
|
|
3561
|
+
return "poor"
|
|
3562
|
+
|
|
3563
|
+
|
|
3351
3564
|
@main.command(name="load")
|
|
3352
3565
|
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
3353
3566
|
@click.option(
|
|
@@ -12145,6 +12358,85 @@ def esdb_switch_backend_cmd(project_dir: str, target_backend: str, confirm_data_
|
|
|
12145
12358
|
)
|
|
12146
12359
|
|
|
12147
12360
|
|
|
12361
|
+
@esdb_group.command(name="sweep")
|
|
12362
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
12363
|
+
@click.option(
|
|
12364
|
+
"--orphans-only",
|
|
12365
|
+
is_flag=True,
|
|
12366
|
+
default=False,
|
|
12367
|
+
help="Only remove orphaned work_item / preflight_decision records (skip retention sweep).",
|
|
12368
|
+
)
|
|
12369
|
+
@click.option(
|
|
12370
|
+
"--dry-run",
|
|
12371
|
+
is_flag=True,
|
|
12372
|
+
default=False,
|
|
12373
|
+
help="Show what would be swept without writing.",
|
|
12374
|
+
)
|
|
12375
|
+
@click.option("--json", "as_json", is_flag=True, default=False)
|
|
12376
|
+
def esdb_sweep_cmd(
|
|
12377
|
+
project_dir: str,
|
|
12378
|
+
orphans_only: bool,
|
|
12379
|
+
dry_run: bool,
|
|
12380
|
+
as_json: bool,
|
|
12381
|
+
) -> None:
|
|
12382
|
+
"""Sweep expired and orphaned ESDB records (REQ-412).
|
|
12383
|
+
|
|
12384
|
+
Tombstones records whose retention period has expired and removes
|
|
12385
|
+
orphaned work_item / preflight_decision records. Refreshes EFF-CURRENT
|
|
12386
|
+
after a full sweep.
|
|
12387
|
+
|
|
12388
|
+
\b
|
|
12389
|
+
Retention periods:
|
|
12390
|
+
session_metric 60 days
|
|
12391
|
+
context_usage 30 days
|
|
12392
|
+
ledger_event 90 days
|
|
12393
|
+
seal_record forever
|
|
12394
|
+
token_metric forever
|
|
12395
|
+
efficiency_metric forever
|
|
12396
|
+
"""
|
|
12397
|
+
import json as _json
|
|
12398
|
+
|
|
12399
|
+
from specsmith.esdb_sweep import run_sweep
|
|
12400
|
+
|
|
12401
|
+
root = Path(project_dir).resolve()
|
|
12402
|
+
result = run_sweep(root, orphans_only=orphans_only, dry_run=dry_run)
|
|
12403
|
+
|
|
12404
|
+
if as_json:
|
|
12405
|
+
click.echo(
|
|
12406
|
+
_json.dumps(
|
|
12407
|
+
{
|
|
12408
|
+
"tombstoned": result.tombstoned,
|
|
12409
|
+
"orphans_flagged": result.orphans_flagged,
|
|
12410
|
+
"efficiency_refreshed": result.efficiency_refreshed,
|
|
12411
|
+
"kinds_swept": result.kinds_swept,
|
|
12412
|
+
"errors": result.errors,
|
|
12413
|
+
"dry_run": dry_run,
|
|
12414
|
+
},
|
|
12415
|
+
indent=2,
|
|
12416
|
+
)
|
|
12417
|
+
)
|
|
12418
|
+
else:
|
|
12419
|
+
mode = "[dim](dry-run)[/dim]" if dry_run else ""
|
|
12420
|
+
console.print(f"[bold]specsmith esdb sweep[/bold] {mode} \u2014 {root}\n")
|
|
12421
|
+
if result.tombstoned:
|
|
12422
|
+
console.print(
|
|
12423
|
+
f" [green]\u2713[/green] Tombstoned {result.tombstoned} expired record(s):"
|
|
12424
|
+
)
|
|
12425
|
+
for kind, n in result.kinds_swept.items():
|
|
12426
|
+
console.print(f" {kind}: {n}")
|
|
12427
|
+
else:
|
|
12428
|
+
console.print(" [dim]No expired records to sweep.[/dim]")
|
|
12429
|
+
if result.orphans_flagged:
|
|
12430
|
+
console.print(
|
|
12431
|
+
f" [green]\u2713[/green] Flagged {result.orphans_flagged} orphan record(s)."
|
|
12432
|
+
)
|
|
12433
|
+
if result.efficiency_refreshed:
|
|
12434
|
+
console.print(" [green]\u2713[/green] EFF-CURRENT refreshed.")
|
|
12435
|
+
if result.errors:
|
|
12436
|
+
for err in result.errors:
|
|
12437
|
+
console.print(f" [yellow]\u26a0[/yellow] {err}")
|
|
12438
|
+
|
|
12439
|
+
|
|
12148
12440
|
main.add_command(esdb_group)
|
|
12149
12441
|
|
|
12150
12442
|
|
|
@@ -74,6 +74,8 @@ class ContextOrchestrator:
|
|
|
74
74
|
) -> OptimizeResultEx:
|
|
75
75
|
"""Run the appropriate optimization tier for the current fill percentage.
|
|
76
76
|
|
|
77
|
+
Uses dynamic thresholds from EFF-CURRENT when available (REQ-415).
|
|
78
|
+
|
|
77
79
|
Args:
|
|
78
80
|
fill_pct: Current context fill as a percentage (0–100).
|
|
79
81
|
history: Current conversation history (list of {role, content}).
|
|
@@ -83,13 +85,18 @@ class ContextOrchestrator:
|
|
|
83
85
|
"""
|
|
84
86
|
result = OptimizeResultEx(history=list(history or []))
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
t1, t2, t3 = self._load_dynamic_thresholds()
|
|
89
|
+
|
|
90
|
+
if fill_pct >= t3:
|
|
87
91
|
self._run_tier3(result)
|
|
88
|
-
elif fill_pct >=
|
|
92
|
+
elif fill_pct >= t2:
|
|
89
93
|
self._run_tier2(result)
|
|
90
|
-
elif fill_pct >=
|
|
94
|
+
elif fill_pct >= t1:
|
|
91
95
|
self._run_tier1(result)
|
|
92
96
|
|
|
97
|
+
# Update context_usage with peak fill (REQ-416)
|
|
98
|
+
self._update_context_usage_peak(fill_pct)
|
|
99
|
+
|
|
93
100
|
return result
|
|
94
101
|
|
|
95
102
|
def optimize_all(self, *, dry_run: bool = False) -> OptimizeResultEx:
|
|
@@ -215,6 +222,67 @@ class ContextOrchestrator:
|
|
|
215
222
|
# ESDB helpers
|
|
216
223
|
# ------------------------------------------------------------------
|
|
217
224
|
|
|
225
|
+
def _load_dynamic_thresholds(self) -> tuple[float, float, float]:
|
|
226
|
+
"""Return (t1, t2, t3) tier thresholds, adjusted by EFF-CURRENT (REQ-415).
|
|
227
|
+
|
|
228
|
+
When the context is degraded, lower thresholds so compression triggers
|
|
229
|
+
earlier. Falls back to module-level constants on any error.
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
from specsmith.esdb import SqliteStore
|
|
233
|
+
|
|
234
|
+
sqlite_path = self.root / ".specsmith" / "esdb.sqlite3"
|
|
235
|
+
if not sqlite_path.exists():
|
|
236
|
+
return TIER1_PCT, TIER2_PCT, TIER3_PCT
|
|
237
|
+
with SqliteStore(self.root) as store:
|
|
238
|
+
rec = store.get("EFF-CURRENT")
|
|
239
|
+
if rec is None or not rec.data.get("degraded"):
|
|
240
|
+
return TIER1_PCT, TIER2_PCT, TIER3_PCT
|
|
241
|
+
# Degraded: trigger compression 5% earlier
|
|
242
|
+
return TIER1_PCT - 5.0, TIER2_PCT - 5.0, TIER3_PCT - 5.0
|
|
243
|
+
except Exception: # noqa: BLE001
|
|
244
|
+
return TIER1_PCT, TIER2_PCT, TIER3_PCT
|
|
245
|
+
|
|
246
|
+
def _update_context_usage_peak(self, fill_pct: float) -> None:
|
|
247
|
+
"""Upsert/update the peak fill percentage in the latest context_usage record (REQ-416)."""
|
|
248
|
+
try:
|
|
249
|
+
from specsmith.esdb import SqliteRecord, SqliteStore, open_default_store
|
|
250
|
+
|
|
251
|
+
sqlite_path = self.root / ".specsmith" / "esdb.sqlite3"
|
|
252
|
+
if not sqlite_path.exists():
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
# Find the most recent context_usage record and update its peak_fill_pct
|
|
256
|
+
with SqliteStore(self.root) as store:
|
|
257
|
+
records = store.query(kind="context_usage", status="active")
|
|
258
|
+
|
|
259
|
+
if not records:
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
# Sort by timestamp desc
|
|
263
|
+
latest = max(
|
|
264
|
+
records,
|
|
265
|
+
key=lambda r: str(r.data.get("timestamp") or ""),
|
|
266
|
+
)
|
|
267
|
+
data = dict(latest.data)
|
|
268
|
+
existing_peak = float(data.get("peak_fill_pct") or 0.0)
|
|
269
|
+
if fill_pct <= existing_peak:
|
|
270
|
+
return # No update needed
|
|
271
|
+
data["peak_fill_pct"] = fill_pct
|
|
272
|
+
updated = SqliteRecord(
|
|
273
|
+
id=latest.id,
|
|
274
|
+
kind=latest.kind,
|
|
275
|
+
status=latest.status,
|
|
276
|
+
label=latest.label,
|
|
277
|
+
confidence=latest.confidence,
|
|
278
|
+
data=data,
|
|
279
|
+
source_ids=latest.source_ids,
|
|
280
|
+
)
|
|
281
|
+
with open_default_store(self.root, warn=False) as store:
|
|
282
|
+
store.upsert(updated)
|
|
283
|
+
except Exception: # noqa: BLE001
|
|
284
|
+
pass
|
|
285
|
+
|
|
218
286
|
def _evict_low_confidence_records(
|
|
219
287
|
self,
|
|
220
288
|
min_confidence: float = 0.5,
|
|
@@ -225,7 +293,8 @@ class ContextOrchestrator:
|
|
|
225
293
|
Writes back to the store (tombstone via store.delete()) so the eviction
|
|
226
294
|
persists across sessions. Data is never physically removed — tombstone
|
|
227
295
|
only. Infrastructure records (edge, rollback_event, token_metric,
|
|
228
|
-
skill_run) and governance records
|
|
296
|
+
skill_run, efficiency_metric, context_usage) and governance records
|
|
297
|
+
(requirement, testcase) are exempt.
|
|
229
298
|
|
|
230
299
|
Returns the count of records actually tombstoned.
|
|
231
300
|
"""
|
|
@@ -237,6 +306,8 @@ class ContextOrchestrator:
|
|
|
237
306
|
"rollback_event",
|
|
238
307
|
"token_metric",
|
|
239
308
|
"skill_run",
|
|
309
|
+
"efficiency_metric",
|
|
310
|
+
"context_usage",
|
|
240
311
|
}
|
|
241
312
|
)
|
|
242
313
|
|