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.
Files changed (348) hide show
  1. {specsmith-0.17.1.dev667/src/specsmith.egg-info → specsmith-0.17.1.dev669}/PKG-INFO +1 -1
  2. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/pyproject.toml +1 -1
  3. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/context_seed.py +134 -3
  4. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/runner.py +17 -0
  5. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/cli.py +292 -0
  6. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/context_orchestrator.py +75 -4
  7. specsmith-0.17.1.dev669/src/specsmith/efficiency.py +293 -0
  8. specsmith-0.17.1.dev669/src/specsmith/esdb_sweep.py +225 -0
  9. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb_writer.py +202 -2
  10. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ledger.py +17 -0
  11. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/__init__.py +2 -0
  12. specsmith-0.17.1.dev669/src/specsmith/migrations/m009_esdb_first.py +257 -0
  13. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/project_metrics.py +7 -0
  14. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/serve.py +11 -0
  15. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/agents.md.j2 +6 -2
  16. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/trace.py +7 -0
  17. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs_commands.py +25 -1
  18. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669/src/specsmith.egg-info}/PKG-INFO +1 -1
  19. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/SOURCES.txt +7 -0
  20. specsmith-0.17.1.dev669/tests/test_dual_write.py +268 -0
  21. specsmith-0.17.1.dev669/tests/test_efficiency.py +246 -0
  22. specsmith-0.17.1.dev669/tests/test_esdb_sweep.py +267 -0
  23. specsmith-0.17.1.dev669/tests/test_resume_command.py +188 -0
  24. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/LICENSE +0 -0
  25. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/README.md +0 -0
  26. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/setup.cfg +0 -0
  27. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/__init__.py +0 -0
  28. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/belief.py +0 -0
  29. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/certainty.py +0 -0
  30. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/failure_graph.py +0 -0
  31. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/py.typed +0 -0
  32. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/recovery.py +0 -0
  33. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/session.py +0 -0
  34. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/stress_tester.py +0 -0
  35. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/epistemic/trace.py +0 -0
  36. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/__init__.py +0 -0
  37. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/__main__.py +0 -0
  38. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/__init__.py +0 -0
  39. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/broker.py +0 -0
  40. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/chat_runner.py +0 -0
  41. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/cleanup.py +0 -0
  42. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/core.py +0 -0
  43. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/__init__.py +0 -0
  44. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/_status.py +0 -0
  45. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/dag.py +0 -0
  46. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  47. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/events.py +0 -0
  48. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/dispatch/result.py +0 -0
  49. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/endpoints.py +0 -0
  50. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/events.py +0 -0
  51. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/execution_profiles.py +0 -0
  52. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/fallback.py +0 -0
  53. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/hf_leaderboard.py +0 -0
  54. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/hf_sync.py +0 -0
  55. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/indexer.py +0 -0
  56. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/llm_client.py +0 -0
  57. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/mcp.py +0 -0
  58. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/memory.py +0 -0
  59. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_intelligence.py +0 -0
  60. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_profiles.py +0 -0
  61. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/model_router.py +0 -0
  62. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/orchestrator.py +0 -0
  63. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/permissions.py +0 -0
  64. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/profiles.py +0 -0
  65. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/provider_registry.py +0 -0
  66. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/repl.py +0 -0
  67. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/router.py +0 -0
  68. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/rules.py +0 -0
  69. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/safety.py +0 -0
  70. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/spawner.py +0 -0
  71. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/suggester.py +0 -0
  72. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/teams.py +0 -0
  73. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/tools.py +0 -0
  74. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/verifier.py +0 -0
  75. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/agent/voice.py +0 -0
  76. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/approvals.py +0 -0
  77. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/architect.py +0 -0
  78. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/auditor.py +0 -0
  79. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/auth.py +0 -0
  80. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/block_export.py +0 -0
  81. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/channel.py +0 -0
  82. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ci_manager.py +0 -0
  83. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/__init__.py +0 -0
  84. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/intelligence.py +0 -0
  85. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/issues_policy.py +0 -0
  86. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/commands/reporting.py +0 -0
  87. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/__init__.py +0 -0
  88. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/_compat.py +0 -0
  89. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/checker.py +0 -0
  90. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/evidence.py +0 -0
  91. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/regulations.py +0 -0
  92. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compliance/reporter.py +0 -0
  93. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/compressor.py +0 -0
  94. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/config.py +0 -0
  95. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/console_utils.py +0 -0
  96. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/context_window.py +0 -0
  97. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/credit_analyzer.py +0 -0
  98. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/credits.py +0 -0
  99. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/dashboard.py +0 -0
  100. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/__init__.py +0 -0
  101. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/base.py +0 -0
  102. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/citations.py +0 -0
  103. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/fpd.py +0 -0
  104. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/odp.py +0 -0
  105. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/patentsview.py +0 -0
  106. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/pfw.py +0 -0
  107. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/ppubs.py +0 -0
  108. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/datasources/ptab.py +0 -0
  109. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/differ.py +0 -0
  110. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/doctor.py +0 -0
  111. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/drive.py +0 -0
  112. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/editor.py +0 -0
  113. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/__init__.py +0 -0
  114. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/belief.py +0 -0
  115. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/certainty.py +0 -0
  116. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/failure_graph.py +0 -0
  117. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/recovery.py +0 -0
  118. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/epistemic/stress_tester.py +0 -0
  119. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/__init__.py +0 -0
  120. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/_license.py +0 -0
  121. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/bridge.py +0 -0
  122. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/esdb/sqlite_store.py +0 -0
  123. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/__init__.py +0 -0
  124. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/builtins.py +0 -0
  125. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/eval/runner.py +0 -0
  126. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/executor.py +0 -0
  127. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/exporter.py +0 -0
  128. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_logic.py +0 -0
  129. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_store.py +0 -0
  130. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governance_yaml.py +0 -0
  131. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/governed_pr.py +0 -0
  132. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/__init__.py +0 -0
  133. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/app.py +0 -0
  134. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/main_window.py +0 -0
  135. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/session_tab.py +0 -0
  136. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/theme.py +0 -0
  137. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/__init__.py +0 -0
  138. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/chat_view.py +0 -0
  139. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/input_bar.py +0 -0
  140. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  141. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/token_meter.py +0 -0
  142. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  143. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/widgets/update_checker.py +0 -0
  144. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/gui/worker.py +0 -0
  145. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/history_search.py +0 -0
  146. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/importer.py +0 -0
  147. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/instinct.py +0 -0
  148. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/__init__.py +0 -0
  149. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/agent_skill.py +0 -0
  150. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/aider.py +0 -0
  151. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/base.py +0 -0
  152. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/claude_code.py +0 -0
  153. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/codity.py +0 -0
  154. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/copilot.py +0 -0
  155. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/cursor.py +0 -0
  156. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/gemini.py +0 -0
  157. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/integrations/windsurf.py +0 -0
  158. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/issue_reporter.py +0 -0
  159. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/languages.py +0 -0
  160. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/local_model.py +0 -0
  161. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/mcp_generator.py +0 -0
  162. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/mcp_server.py +0 -0
  163. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  164. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  165. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  166. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  167. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  168. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m006_session_governance.py +0 -0
  169. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m007_yaml_first.py +0 -0
  170. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/m008_esdb_full_coverage.py +0 -0
  171. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/migrations/runner.py +0 -0
  172. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/ollama_cmds.py +0 -0
  173. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/patent.py +0 -0
  174. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/paths.py +0 -0
  175. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/phase.py +0 -0
  176. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/plugins.py +0 -0
  177. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/policy.py +0 -0
  178. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/profiles.py +0 -0
  179. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/quality_report.py +0 -0
  180. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/rate_limits.py +0 -0
  181. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/recover.py +0 -0
  182. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/releaser.py +0 -0
  183. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/requirements.py +0 -0
  184. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/requirements_parser.py +0 -0
  185. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/retrieval.py +0 -0
  186. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/risk.py +0 -0
  187. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/safe_write.py +0 -0
  188. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/scaffolder.py +0 -0
  189. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session.py +0 -0
  190. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session_init.py +0 -0
  191. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/session_store.py +0 -0
  192. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/__init__.py +0 -0
  193. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/ai_agents.py +0 -0
  194. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/cloud.py +0 -0
  195. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/corporate.py +0 -0
  196. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/cross_platform.py +0 -0
  197. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/data_engineering.py +0 -0
  198. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/devops.py +0 -0
  199. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/docs.py +0 -0
  200. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/embedded.py +0 -0
  201. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/governance.py +0 -0
  202. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/hardware.py +0 -0
  203. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/mobile.py +0 -0
  204. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/platform_engineering.py +0 -0
  205. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/productivity.py +0 -0
  206. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/software_engineering.py +0 -0
  207. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/specsmith_skills.py +0 -0
  208. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/ssh.py +0 -0
  209. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills/web_backend.py +0 -0
  210. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/skills_builder.py +0 -0
  211. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/sync.py +0 -0
  212. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  213. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  214. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  215. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  216. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  217. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  218. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  219. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/community/security.md.j2 +0 -0
  220. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  221. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  222. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  223. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  224. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  225. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/editorconfig.j2 +0 -0
  226. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/gitattributes.j2 +0 -0
  227. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/gitignore.j2 +0 -0
  228. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/go/go.mod.j2 +0 -0
  229. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/go/main.go.j2 +0 -0
  230. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  231. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  232. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  233. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  234. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  235. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  236. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  237. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  238. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  239. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  240. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  241. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/js/package.json.j2 +0 -0
  242. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/ledger.md.j2 +0 -0
  243. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/cli.py.j2 +0 -0
  244. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/init.py.j2 +0 -0
  245. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  246. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/readme.md.j2 +0 -0
  247. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  248. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  249. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  250. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  251. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  252. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  253. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  254. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  255. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  256. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/tool_installer.py +0 -0
  257. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/toolrules.py +0 -0
  258. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/tools.py +0 -0
  259. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/transcripts.py +0 -0
  260. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/updater.py +0 -0
  261. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/upgrader.py +0 -0
  262. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/validator.py +0 -0
  263. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/__init__.py +0 -0
  264. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/base.py +0 -0
  265. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/bitbucket.py +0 -0
  266. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/github.py +0 -0
  267. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/vcs/gitlab.py +0 -0
  268. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/wi_store.py +0 -0
  269. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/wireframes.py +0 -0
  270. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith/workspace.py +0 -0
  271. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/dependency_links.txt +0 -0
  272. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/entry_points.txt +0 -0
  273. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/requires.txt +0 -0
  274. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/src/specsmith.egg-info/top_level.txt +0 -0
  275. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_CMD_001.py +0 -0
  276. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_profiles.py +0 -0
  277. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_run_feedback.py +0 -0
  278. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_agent_runner_ready.py +0 -0
  279. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_ai_client.py +0 -0
  280. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_ai_intelligence.py +0 -0
  281. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architect_interview.py +0 -0
  282. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architect_v2.py +0 -0
  283. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_architecture.py +0 -0
  284. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_auditor.py +0 -0
  285. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_channel.py +0 -0
  286. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_diff_decision.py +0 -0
  287. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_runner_openai_compat.py +0 -0
  288. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_chat_stdin_protocol.py +0 -0
  289. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cleanup_cmd.py +0 -0
  290. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli.py +0 -0
  291. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli_regressions.py +0 -0
  292. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_cli_workflows_history_drive.py +0 -0
  293. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compliance.py +0 -0
  294. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compliance_governance.py +0 -0
  295. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_compressor.py +0 -0
  296. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_dispatch.py +0 -0
  297. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_e2e_nexus.py +0 -0
  298. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_endpoints_cli.py +0 -0
  299. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_endpoints_store.py +0 -0
  300. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_epistemic.py +0 -0
  301. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_backend_switch.py +0 -0
  302. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_enforcement.py +0 -0
  303. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_integration.py +0 -0
  304. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_license.py +0 -0
  305. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_sqlite.py +0 -0
  306. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_verify_chain_cli.py +0 -0
  307. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_esdb_writer.py +0 -0
  308. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_fallback_chain.py +0 -0
  309. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_golden_path.py +0 -0
  310. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_importer.py +0 -0
  311. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_init_modes.py +0 -0
  312. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_integrations.py +0 -0
  313. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_integrations_codity.py +0 -0
  314. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_intelligence.py +0 -0
  315. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issue_policy_features.py +0 -0
  316. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issue_reporter.py +0 -0
  317. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_issues_263_264.py +0 -0
  318. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_local_model.py +0 -0
  319. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_markdown_deprecation.py +0 -0
  320. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_mcp_client.py +0 -0
  321. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_mcp_server.py +0 -0
  322. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_migration_direction.py +0 -0
  323. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_model_router.py +0 -0
  324. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_new_modules.py +0 -0
  325. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_nexus.py +0 -0
  326. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_permissions.py +0 -0
  327. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_phase1_4_new.py +0 -0
  328. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_phase34_completion.py +0 -0
  329. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_project_metrics.py +0 -0
  330. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_quality_report.py +0 -0
  331. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_rate_limits.py +0 -0
  332. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_repl.py +0 -0
  333. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_reporting_commands.py +0 -0
  334. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_req_248_262.py +0 -0
  335. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_req_358_359.py +0 -0
  336. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_scaffolder.py +0 -0
  337. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_schema_migrations.py +0 -0
  338. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_skill_marketplace.py +0 -0
  339. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_skills_mcp.py +0 -0
  340. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_smoke.py +0 -0
  341. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_suggester.py +0 -0
  342. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_tools.py +0 -0
  343. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_typing_guardrails.py +0 -0
  344. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_validator.py +0 -0
  345. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_vcs.py +0 -0
  346. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_warp_parity.py +0 -0
  347. {specsmith-0.17.1.dev667 → specsmith-0.17.1.dev669}/tests/test_warp_parity_followup.py +0 -0
  348. {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.dev667
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.dev667"
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. ESDB records relevance-based by kind (REQ-399) ──────────────
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 LEDGER.md, newest-first."""
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
- if fill_pct >= TIER3_PCT:
88
+ t1, t2, t3 = self._load_dynamic_thresholds()
89
+
90
+ if fill_pct >= t3:
87
91
  self._run_tier3(result)
88
- elif fill_pct >= TIER2_PCT:
92
+ elif fill_pct >= t2:
89
93
  self._run_tier2(result)
90
- elif fill_pct >= TIER1_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 (requirement, testcase) are exempt.
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