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