specsmith 0.15.3.dev570__tar.gz → 0.15.3.dev572__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 (302) hide show
  1. {specsmith-0.15.3.dev570/src/specsmith.egg-info → specsmith-0.15.3.dev572}/PKG-INFO +1 -1
  2. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/pyproject.toml +1 -1
  3. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/auditor.py +11 -4
  4. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/cli.py +80 -23
  5. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/esdb/sqlite_store.py +16 -0
  6. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/sync.py +84 -0
  7. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/gitignore.j2 +7 -0
  8. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572/src/specsmith.egg-info}/PKG-INFO +1 -1
  9. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith.egg-info/SOURCES.txt +1 -0
  10. specsmith-0.15.3.dev572/tests/test_esdb_enforcement.py +81 -0
  11. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_scaffolder.py +3 -0
  12. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/LICENSE +0 -0
  13. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/README.md +0 -0
  14. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/setup.cfg +0 -0
  15. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/__init__.py +0 -0
  16. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/belief.py +0 -0
  17. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/certainty.py +0 -0
  18. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/failure_graph.py +0 -0
  19. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/py.typed +0 -0
  20. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/recovery.py +0 -0
  21. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/session.py +0 -0
  22. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/stress_tester.py +0 -0
  23. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/epistemic/trace.py +0 -0
  24. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/__init__.py +0 -0
  25. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/__main__.py +0 -0
  26. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/__init__.py +0 -0
  27. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/broker.py +0 -0
  28. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/chat_runner.py +0 -0
  29. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/cleanup.py +0 -0
  30. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/context_seed.py +0 -0
  31. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/core.py +0 -0
  32. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/__init__.py +0 -0
  33. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/_status.py +0 -0
  34. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/dag.py +0 -0
  35. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  36. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/events.py +0 -0
  37. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/dispatch/result.py +0 -0
  38. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/endpoints.py +0 -0
  39. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/events.py +0 -0
  40. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/execution_profiles.py +0 -0
  41. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/fallback.py +0 -0
  42. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/hf_leaderboard.py +0 -0
  43. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/hf_sync.py +0 -0
  44. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/indexer.py +0 -0
  45. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/llm_client.py +0 -0
  46. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/mcp.py +0 -0
  47. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/memory.py +0 -0
  48. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/model_intelligence.py +0 -0
  49. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/model_profiles.py +0 -0
  50. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/orchestrator.py +0 -0
  51. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/permissions.py +0 -0
  52. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/profiles.py +0 -0
  53. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/provider_registry.py +0 -0
  54. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/repl.py +0 -0
  55. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/router.py +0 -0
  56. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/rules.py +0 -0
  57. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/runner.py +0 -0
  58. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/safety.py +0 -0
  59. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/spawner.py +0 -0
  60. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/suggester.py +0 -0
  61. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/teams.py +0 -0
  62. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/tools.py +0 -0
  63. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/verifier.py +0 -0
  64. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/agent/voice.py +0 -0
  65. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/architect.py +0 -0
  66. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/auth.py +0 -0
  67. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/block_export.py +0 -0
  68. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/channel.py +0 -0
  69. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/ci_manager.py +0 -0
  70. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/commands/__init__.py +0 -0
  71. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/commands/intelligence.py +0 -0
  72. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/__init__.py +0 -0
  73. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/_compat.py +0 -0
  74. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/checker.py +0 -0
  75. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/evidence.py +0 -0
  76. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/regulations.py +0 -0
  77. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compliance/reporter.py +0 -0
  78. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/compressor.py +0 -0
  79. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/config.py +0 -0
  80. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/console_utils.py +0 -0
  81. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/context_orchestrator.py +0 -0
  82. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/context_window.py +0 -0
  83. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/credit_analyzer.py +0 -0
  84. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/credits.py +0 -0
  85. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/__init__.py +0 -0
  86. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/base.py +0 -0
  87. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/citations.py +0 -0
  88. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/fpd.py +0 -0
  89. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/odp.py +0 -0
  90. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/patentsview.py +0 -0
  91. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/pfw.py +0 -0
  92. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/ppubs.py +0 -0
  93. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/datasources/ptab.py +0 -0
  94. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/differ.py +0 -0
  95. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/doctor.py +0 -0
  96. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/drive.py +0 -0
  97. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/editor.py +0 -0
  98. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/__init__.py +0 -0
  99. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/belief.py +0 -0
  100. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/certainty.py +0 -0
  101. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/failure_graph.py +0 -0
  102. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/recovery.py +0 -0
  103. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/epistemic/stress_tester.py +0 -0
  104. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/esdb/__init__.py +0 -0
  105. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/esdb/_license.py +0 -0
  106. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/esdb/bridge.py +0 -0
  107. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/eval/__init__.py +0 -0
  108. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/eval/builtins.py +0 -0
  109. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/eval/runner.py +0 -0
  110. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/executor.py +0 -0
  111. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/exporter.py +0 -0
  112. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/governance_logic.py +0 -0
  113. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/governance_store.py +0 -0
  114. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/governance_yaml.py +0 -0
  115. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/__init__.py +0 -0
  116. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/app.py +0 -0
  117. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/main_window.py +0 -0
  118. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/session_tab.py +0 -0
  119. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/theme.py +0 -0
  120. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/__init__.py +0 -0
  121. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/chat_view.py +0 -0
  122. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/input_bar.py +0 -0
  123. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  124. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/token_meter.py +0 -0
  125. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  126. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/widgets/update_checker.py +0 -0
  127. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/gui/worker.py +0 -0
  128. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/history_search.py +0 -0
  129. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/importer.py +0 -0
  130. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/instinct.py +0 -0
  131. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/__init__.py +0 -0
  132. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/agent_skill.py +0 -0
  133. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/aider.py +0 -0
  134. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/base.py +0 -0
  135. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/claude_code.py +0 -0
  136. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/codity.py +0 -0
  137. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/copilot.py +0 -0
  138. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/cursor.py +0 -0
  139. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/gemini.py +0 -0
  140. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/integrations/windsurf.py +0 -0
  141. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/issue_reporter.py +0 -0
  142. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/languages.py +0 -0
  143. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/ledger.py +0 -0
  144. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/mcp_generator.py +0 -0
  145. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/mcp_server.py +0 -0
  146. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/__init__.py +0 -0
  147. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  148. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  149. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  150. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  151. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  152. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/m006_session_governance.py +0 -0
  153. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/migrations/runner.py +0 -0
  154. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/ollama_cmds.py +0 -0
  155. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/patent.py +0 -0
  156. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/paths.py +0 -0
  157. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/phase.py +0 -0
  158. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/plugins.py +0 -0
  159. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/profiles.py +0 -0
  160. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/rate_limits.py +0 -0
  161. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/releaser.py +0 -0
  162. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/requirements.py +0 -0
  163. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/requirements_parser.py +0 -0
  164. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/retrieval.py +0 -0
  165. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/safe_write.py +0 -0
  166. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/scaffolder.py +0 -0
  167. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/serve.py +0 -0
  168. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/session.py +0 -0
  169. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/session_init.py +0 -0
  170. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/session_store.py +0 -0
  171. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/__init__.py +0 -0
  172. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/ai_agents.py +0 -0
  173. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/cloud.py +0 -0
  174. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/corporate.py +0 -0
  175. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/cross_platform.py +0 -0
  176. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/data_engineering.py +0 -0
  177. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/devops.py +0 -0
  178. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/docs.py +0 -0
  179. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/embedded.py +0 -0
  180. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/governance.py +0 -0
  181. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/hardware.py +0 -0
  182. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/mobile.py +0 -0
  183. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/platform_engineering.py +0 -0
  184. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/productivity.py +0 -0
  185. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/software_engineering.py +0 -0
  186. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/specsmith_skills.py +0 -0
  187. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/ssh.py +0 -0
  188. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills/web_backend.py +0 -0
  189. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/skills_builder.py +0 -0
  190. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/agents.md.j2 +0 -0
  191. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  192. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  193. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  194. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  195. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  196. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  197. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  198. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/community/security.md.j2 +0 -0
  199. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  200. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  201. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  202. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  203. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  204. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/editorconfig.j2 +0 -0
  205. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/gitattributes.j2 +0 -0
  206. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/go/go.mod.j2 +0 -0
  207. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/go/main.go.j2 +0 -0
  208. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  209. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  210. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  211. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  212. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  213. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  214. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  215. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  216. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  217. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  218. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  219. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/js/package.json.j2 +0 -0
  220. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/ledger.md.j2 +0 -0
  221. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/python/cli.py.j2 +0 -0
  222. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/python/init.py.j2 +0 -0
  223. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  224. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/readme.md.j2 +0 -0
  225. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  226. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  227. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  228. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  229. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  230. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  231. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  232. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  233. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  234. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/tool_installer.py +0 -0
  235. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/toolrules.py +0 -0
  236. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/tools.py +0 -0
  237. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/trace.py +0 -0
  238. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/updater.py +0 -0
  239. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/upgrader.py +0 -0
  240. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/validator.py +0 -0
  241. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs/__init__.py +0 -0
  242. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs/base.py +0 -0
  243. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs/bitbucket.py +0 -0
  244. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs/github.py +0 -0
  245. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs/gitlab.py +0 -0
  246. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/vcs_commands.py +0 -0
  247. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/wi_store.py +0 -0
  248. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/wireframes.py +0 -0
  249. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith/workspace.py +0 -0
  250. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith.egg-info/dependency_links.txt +0 -0
  251. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith.egg-info/entry_points.txt +0 -0
  252. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith.egg-info/requires.txt +0 -0
  253. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/src/specsmith.egg-info/top_level.txt +0 -0
  254. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_CMD_001.py +0 -0
  255. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_agent_profiles.py +0 -0
  256. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_agent_run_feedback.py +0 -0
  257. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_agent_runner_ready.py +0 -0
  258. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_ai_client.py +0 -0
  259. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_ai_intelligence.py +0 -0
  260. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_auditor.py +0 -0
  261. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_channel.py +0 -0
  262. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_chat_diff_decision.py +0 -0
  263. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_chat_runner_openai_compat.py +0 -0
  264. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_chat_stdin_protocol.py +0 -0
  265. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_cli.py +0 -0
  266. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_cli_workflows_history_drive.py +0 -0
  267. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_compliance.py +0 -0
  268. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_compliance_governance.py +0 -0
  269. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_compressor.py +0 -0
  270. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_dispatch.py +0 -0
  271. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_e2e_nexus.py +0 -0
  272. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_endpoints_cli.py +0 -0
  273. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_endpoints_store.py +0 -0
  274. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_epistemic.py +0 -0
  275. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_esdb_license.py +0 -0
  276. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_esdb_sqlite.py +0 -0
  277. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_fallback_chain.py +0 -0
  278. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_importer.py +0 -0
  279. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_integrations.py +0 -0
  280. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_integrations_codity.py +0 -0
  281. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_intelligence.py +0 -0
  282. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_issue_reporter.py +0 -0
  283. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_mcp_client.py +0 -0
  284. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_mcp_server.py +0 -0
  285. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_new_modules.py +0 -0
  286. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_nexus.py +0 -0
  287. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_permissions.py +0 -0
  288. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_phase1_4_new.py +0 -0
  289. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_phase34_completion.py +0 -0
  290. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_rate_limits.py +0 -0
  291. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_req_248_262.py +0 -0
  292. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_req_358_359.py +0 -0
  293. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_skill_marketplace.py +0 -0
  294. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_skills_mcp.py +0 -0
  295. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_smoke.py +0 -0
  296. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_suggester.py +0 -0
  297. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_tools.py +0 -0
  298. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_validator.py +0 -0
  299. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_vcs.py +0 -0
  300. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_warp_parity.py +0 -0
  301. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_warp_parity_followup.py +0 -0
  302. {specsmith-0.15.3.dev570 → specsmith-0.15.3.dev572}/tests/test_wi_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.15.3.dev570
3
+ Version: 0.15.3.dev572
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.15.3.dev570"
7
+ version = "0.15.3.dev572"
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"
@@ -1130,7 +1130,11 @@ def check_industrial_artifacts(root: Path) -> list[AuditResult]:
1130
1130
  raw = _read_scaffold_raw(root)
1131
1131
  declared: dict[str, Any] = raw.get("industrial_artifacts", {}) or {}
1132
1132
  declared_eds = declared.get("canopen_eds", []) or []
1133
- declared_paths = {e.get("path", "") for e in declared_eds if isinstance(e, dict)}
1133
+ # Normalise declared paths to forward slashes and lowercase so they match
1134
+ # on Windows where YAML uses '/' but Path.relative_to() returns '\\' (#251).
1135
+ declared_paths = {
1136
+ e.get("path", "").replace("\\", "/").lower() for e in declared_eds if isinstance(e, dict)
1137
+ }
1134
1138
 
1135
1139
  # Scan for .eds and .xdd files — respect scan_exclude_dirs and
1136
1140
  # scan_exclude_patterns from scaffold.yml (#175).
@@ -1158,9 +1162,12 @@ def check_industrial_artifacts(root: Path) -> list[AuditResult]:
1158
1162
  if not found_eds:
1159
1163
  return results # No industrial artifacts found
1160
1164
 
1161
- # Use as_posix() for cross-platform comparison — avoids single-backslash
1162
- # mismatch on Windows where str(Path) uses '\' but declared paths use '/' (#173).
1163
- undeclared = [f for f in found_eds if f.relative_to(root).as_posix() not in declared_paths]
1165
+ # Use as_posix().lower() for cross-platform comparison — avoids backslash
1166
+ # mismatch on Windows where str(Path) uses '\' but YAML paths use '/' (#173,
1167
+ # #251). Both sides are lowercased so Windows case-insensitive FS is handled.
1168
+ undeclared = [
1169
+ f for f in found_eds if f.relative_to(root).as_posix().lower() not in declared_paths
1170
+ ]
1164
1171
 
1165
1172
  if undeclared:
1166
1173
  results.append(
@@ -2577,30 +2577,47 @@ def save_cmd(project_dir: str, message: str, no_push: bool, force: bool, as_json
2577
2577
  """
2578
2578
  import json as _json
2579
2579
 
2580
- from specsmith.esdb.bridge import EsdbBridge
2580
+ from specsmith import esdb as esdb_mod
2581
+ from specsmith.sync import normalize_esdb_gitignore_policy
2581
2582
  from specsmith.vcs_commands import has_uncommitted_changes, run_commit, run_push
2582
2583
 
2583
2584
  root = Path(project_dir).resolve()
2584
2585
  steps: list[dict[str, Any]] = []
2586
+ # 0. Normalize ESDB policy for legacy projects
2587
+ try:
2588
+ changed = normalize_esdb_gitignore_policy(root)
2589
+ steps.append(
2590
+ {
2591
+ "step": "gitignore_policy",
2592
+ "ok": True,
2593
+ "note": "normalized legacy ESDB policy" if changed else "already compliant",
2594
+ }
2595
+ )
2596
+ except Exception as exc: # noqa: BLE001
2597
+ steps.append({"step": "gitignore_policy", "ok": False, "error": str(exc)})
2585
2598
 
2586
2599
  # 1. ESDB backup
2587
2600
  try:
2588
- bridge = EsdbBridge(str(root))
2589
- store = bridge._get_store() # noqa: SLF001
2590
- if store is not None:
2591
- backup_path = store.backup()
2592
- steps.append({"step": "esdb_backup", "ok": True, "path": str(backup_path)})
2593
- else:
2594
- steps.append(
2595
- {"step": "esdb_backup", "ok": True, "note": "JSON fallback (no WAL to backup)"}
2596
- )
2597
- except ImportError:
2598
- # chronomemory not installed — non-fatal; commit and push still proceed.
2599
- # Install with: pipx inject specsmith
2600
- # "chronomemory @ git+https://github.com/layer1labs/chronomemory.git@v0.1.1"
2601
- from specsmith.esdb import _INSTALL_HINT # noqa: PLC0415
2602
-
2603
- steps.append({"step": "esdb_backup", "ok": True, "note": f"skipped — {_INSTALL_HINT}"})
2601
+ with esdb_mod.open_default_store(root, warn=False) as store: # type: ignore[attr-defined]
2602
+ backup_fn = getattr(store, "backup", None)
2603
+ if callable(backup_fn):
2604
+ backup_path = backup_fn()
2605
+ steps.append(
2606
+ {
2607
+ "step": "esdb_backup",
2608
+ "ok": True,
2609
+ "path": str(backup_path),
2610
+ "backend": esdb_mod.ESDB_BACKEND,
2611
+ }
2612
+ )
2613
+ else:
2614
+ steps.append(
2615
+ {
2616
+ "step": "esdb_backup",
2617
+ "ok": True,
2618
+ "note": f"{esdb_mod.ESDB_BACKEND} backend has no native backup method",
2619
+ }
2620
+ )
2604
2621
  except Exception as exc: # noqa: BLE001
2605
2622
  steps.append({"step": "esdb_backup", "ok": False, "error": str(exc)})
2606
2623
 
@@ -5653,7 +5670,8 @@ def phase_next(project_dir: str, force: bool) -> None:
5653
5670
  """Advance to the next AEE workflow phase.
5654
5671
 
5655
5672
  Performs readiness checks on the current phase first.
5656
- Use --force to skip checks.
5673
+ Use --force to skip checks. Phases listed in ``suppressed_phases`` in the
5674
+ scaffold config (docs/SPECSMITH.yml) are automatically skipped (#254).
5657
5675
  """
5658
5676
  from specsmith.phase import PHASE_MAP, evaluate_phase, read_phase, write_phase
5659
5677
 
@@ -5679,12 +5697,45 @@ def phase_next(project_dir: str, force: bool) -> None:
5679
5697
  )
5680
5698
  return
5681
5699
 
5682
- write_phase(root, phase.next_phase)
5683
- next_phase = PHASE_MAP[phase.next_phase]
5700
+ # Read suppressed_phases from the scaffold config (#254).
5701
+ suppressed: list[str] = []
5702
+ try:
5703
+ import yaml as _yaml
5704
+
5705
+ from specsmith.paths import find_scaffold
5706
+
5707
+ sp = find_scaffold(root)
5708
+ if sp:
5709
+ _raw = _yaml.safe_load(sp.read_text(encoding="utf-8")) or {}
5710
+ suppressed = [str(p) for p in (_raw.get("suppressed_phases") or [])]
5711
+ except Exception: # noqa: BLE001
5712
+ pass # Never block phase advance on config parse errors
5713
+
5714
+ # Walk the phase chain until we find a non-suppressed next phase.
5715
+ target_key: str | None = phase.next_phase
5716
+ skipped: list[str] = []
5717
+ while target_key and target_key in suppressed:
5718
+ skipped.append(target_key)
5719
+ target_phase = PHASE_MAP.get(target_key)
5720
+ target_key = target_phase.next_phase if target_phase else None
5721
+
5722
+ if not target_key:
5723
+ console.print(
5724
+ f"[bold]{phase.emoji} {phase.label}[/bold] is the effective final phase "
5725
+ "(all remaining phases are suppressed)."
5726
+ )
5727
+ if skipped:
5728
+ console.print(f" [dim]Suppressed: {', '.join(skipped)}[/dim]")
5729
+ return
5730
+
5731
+ write_phase(root, target_key)
5732
+ next_phase = PHASE_MAP[target_key]
5684
5733
  console.print(
5685
5734
  f"[green]\u2713[/green] Advanced from [bold]{phase.label}[/bold] "
5686
5735
  f"to [bold]{next_phase.emoji} {next_phase.label}[/bold]."
5687
5736
  )
5737
+ if skipped:
5738
+ console.print(f" [dim]Skipped suppressed phase(s): {', '.join(skipped)}[/dim]")
5688
5739
  console.print(f" {next_phase.description}")
5689
5740
  if next_phase.commands:
5690
5741
  console.print("\n [bold]Next steps:[/bold]")
@@ -5693,14 +5744,14 @@ def phase_next(project_dir: str, force: bool) -> None:
5693
5744
 
5694
5745
  # G3: keep the agents routing table aligned with the active phase.
5695
5746
  # We pin a synthetic ``phase:active`` route so the runner can flip the
5696
- # whole session to the new phases preferred profile without the user
5747
+ # whole session to the new phase's preferred profile without the user
5697
5748
  # having to run `specsmith agents route set` themselves.
5698
5749
  try:
5699
5750
  from specsmith.agent.profiles import ProfileStore
5700
5751
 
5701
5752
  agents_store = ProfileStore.load()
5702
5753
  if agents_store.profiles:
5703
- phase_key_target = f"phase:{phase.next_phase}"
5754
+ phase_key_target = f"phase:{target_key}"
5704
5755
  target_id = agents_store.routes.get(phase_key_target) or (
5705
5756
  agents_store.default_profile_id
5706
5757
  )
@@ -10554,9 +10605,15 @@ def esdb_enable_cmd(key_file: str, as_json: bool) -> None:
10554
10605
  console.print("[dim]Contact licensing@layer1labs.com to obtain a valid license.[/dim]")
10555
10606
  raise SystemExit(1)
10556
10607
 
10608
+ import os
10609
+
10557
10610
  dest = Path.home() / ".specsmith" / "esdb.key"
10558
10611
  dest.parent.mkdir(parents=True, exist_ok=True)
10559
- shutil.copy2(key_file, dest)
10612
+ # Skip the copy when source and destination are the same file (#249).
10613
+ # On Windows, shutil.copy2(src, dst) raises PermissionError / WinError 32
10614
+ # when src == dst (file is already at the expected location).
10615
+ if os.path.realpath(str(key_file)) != os.path.realpath(str(dest)):
10616
+ shutil.copy2(key_file, dest)
10560
10617
 
10561
10618
  if as_json:
10562
10619
  click.echo(
@@ -289,6 +289,22 @@ class SqliteStore:
289
289
  row = conn.execute("SELECT MAX(rowid) FROM records").fetchone()
290
290
  return int(row[0]) if row and row[0] is not None else 0
291
291
 
292
+ def backup(self, backup_dir: str | Path | None = None) -> Path:
293
+ """Create a timestamped SQLite snapshot and return its path."""
294
+ conn = self._require_open()
295
+ ts = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
296
+ dest_dir = Path(backup_dir) if backup_dir else self._db_path.parent / "backups"
297
+ dest_dir.mkdir(parents=True, exist_ok=True)
298
+ dest = dest_dir / f"esdb_sqlite_backup_{ts}.sqlite3"
299
+
300
+ backup_conn = sqlite3.connect(str(dest))
301
+ try:
302
+ conn.backup(backup_conn)
303
+ finally:
304
+ backup_conn.close()
305
+
306
+ return dest
307
+
292
308
  def chain_valid(self) -> bool:
293
309
  """Always True — SQLite ACID guarantees integrity (no SHA-256 WAL chain)."""
294
310
  return True
@@ -207,6 +207,88 @@ class SyncResult:
207
207
  return "Machine state already in sync."
208
208
 
209
209
 
210
+ # ---------------------------------------------------------------------------
211
+ # ESDB legacy policy normalization (.gitignore)
212
+ # ---------------------------------------------------------------------------
213
+
214
+ _ESDB_GITIGNORE_FORBIDDEN = frozenset(
215
+ {
216
+ ".specsmith/",
217
+ ".chronomemory/",
218
+ }
219
+ )
220
+
221
+ _ESDB_GITIGNORE_REQUIRED = (
222
+ "!.specsmith/config.yml",
223
+ "!.specsmith/requirements.json",
224
+ "!.specsmith/testcases.json",
225
+ "!.specsmith/esdb.sqlite3",
226
+ "!.chronomemory/events.wal",
227
+ "!.chronomemory/snapshot.json",
228
+ ".chronomemory/backup/",
229
+ ".specsmith/workitems.json",
230
+ ".specsmith/runs/",
231
+ ".specsmith/chat/",
232
+ ".specsmith/perf/",
233
+ ".specsmith/recovery/",
234
+ ".specsmith/ledger.jsonl",
235
+ ".specsmith/ledger-chain.txt",
236
+ ".specsmith/trace.jsonl",
237
+ ".specsmith/credit-budget.json",
238
+ ".specsmith/credits.json",
239
+ ".specsmith/model-rate-limits.json",
240
+ ".specsmith/pids/",
241
+ ".specsmith/logs/",
242
+ ".specsmith/sessions/",
243
+ ".specsmith/agent-reports/",
244
+ ".specsmith/dispatch/",
245
+ ".specsmith/esdb_migration_manifest.json",
246
+ )
247
+
248
+
249
+ def normalize_esdb_gitignore_policy(root: Path, *, dry_run: bool = False) -> bool:
250
+ """Normalize ESDB gitignore policy for legacy projects.
251
+
252
+ Ensures broad ignores that hide canonical ESDB state are removed and that
253
+ explicit policy lines are present so SQLite/Chrono canonical artifacts are
254
+ always tracked while runtime files stay ignored.
255
+ """
256
+ gitignore_path = root / ".gitignore"
257
+ if gitignore_path.exists():
258
+ original_lines = gitignore_path.read_text(encoding="utf-8").splitlines()
259
+ else:
260
+ original_lines = []
261
+
262
+ normalized_lines: list[str] = []
263
+ removed_forbidden = False
264
+ for line in original_lines:
265
+ if line.strip() in _ESDB_GITIGNORE_FORBIDDEN:
266
+ removed_forbidden = True
267
+ continue
268
+ normalized_lines.append(line)
269
+
270
+ existing = {line.strip() for line in normalized_lines if line.strip()}
271
+ missing = [entry for entry in _ESDB_GITIGNORE_REQUIRED if entry not in existing]
272
+ changed = removed_forbidden or bool(missing)
273
+
274
+ if not changed:
275
+ return False
276
+
277
+ if normalized_lines and normalized_lines[-1].strip():
278
+ normalized_lines.append("")
279
+ if missing:
280
+ normalized_lines.append("# specsmith ESDB policy (auto-normalized)")
281
+ normalized_lines.extend(missing)
282
+
283
+ if not dry_run:
284
+ rendered = "\n".join(normalized_lines)
285
+ if rendered and not rendered.endswith("\n"):
286
+ rendered += "\n"
287
+ gitignore_path.write_text(rendered, encoding="utf-8")
288
+
289
+ return True
290
+
291
+
210
292
  # ---------------------------------------------------------------------------
211
293
  # Core sync
212
294
  # ---------------------------------------------------------------------------
@@ -244,6 +326,8 @@ def run_sync(root: Path, *, dry_run: bool = False) -> SyncResult:
244
326
  tests_md_path = root / "docs" / "TESTS.md"
245
327
  reqs_json_path = state_dir / "requirements.json"
246
328
  tests_json_path = state_dir / "testcases.json"
329
+ if not dry_run:
330
+ normalize_esdb_gitignore_policy(root)
247
331
 
248
332
  if is_yaml_mode(root):
249
333
  # ── YAML-first mode ─────────────────────────────────────────────────
@@ -45,6 +45,13 @@ htmlcov/
45
45
  # - .specsmith/esdb.sqlite3
46
46
  # - .chronomemory/events.wal
47
47
  # - .chronomemory/snapshot.json
48
+ # Always track these even if broader ignore rules are added later:
49
+ !.specsmith/config.yml
50
+ !.specsmith/requirements.json
51
+ !.specsmith/testcases.json
52
+ !.specsmith/esdb.sqlite3
53
+ !.chronomemory/events.wal
54
+ !.chronomemory/snapshot.json
48
55
  #
49
56
  # Ignore runtime-only / regeneratable artifacts:
50
57
  .chronomemory/backup/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.15.3.dev570
3
+ Version: 0.15.3.dev572
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
@@ -268,6 +268,7 @@ tests/test_e2e_nexus.py
268
268
  tests/test_endpoints_cli.py
269
269
  tests/test_endpoints_store.py
270
270
  tests/test_epistemic.py
271
+ tests/test_esdb_enforcement.py
271
272
  tests/test_esdb_license.py
272
273
  tests/test_esdb_sqlite.py
273
274
  tests/test_fallback_chain.py
@@ -0,0 +1,81 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Layer1Labs Silicon, Inc. All rights reserved.
3
+ """Regression tests for ESDB fallback and legacy policy enforcement."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+ from click.testing import CliRunner
12
+
13
+ from specsmith.cli import main
14
+ from specsmith.esdb.sqlite_store import SqliteRecord, SqliteStore
15
+ from specsmith.sync import run_sync
16
+
17
+
18
+ def _run(args: list[str]): # type: ignore[return] # noqa: ANN201
19
+ runner = CliRunner()
20
+ return runner.invoke(
21
+ main,
22
+ args,
23
+ env={
24
+ "SPECSMITH_NO_AUTO_UPDATE": "1",
25
+ "SPECSMITH_PYPI_CHECKED": "1",
26
+ "SPECSMITH_ALLOW_NON_PIPX": "1",
27
+ },
28
+ )
29
+
30
+
31
+ def test_run_sync_normalizes_legacy_esdb_gitignore(tmp_path: Path) -> None:
32
+ """Legacy broad ignores are removed and canonical ESDB policy is injected."""
33
+ (tmp_path / ".gitignore").write_text(
34
+ "\n".join(
35
+ [
36
+ ".specsmith/",
37
+ ".chronomemory/",
38
+ ]
39
+ )
40
+ + "\n",
41
+ encoding="utf-8",
42
+ )
43
+
44
+ run_sync(tmp_path)
45
+
46
+ lines = {
47
+ line.strip()
48
+ for line in (tmp_path / ".gitignore").read_text(encoding="utf-8").splitlines()
49
+ if line.strip()
50
+ }
51
+ assert ".specsmith/" not in lines
52
+ assert ".chronomemory/" not in lines
53
+ assert ".chronomemory/backup/" in lines
54
+ assert ".specsmith/workitems.json" in lines
55
+ assert "!.specsmith/esdb.sqlite3" in lines
56
+ assert "!.chronomemory/events.wal" in lines
57
+ assert "!.chronomemory/snapshot.json" in lines
58
+
59
+
60
+ def test_save_uses_sqlite_store_backup(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
61
+ """specsmith save should create a SQLite backup when ChronoStore is unavailable."""
62
+ monkeypatch.setenv("SPECSMITH_ESDB_BACKEND", "sqlite")
63
+ with SqliteStore(tmp_path) as store:
64
+ store.upsert(
65
+ SqliteRecord(
66
+ id="REQ-001",
67
+ kind="requirement",
68
+ label="SQLite fallback requirement",
69
+ )
70
+ )
71
+
72
+ result = _run(["save", "--project-dir", str(tmp_path), "--no-push", "--json"])
73
+ assert result.exit_code == 0, result.output
74
+ payload = json.loads(result.output)
75
+ backup_step = next(step for step in payload["steps"] if step["step"] == "esdb_backup")
76
+ assert backup_step["ok"] is True
77
+ assert backup_step["backend"] == "sqlite"
78
+
79
+ backup_path = Path(backup_step["path"])
80
+ assert backup_path.is_file()
81
+ assert backup_path.suffix == ".sqlite3"
@@ -93,6 +93,9 @@ class TestScaffoldCLIPython:
93
93
  assert ".chronomemory/backup/" in content
94
94
  assert ".specsmith/workitems.json" in content
95
95
  assert ".specsmith/esdb_migration_manifest.json" in content
96
+ assert "!.specsmith/esdb.sqlite3" in content
97
+ assert "!.chronomemory/events.wal" in content
98
+ assert "!.chronomemory/snapshot.json" in content
96
99
  assert "\n.specsmith/\n" not in content
97
100
  assert "\n.chronomemory/\n" not in content
98
101