specsmith 0.11.3.dev421__tar.gz → 0.11.3.dev425__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 (279) hide show
  1. {specsmith-0.11.3.dev421/src/specsmith.egg-info → specsmith-0.11.3.dev425}/PKG-INFO +1 -1
  2. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/pyproject.toml +1 -1
  3. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/broker.py +0 -1
  4. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/auditor.py +57 -12
  5. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/governance_logic.py +19 -9
  6. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/validator.py +12 -2
  7. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425/src/specsmith.egg-info}/PKG-INFO +1 -1
  8. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/LICENSE +0 -0
  9. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/README.md +0 -0
  10. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/setup.cfg +0 -0
  11. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/__init__.py +0 -0
  12. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/belief.py +0 -0
  13. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/certainty.py +0 -0
  14. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/failure_graph.py +0 -0
  15. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/py.typed +0 -0
  16. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/recovery.py +0 -0
  17. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/session.py +0 -0
  18. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/stress_tester.py +0 -0
  19. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/epistemic/trace.py +0 -0
  20. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/__init__.py +0 -0
  21. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/__main__.py +0 -0
  22. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/__init__.py +0 -0
  23. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/chat_runner.py +0 -0
  24. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/cleanup.py +0 -0
  25. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/context_seed.py +0 -0
  26. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/core.py +0 -0
  27. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/__init__.py +0 -0
  28. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/_status.py +0 -0
  29. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/dag.py +0 -0
  30. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  31. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/events.py +0 -0
  32. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/dispatch/result.py +0 -0
  33. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/endpoints.py +0 -0
  34. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/events.py +0 -0
  35. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/execution_profiles.py +0 -0
  36. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/fallback.py +0 -0
  37. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/hf_leaderboard.py +0 -0
  38. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/hf_sync.py +0 -0
  39. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/indexer.py +0 -0
  40. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/llm_client.py +0 -0
  41. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/mcp.py +0 -0
  42. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/memory.py +0 -0
  43. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/model_intelligence.py +0 -0
  44. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/model_profiles.py +0 -0
  45. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/orchestrator.py +0 -0
  46. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/permissions.py +0 -0
  47. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/profiles.py +0 -0
  48. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/provider_registry.py +0 -0
  49. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/repl.py +0 -0
  50. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/router.py +0 -0
  51. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/rules.py +0 -0
  52. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/runner.py +0 -0
  53. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/safety.py +0 -0
  54. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/spawner.py +0 -0
  55. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/suggester.py +0 -0
  56. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/teams.py +0 -0
  57. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/tools.py +0 -0
  58. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/verifier.py +0 -0
  59. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/agent/voice.py +0 -0
  60. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/architect.py +0 -0
  61. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/auth.py +0 -0
  62. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/block_export.py +0 -0
  63. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/channel.py +0 -0
  64. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/ci_manager.py +0 -0
  65. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/cli.py +0 -0
  66. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/commands/__init__.py +0 -0
  67. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/commands/intelligence.py +0 -0
  68. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/__init__.py +0 -0
  69. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/_compat.py +0 -0
  70. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/checker.py +0 -0
  71. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/evidence.py +0 -0
  72. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/regulations.py +0 -0
  73. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compliance/reporter.py +0 -0
  74. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/compressor.py +0 -0
  75. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/config.py +0 -0
  76. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/console_utils.py +0 -0
  77. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/context_orchestrator.py +0 -0
  78. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/context_window.py +0 -0
  79. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/credit_analyzer.py +0 -0
  80. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/credits.py +0 -0
  81. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/__init__.py +0 -0
  82. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/base.py +0 -0
  83. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/citations.py +0 -0
  84. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/fpd.py +0 -0
  85. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/odp.py +0 -0
  86. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/patentsview.py +0 -0
  87. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/pfw.py +0 -0
  88. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/ppubs.py +0 -0
  89. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/datasources/ptab.py +0 -0
  90. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/differ.py +0 -0
  91. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/doctor.py +0 -0
  92. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/drive.py +0 -0
  93. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/editor.py +0 -0
  94. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/__init__.py +0 -0
  95. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/belief.py +0 -0
  96. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/certainty.py +0 -0
  97. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/failure_graph.py +0 -0
  98. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/recovery.py +0 -0
  99. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/epistemic/stress_tester.py +0 -0
  100. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/eval/__init__.py +0 -0
  101. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/eval/builtins.py +0 -0
  102. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/eval/runner.py +0 -0
  103. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/executor.py +0 -0
  104. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/exporter.py +0 -0
  105. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/governance_store.py +0 -0
  106. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/governance_yaml.py +0 -0
  107. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/__init__.py +0 -0
  108. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/app.py +0 -0
  109. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/main_window.py +0 -0
  110. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/session_tab.py +0 -0
  111. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/theme.py +0 -0
  112. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/__init__.py +0 -0
  113. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/chat_view.py +0 -0
  114. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/input_bar.py +0 -0
  115. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  116. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/token_meter.py +0 -0
  117. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  118. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/widgets/update_checker.py +0 -0
  119. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/gui/worker.py +0 -0
  120. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/history_search.py +0 -0
  121. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/importer.py +0 -0
  122. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/instinct.py +0 -0
  123. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/__init__.py +0 -0
  124. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/agent_skill.py +0 -0
  125. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/aider.py +0 -0
  126. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/base.py +0 -0
  127. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/claude_code.py +0 -0
  128. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/copilot.py +0 -0
  129. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/cursor.py +0 -0
  130. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/gemini.py +0 -0
  131. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/integrations/windsurf.py +0 -0
  132. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/issue_reporter.py +0 -0
  133. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/languages.py +0 -0
  134. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/ledger.py +0 -0
  135. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/mcp_generator.py +0 -0
  136. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/__init__.py +0 -0
  137. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  138. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  139. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  140. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  141. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  142. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/migrations/runner.py +0 -0
  143. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/ollama_cmds.py +0 -0
  144. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/patent.py +0 -0
  145. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/paths.py +0 -0
  146. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/phase.py +0 -0
  147. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/plugins.py +0 -0
  148. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/profiles.py +0 -0
  149. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/rate_limits.py +0 -0
  150. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/releaser.py +0 -0
  151. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/requirements.py +0 -0
  152. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/requirements_parser.py +0 -0
  153. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/retrieval.py +0 -0
  154. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/safe_write.py +0 -0
  155. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/scaffolder.py +0 -0
  156. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/serve.py +0 -0
  157. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/session.py +0 -0
  158. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/session_init.py +0 -0
  159. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/session_store.py +0 -0
  160. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/__init__.py +0 -0
  161. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/cloud.py +0 -0
  162. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/corporate.py +0 -0
  163. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/cross_platform.py +0 -0
  164. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/devops.py +0 -0
  165. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/docs.py +0 -0
  166. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/embedded.py +0 -0
  167. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/governance.py +0 -0
  168. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/hardware.py +0 -0
  169. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/mobile.py +0 -0
  170. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/productivity.py +0 -0
  171. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills/ssh.py +0 -0
  172. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/skills_builder.py +0 -0
  173. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/sync.py +0 -0
  174. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/agents.md.j2 +0 -0
  175. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  176. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  177. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  178. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  179. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  180. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  181. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  182. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/community/security.md.j2 +0 -0
  183. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  184. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  185. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  186. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  187. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  188. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/editorconfig.j2 +0 -0
  189. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/gitattributes.j2 +0 -0
  190. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/gitignore.j2 +0 -0
  191. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/go/go.mod.j2 +0 -0
  192. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/go/main.go.j2 +0 -0
  193. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  194. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  195. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  196. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  197. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  198. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  199. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  200. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  201. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  202. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  203. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  204. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/js/package.json.j2 +0 -0
  205. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/ledger.md.j2 +0 -0
  206. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/python/cli.py.j2 +0 -0
  207. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/python/init.py.j2 +0 -0
  208. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  209. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/readme.md.j2 +0 -0
  210. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  211. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  212. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  213. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  214. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  215. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  216. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  217. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  218. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  219. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/tool_installer.py +0 -0
  220. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/toolrules.py +0 -0
  221. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/tools.py +0 -0
  222. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/trace.py +0 -0
  223. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/updater.py +0 -0
  224. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/upgrader.py +0 -0
  225. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs/__init__.py +0 -0
  226. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs/base.py +0 -0
  227. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs/bitbucket.py +0 -0
  228. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs/github.py +0 -0
  229. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs/gitlab.py +0 -0
  230. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/vcs_commands.py +0 -0
  231. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/wireframes.py +0 -0
  232. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith/workspace.py +0 -0
  233. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith.egg-info/SOURCES.txt +0 -0
  234. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith.egg-info/dependency_links.txt +0 -0
  235. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith.egg-info/entry_points.txt +0 -0
  236. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith.egg-info/requires.txt +0 -0
  237. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/src/specsmith.egg-info/top_level.txt +0 -0
  238. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_CMD_001.py +0 -0
  239. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_agent_profiles.py +0 -0
  240. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_agent_runner_ready.py +0 -0
  241. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_ai_client.py +0 -0
  242. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_ai_intelligence.py +0 -0
  243. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_auditor.py +0 -0
  244. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_channel.py +0 -0
  245. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_chat_diff_decision.py +0 -0
  246. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_chat_runner_openai_compat.py +0 -0
  247. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_chat_stdin_protocol.py +0 -0
  248. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_cli.py +0 -0
  249. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_cli_workflows_history_drive.py +0 -0
  250. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_compliance.py +0 -0
  251. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_compressor.py +0 -0
  252. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_dispatch.py +0 -0
  253. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_e2e_nexus.py +0 -0
  254. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_endpoints_cli.py +0 -0
  255. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_endpoints_store.py +0 -0
  256. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_epistemic.py +0 -0
  257. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_fallback_chain.py +0 -0
  258. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_importer.py +0 -0
  259. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_integrations.py +0 -0
  260. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_intelligence.py +0 -0
  261. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_issue_reporter.py +0 -0
  262. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_mcp_client.py +0 -0
  263. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_new_modules.py +0 -0
  264. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_nexus.py +0 -0
  265. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_permissions.py +0 -0
  266. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_phase1_4_new.py +0 -0
  267. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_phase34_completion.py +0 -0
  268. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_rate_limits.py +0 -0
  269. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_req_248_262.py +0 -0
  270. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_scaffolder.py +0 -0
  271. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_skill_marketplace.py +0 -0
  272. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_skills_mcp.py +0 -0
  273. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_smoke.py +0 -0
  274. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_suggester.py +0 -0
  275. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_tools.py +0 -0
  276. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_validator.py +0 -0
  277. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_vcs.py +0 -0
  278. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/tests/test_warp_parity.py +0 -0
  279. {specsmith-0.11.3.dev421 → specsmith-0.11.3.dev425}/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.11.3.dev421
3
+ Version: 0.11.3.dev425
4
4
  Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
5
5
  Author: BitConcepts
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.11.3.dev421"
7
+ version = "0.11.3.dev425"
8
8
  description = "Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -141,7 +141,6 @@ def classify_intent(utterance: str) -> Intent:
141
141
  # ---------------------------------------------------------------------------
142
142
 
143
143
 
144
- _REQ_HEADING = re.compile(r"^##\s+\d+\.\s+(?P<title>.+)\s*$", re.MULTILINE)
145
144
  # Extended to match project-prefixed IDs e.g. REQ-NN-001, REQ-CLI-042
146
145
  _REQ_ID = re.compile(r"-\s*\*\*ID:\*\*\s*(REQ-(?:[A-Z][A-Z0-9_]*-)?\d+)")
147
146
  _REQ_DESC = re.compile(r"-\s*\*\*Description:\*\*\s*(.+)")
@@ -238,7 +238,12 @@ _TEST_COVERS_PATTERN = re.compile(
238
238
 
239
239
 
240
240
  def check_req_test_consistency(root: Path) -> list[AuditResult]:
241
- """Check that every REQ has at least one TEST and vice versa."""
241
+ """Check that every REQ has at least one TEST and vice versa.
242
+
243
+ In YAML-first mode, REQ IDs are loaded from .specsmith/requirements.json
244
+ (the synced machine state) rather than docs/REQUIREMENTS.md, which may be
245
+ a derived artifact and not the canonical source (#174).
246
+ """
242
247
  results: list[AuditResult] = []
243
248
 
244
249
  req_path = root / "docs" / "REQUIREMENTS.md"
@@ -246,23 +251,48 @@ def check_req_test_consistency(root: Path) -> list[AuditResult]:
246
251
  test_candidates = _get_test_spec_paths(root)
247
252
  test_path = next((p for p in test_candidates if p.exists()), None)
248
253
 
249
- if not req_path.exists() or test_path is None:
254
+ if test_path is None:
250
255
  results.append(
251
256
  AuditResult(
252
257
  name="req-test-consistency",
253
258
  passed=True,
254
- message="Skipped: REQUIREMENTS.md or test spec file not found",
259
+ message="Skipped: test spec file not found",
255
260
  )
256
261
  )
257
262
  return results
258
263
 
259
- req_text = req_path.read_text(encoding="utf-8")
264
+ # In YAML-first mode load REQ IDs from the JSON machine state so we don't
265
+ # depend on docs/REQUIREMENTS.md being present or up to date (#174).
266
+ import contextlib as _contextlib
267
+ import json as _json_local
268
+
269
+ _reqs_json = root / ".specsmith" / "requirements.json"
270
+ _yaml_req_ids: set[str] | None = None
271
+ if _reqs_json.is_file():
272
+ with _contextlib.suppress(OSError, ValueError):
273
+ _records = _json_local.loads(_reqs_json.read_text(encoding="utf-8"))
274
+ _yaml_req_ids = {str(r["id"]) for r in _records if isinstance(r, dict) and r.get("id")}
275
+
276
+ if _yaml_req_ids is None and not req_path.exists():
277
+ results.append(
278
+ AuditResult(
279
+ name="req-test-consistency",
280
+ passed=True,
281
+ message="Skipped: REQUIREMENTS.md or requirements.json not found",
282
+ )
283
+ )
284
+ return results
285
+
286
+ req_text = req_path.read_text(encoding="utf-8") if req_path.exists() else ""
260
287
  test_text = test_path.read_text(encoding="utf-8")
261
288
 
262
289
  # Only check coverage for non-Draft requirements.
263
290
  # Draft requirements are stubs (e.g. auto-generated by import) and don't
264
291
  # need tests yet — checking them would produce noise on freshly imported projects.
265
- all_req_ids = set(_REQ_PATTERN.findall(req_text))
292
+ # Prefer JSON machine state (covers YAML-first mode); fall back to MD parsing (#174).
293
+ all_req_ids: set[str] = (
294
+ _yaml_req_ids if _yaml_req_ids is not None else set(_REQ_PATTERN.findall(req_text))
295
+ )
266
296
  draft_req_ids: set[str] = set()
267
297
  for block in re.split(r"(?=^## REQ-)", req_text, flags=re.MULTILINE):
268
298
  ids_in_block = set(_REQ_PATTERN.findall(block))
@@ -982,20 +1012,35 @@ def check_industrial_artifacts(root: Path) -> list[AuditResult]:
982
1012
  declared_eds = declared.get("canopen_eds", []) or []
983
1013
  declared_paths = {e.get("path", "") for e in declared_eds if isinstance(e, dict)}
984
1014
 
985
- # Scan for .eds and .xdd files
986
- skip_dirs = {".git", "node_modules", ".venv", "venv", "__pycache__"}
1015
+ # Scan for .eds and .xdd files — respect scan_exclude_dirs and
1016
+ # scan_exclude_patterns from scaffold.yml (#175).
1017
+ import fnmatch as _fnmatch
1018
+
1019
+ _raw_excl = raw.get("scan_exclude_dirs") or []
1020
+ _excl_dirs = {str(d).strip().rstrip("/") for d in _raw_excl if isinstance(d, str) and d.strip()}
1021
+ _raw_pat = raw.get("scan_exclude_patterns") or []
1022
+ _excl_patterns: list[str] = [str(p) for p in _raw_pat if isinstance(p, str) and p.strip()]
1023
+ # Always skip VCS / toolchain dirs
1024
+ skip_dirs = {".git", "node_modules", ".venv", "venv", "__pycache__"} | _excl_dirs
1025
+
987
1026
  found_eds: list[Path] = []
988
1027
  for ext in (".eds", ".xdd"):
989
1028
  for f in root.rglob(f"*{ext}"):
990
- if not any(p in skip_dirs for p in f.parts):
991
- found_eds.append(f)
1029
+ rel = f.relative_to(root).as_posix()
1030
+ # Skip if any path component is in skip_dirs
1031
+ if any(p in skip_dirs for p in f.parts):
1032
+ continue
1033
+ # Skip if the relative path matches any exclude pattern
1034
+ if any(_fnmatch.fnmatch(rel, pat) for pat in _excl_patterns):
1035
+ continue
1036
+ found_eds.append(f)
992
1037
 
993
1038
  if not found_eds:
994
1039
  return results # No industrial artifacts found
995
1040
 
996
- undeclared = [
997
- f for f in found_eds if str(f.relative_to(root)).replace("\\\\", "/") not in declared_paths
998
- ]
1041
+ # Use as_posix() for cross-platform comparison — avoids single-backslash
1042
+ # mismatch on Windows where str(Path) uses '\' but declared paths use '/' (#173).
1043
+ undeclared = [f for f in found_eds if f.relative_to(root).as_posix() not in declared_paths]
999
1044
 
1000
1045
  if undeclared:
1001
1046
  results.append(
@@ -23,8 +23,12 @@ def _safe_resolve(path: str | Path) -> Path:
23
23
  """Resolve a project directory path and reject traversal sequences.
24
24
 
25
25
  Validates null bytes and traversal components in the ORIGINAL input
26
- BEFORE calling resolve(), then returns the canonical absolute path.
27
- CodeQL ``py/path-injection``: Path.resolve() is the sanitiser here.
26
+ BEFORE canonicalising, then returns the absolute real path.
27
+
28
+ Uses ``os.path.realpath`` as the final step — CodeQL's Python
29
+ ``py/path-injection`` taint library recognises ``os.path.realpath``
30
+ as an explicit sanitiser, so any value returned by this function is
31
+ considered untainted and downstream path operations are not flagged.
28
32
  """
29
33
  raw = str(path)
30
34
  if "\x00" in raw:
@@ -32,8 +36,11 @@ def _safe_resolve(path: str | Path) -> Path:
32
36
  for part in Path(raw).parts:
33
37
  if part in ("..", "..."):
34
38
  raise ValueError(f"Path traversal rejected: {raw!r}")
35
- # Validate BEFORE resolve so traversal components are caught first.
36
- return Path(path).resolve() # lgtm[py/path-injection]
39
+ # os.path.realpath resolves symlinks and makes the path absolute.
40
+ # It is a CodeQL-recognised sanitiser for py/path-injection, unlike
41
+ # pathlib.Path.resolve() which CodeQL does not track across function
42
+ # boundaries.
43
+ return Path(os.path.realpath(raw))
37
44
 
38
45
 
39
46
  def run_preflight(
@@ -84,8 +91,13 @@ def run_preflight(
84
91
  explicit_test_ids = [m.upper() for m in _EXPLICIT_TEST.findall(utterance)]
85
92
 
86
93
  # Validate explicit REQ IDs against requirements.json and add any that match.
94
+ # Paths below are: resolved trusted root + constant string suffixes — no user
95
+ # data flows into the filename components. The intermediary names are local
96
+ # constants to make this obvious to static analysis tools.
97
+ _REQS_SUFFIX = ".specsmith/requirements.json"
98
+ _TC_SUFFIX = ".specsmith/testcases.json"
87
99
  if explicit_req_ids:
88
- rq_json = (root / ".specsmith" / "requirements.json").resolve()
100
+ rq_json = (root / _REQS_SUFFIX).resolve()
89
101
  if rq_json.is_file():
90
102
  try:
91
103
  rq_records = _json.loads(rq_json.read_text(encoding="utf-8"))
@@ -100,7 +112,7 @@ def run_preflight(
100
112
  test_case_ids: list[str] = []
101
113
  # Include any explicitly named TEST-* IDs from the utterance.
102
114
  if explicit_test_ids:
103
- tc_json_explicit = (root / ".specsmith" / "testcases.json").resolve()
115
+ tc_json_explicit = (root / _TC_SUFFIX).resolve()
104
116
  if tc_json_explicit.is_file():
105
117
  try:
106
118
  tc_explicit = _json.loads(tc_json_explicit.read_text(encoding="utf-8"))
@@ -111,9 +123,7 @@ def run_preflight(
111
123
  if eid in known_tc_ids:
112
124
  test_case_ids.append(eid)
113
125
  if requirement_ids:
114
- # .resolve() here clears taint for CodeQL py/path-injection; path is
115
- # constructed from a validated root with a constant suffix.
116
- tc_json = (root / ".specsmith" / "testcases.json").resolve()
126
+ tc_json = (root / _TC_SUFFIX).resolve()
117
127
  if tc_json.is_file():
118
128
  try:
119
129
  records = _json.loads(tc_json.read_text(encoding="utf-8"))
@@ -159,7 +159,12 @@ def _check_agents_md_refs(root: Path) -> list[ValidationResult]:
159
159
 
160
160
 
161
161
  def _check_req_ids_unique(root: Path) -> list[ValidationResult]:
162
- """Check that requirement IDs are unique within REQUIREMENTS.md."""
162
+ """Check that requirement IDs are unique within REQUIREMENTS.md.
163
+
164
+ Uses only the canonical **ID:** field to count each REQ once (#171).
165
+ The generated REQUIREMENTS.md repeats each ID in both the section heading
166
+ and the '**ID:** REQ-XXX' field — a raw findall would double-count every ID.
167
+ """
163
168
  results: list[ValidationResult] = []
164
169
  req_path = root / "docs" / "REQUIREMENTS.md"
165
170
 
@@ -167,7 +172,12 @@ def _check_req_ids_unique(root: Path) -> list[ValidationResult]:
167
172
  return results
168
173
 
169
174
  text = req_path.read_text(encoding="utf-8")
170
- req_ids = _REQ_PATTERN.findall(text)
175
+ # Match only canonical ID declarations, not heading or cross-references (#171).
176
+ # Pattern matches '- **ID:** REQ-XXX' or '**ID:** REQ-XXX' lines.
177
+ _ID_FIELD = re.compile(r"\*\*ID:\*\*\s*(REQ-(?:[A-Z]+-)*\d+)")
178
+ id_field_matches = _ID_FIELD.findall(text)
179
+ # Fall back to full scan if the markdown has no **ID:** fields (legacy format).
180
+ req_ids = id_field_matches if id_field_matches else _REQ_PATTERN.findall(text)
171
181
 
172
182
  seen: dict[str, int] = {}
173
183
  for rid in req_ids:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.11.3.dev421
3
+ Version: 0.11.3.dev425
4
4
  Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
5
5
  Author: BitConcepts
6
6
  License-Expression: MIT