invar-tools 1.4.0__tar.gz → 1.5.0__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 (273) hide show
  1. invar_tools-1.5.0/.claude/hooks/PostToolUse.sh +3 -0
  2. invar_tools-1.5.0/.claude/hooks/PreToolUse.sh +3 -0
  3. invar_tools-1.5.0/.claude/hooks/Stop.sh +3 -0
  4. invar_tools-1.5.0/.claude/hooks/UserPromptSubmit.sh +3 -0
  5. invar_tools-1.5.0/.claude/hooks/invar.PostToolUse.sh +102 -0
  6. invar_tools-1.5.0/.claude/hooks/invar.PreToolUse.sh +74 -0
  7. invar_tools-1.5.0/.claude/hooks/invar.Stop.sh +23 -0
  8. invar_tools-1.5.0/.claude/hooks/invar.UserPromptSubmit.sh +386 -0
  9. {invar_tools-1.4.0 → invar_tools-1.5.0}/.claude/skills/develop/SKILL.md +37 -2
  10. {invar_tools-1.4.0 → invar_tools-1.5.0}/.claude/skills/investigate/SKILL.md +1 -2
  11. {invar_tools-1.4.0 → invar_tools-1.5.0}/.claude/skills/propose/SKILL.md +1 -2
  12. invar_tools-1.5.0/.claude/skills/review/SKILL.md +337 -0
  13. invar_tools-1.5.0/.invar/examples/functional.py +450 -0
  14. invar_tools-1.5.0/.invar/mcp-setup.md +83 -0
  15. invar_tools-1.5.0/.serena/cache/python/document_symbols.pkl +0 -0
  16. invar_tools-1.5.0/.serena/cache/python/raw_document_symbols.pkl +0 -0
  17. {invar_tools-1.4.0 → invar_tools-1.5.0}/CLAUDE.md +1 -0
  18. {invar_tools-1.4.0 → invar_tools-1.5.0}/PKG-INFO +12 -8
  19. {invar_tools-1.4.0 → invar_tools-1.5.0}/README.md +11 -7
  20. invar_tools-1.5.0/docs/guides/aider.md +475 -0
  21. invar_tools-1.5.0/docs/guides/cline.md +355 -0
  22. invar_tools-1.5.0/docs/guides/continue.md +466 -0
  23. invar_tools-1.5.0/docs/guides/cursor.md +442 -0
  24. invar_tools-1.5.0/docs/guides/multi-agent.md +226 -0
  25. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/index.html +7 -5
  26. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-61-functional-pattern-guidance.md +3 -1
  27. invar_tools-1.5.0/docs/proposals/DX-61-implementation-design.md +1466 -0
  28. invar_tools-1.5.0/docs/proposals/DX-62-proactive-reference-reading.md +360 -0
  29. invar_tools-1.5.0/docs/proposals/DX-63-contracts-first-enforcement.md +511 -0
  30. invar_tools-1.5.0/docs/proposals/LX-02-agent-portability-analysis.md +445 -0
  31. invar_tools-1.5.0/docs/proposals/LX-03-multi-agent-support.md +422 -0
  32. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/index.md +25 -4
  33. {invar_tools-1.4.0 → invar_tools-1.5.0}/pyproject.toml +10 -2
  34. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/formatter.py +6 -1
  35. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/models.py +13 -0
  36. invar_tools-1.5.0/src/invar/core/patterns/__init__.py +53 -0
  37. invar_tools-1.5.0/src/invar/core/patterns/detector.py +249 -0
  38. invar_tools-1.5.0/src/invar/core/patterns/p0_exhaustive.py +207 -0
  39. invar_tools-1.5.0/src/invar/core/patterns/p0_literal.py +307 -0
  40. invar_tools-1.5.0/src/invar/core/patterns/p0_newtype.py +211 -0
  41. invar_tools-1.5.0/src/invar/core/patterns/p0_nonempty.py +307 -0
  42. invar_tools-1.5.0/src/invar/core/patterns/p0_validation.py +278 -0
  43. invar_tools-1.5.0/src/invar/core/patterns/registry.py +234 -0
  44. invar_tools-1.5.0/src/invar/core/patterns/types.py +167 -0
  45. invar_tools-1.5.0/src/invar/core/trivial_detection.py +189 -0
  46. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/mcp/server.py +4 -0
  47. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/guard.py +65 -0
  48. invar_tools-1.5.0/src/invar/shell/contract_coverage.py +358 -0
  49. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/guard_output.py +5 -0
  50. invar_tools-1.5.0/src/invar/shell/pattern_integration.py +234 -0
  51. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/testing.py +13 -2
  52. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/config/CLAUDE.md.jinja +1 -0
  53. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/skills/develop/SKILL.md.jinja +49 -0
  54. invar_tools-1.5.0/src/invar/templates/skills/review/SKILL.md.jinja +338 -0
  55. invar_tools-1.5.0/src/shell/__init__.py +0 -0
  56. invar_tools-1.5.0/tests/integration/__init__.py +0 -0
  57. invar_tools-1.4.0/.claude/skills/review/SKILL.md +0 -173
  58. invar_tools-1.4.0/src/invar/templates/skills/review/SKILL.md.jinja +0 -173
  59. {invar_tools-1.4.0 → invar_tools-1.5.0}/.aider.conf.yml +0 -0
  60. {invar_tools-1.4.0 → invar_tools-1.5.0}/.claude/commands/audit.md +0 -0
  61. {invar_tools-1.4.0 → invar_tools-1.5.0}/.claude/commands/guard.md +0 -0
  62. {invar_tools-1.4.0 → invar_tools-1.5.0}/.cursorrules +0 -0
  63. {invar_tools-1.4.0 → invar_tools-1.5.0}/.github/workflows/ci.yml +0 -0
  64. {invar_tools-1.4.0 → invar_tools-1.5.0}/.github/workflows/publish.yml +0 -0
  65. {invar_tools-1.4.0 → invar_tools-1.5.0}/.gitignore +0 -0
  66. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/archive/sessions-2025-12.md +0 -0
  67. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/context.md +0 -0
  68. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/examples/README.md +0 -0
  69. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/examples/conftest.py +0 -0
  70. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/examples/contracts.py +0 -0
  71. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/examples/core_shell.py +0 -0
  72. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/examples/workflow.md +0 -0
  73. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/project-additions.md +0 -0
  74. {invar_tools-1.4.0 → invar_tools-1.5.0}/.invar/proposals/TEMPLATE.md +0 -0
  75. {invar_tools-1.4.0 → invar_tools-1.5.0}/.mcp.json +0 -0
  76. {invar_tools-1.4.0 → invar_tools-1.5.0}/.pre-commit-config.yaml +0 -0
  77. {invar_tools-1.4.0 → invar_tools-1.5.0}/.serena/.gitignore +0 -0
  78. {invar_tools-1.4.0 → invar_tools-1.5.0}/.serena/project.yml +0 -0
  79. {invar_tools-1.4.0 → invar_tools-1.5.0}/INVAR.md +0 -0
  80. {invar_tools-1.4.0 → invar_tools-1.5.0}/LICENSE +0 -0
  81. {invar_tools-1.4.0 → invar_tools-1.5.0}/LICENSE-GPL +0 -0
  82. {invar_tools-1.4.0 → invar_tools-1.5.0}/NOTICE +0 -0
  83. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/.nojekyll +0 -0
  84. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/agents.md +0 -0
  85. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/design.md +0 -0
  86. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/diagrams.md +0 -0
  87. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/guide.md +0 -0
  88. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/decisions-2024.md +0 -0
  89. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/feedback/compliance-analysis.md +0 -0
  90. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/feedback/feedback-memo.md +0 -0
  91. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/feedback/index.md +0 -0
  92. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/index.md +0 -0
  93. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/original-vision.md +0 -0
  94. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/protocol-evolution.md +0 -0
  95. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/history/research/cruxeval-quick-validation.md +0 -0
  96. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/logo.svg +0 -0
  97. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-23-entry-point-detection.md +0 -0
  98. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-25-functional-patterns.md +0 -0
  99. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-29-pure-content-detection.md +0 -0
  100. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-37-coverage-integration.md +0 -0
  101. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-38-contract-quality-rules.md +0 -0
  102. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-39-workflow-efficiency.md +0 -0
  103. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-40-smart-tool-redirect-hook.md +0 -0
  104. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-41-automatic-review-orchestration.md +0 -0
  105. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-42-workflow-auto-routing.md +0 -0
  106. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-43-cross-platform-distribution.md +0 -0
  107. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-46-documentation-audit.md +0 -0
  108. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-48-code-structure-reorganization.md +0 -0
  109. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-49-protocol-distribution-unification.md +0 -0
  110. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-50-workflow-enforcement.md +0 -0
  111. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-51-workflow-phase-visibility.md +0 -0
  112. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-52-venv-dependency-injection.md +0 -0
  113. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-53-review-loop-effectiveness.md +0 -0
  114. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-54-agent-native-context-management.md +0 -0
  115. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-55-claude-init-conflict-resolution.md +0 -0
  116. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-56-template-sync-unification.md +0 -0
  117. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/DX-60-structured-rules-ssot.md +0 -0
  118. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/LX-01-multi-language-feasibility.md +0 -0
  119. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2024-12-21-guard-enhancements.md +0 -0
  120. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2024-12-21-language-inspired-enhancements.md +0 -0
  121. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2024-12-21-test-first-enhancement.md +0 -0
  122. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2025-12-21-dx-improvements.md +0 -0
  123. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2025-12-23-dx-20-property-testing-enhancements.md +0 -0
  124. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/2025-12-23-dx-21-package-and-init.md +0 -0
  125. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/AGENT-IMPROVEMENTS.md +0 -0
  126. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-11-documentation-restructure.md +0 -0
  127. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-12-hypothesis-fallback.md +0 -0
  128. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-13-incremental-prove.md +0 -0
  129. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-14-expanded-prove-usage.md +0 -0
  130. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-16-agent-tool-enforcement.md +0 -0
  131. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-17-workflow-enforcement.md +0 -0
  132. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-22-verification-strategy.md +0 -0
  133. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-24-mechanism-documentation.md +0 -0
  134. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-26-guard-simplification.md +0 -0
  135. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-27-system-prompt-protocol.md +0 -0
  136. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-28-semantic-verification.md +0 -0
  137. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-30-visible-workflow.md +0 -0
  138. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-31-adversarial-reviewer.md +0 -0
  139. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-32-workflow-iteration.md +0 -0
  140. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-33-verification-blind-spots.md +0 -0
  141. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-34-review-cycle.md +0 -0
  142. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-35-workflow-phase-separation.md +0 -0
  143. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-36-documentation-restructuring.md +0 -0
  144. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-45-template-consistency.md +0 -0
  145. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-47-command-skill-naming.md +0 -0
  146. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-57-claude-code-hooks.md +0 -0
  147. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/proposals/completed/DX-58-document-structure-optimization.md +0 -0
  148. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/architecture/index.md +0 -0
  149. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/contracts/advanced.md +0 -0
  150. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/contracts/completeness.md +0 -0
  151. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/contracts/doctests.md +0 -0
  152. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/contracts/index.md +0 -0
  153. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/contracts/pre-post.md +0 -0
  154. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/documentation.md +0 -0
  155. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/index.md +0 -0
  156. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/proposal-workflow.md +0 -0
  157. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/rules/index.md +0 -0
  158. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/rules/severity.md +0 -0
  159. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/verification/crosshair-vs-hypothesis.md +0 -0
  160. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/verification/index.md +0 -0
  161. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/verification/smart-routing.md +0 -0
  162. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/workflow/index.md +0 -0
  163. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/workflow/session-start.md +0 -0
  164. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/reference/workflow/usbv.md +0 -0
  165. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/terminal-gif-guide.md +0 -0
  166. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/test-reports/DX-55-test-report.md +0 -0
  167. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/test-reports/DX-56-test-report.md +0 -0
  168. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/test-reports/DX-58-test-scenario.md +0 -0
  169. {invar_tools-1.4.0 → invar_tools-1.5.0}/docs/vision.md +0 -0
  170. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/LICENSE +0 -0
  171. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/README.md +0 -0
  172. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/pyproject.toml +0 -0
  173. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/__init__.py +0 -0
  174. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/contracts.py +0 -0
  175. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/decorators.py +0 -0
  176. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/invariant.py +0 -0
  177. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/relations.py +0 -0
  178. {invar_tools-1.4.0 → invar_tools-1.5.0}/runtime/src/invar_runtime/resource.py +0 -0
  179. {invar_tools-1.4.0 → invar_tools-1.5.0}/scripts/smart-guard.sh +0 -0
  180. {invar_tools-1.4.0/tests/integration → invar_tools-1.5.0/src/core}/__init__.py +0 -0
  181. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/__init__.py +0 -0
  182. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/__init__.py +0 -0
  183. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/contracts.py +0 -0
  184. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/entry_points.py +0 -0
  185. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/extraction.py +0 -0
  186. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/format_specs.py +0 -0
  187. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/format_strategies.py +0 -0
  188. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/hypothesis_strategies.py +0 -0
  189. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/inspect.py +0 -0
  190. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/lambda_helpers.py +0 -0
  191. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/must_use.py +0 -0
  192. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/parser.py +0 -0
  193. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/postcondition_scope.py +0 -0
  194. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/property_gen.py +0 -0
  195. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/purity.py +0 -0
  196. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/purity_heuristics.py +0 -0
  197. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/references.py +0 -0
  198. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/review_trigger.py +0 -0
  199. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/rule_meta.py +0 -0
  200. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/rules.py +0 -0
  201. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/shell_analysis.py +0 -0
  202. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/shell_architecture.py +0 -0
  203. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/strategies.py +0 -0
  204. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/suggestions.py +0 -0
  205. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/sync_helpers.py +0 -0
  206. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/tautology.py +0 -0
  207. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/template_parser.py +0 -0
  208. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/timeout_inference.py +0 -0
  209. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/utils.py +0 -0
  210. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/core/verification_routing.py +0 -0
  211. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/mcp/__init__.py +0 -0
  212. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/mcp/__main__.py +0 -0
  213. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/py.typed +0 -0
  214. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/__init__.py +0 -0
  215. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/claude_hooks.py +0 -0
  216. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/__init__.py +0 -0
  217. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/hooks.py +0 -0
  218. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/init.py +0 -0
  219. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/merge.py +0 -0
  220. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/mutate.py +0 -0
  221. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/perception.py +0 -0
  222. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/sync_self.py +0 -0
  223. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/template_sync.py +0 -0
  224. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/test.py +0 -0
  225. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/commands/update.py +0 -0
  226. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/config.py +0 -0
  227. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/coverage.py +0 -0
  228. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/fs.py +0 -0
  229. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/git.py +0 -0
  230. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/guard_helpers.py +0 -0
  231. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/mcp_config.py +0 -0
  232. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/mutation.py +0 -0
  233. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/property_tests.py +0 -0
  234. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/prove/__init__.py +0 -0
  235. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/prove/accept.py +0 -0
  236. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/prove/cache.py +0 -0
  237. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/prove/crosshair.py +0 -0
  238. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/prove/hypothesis.py +0 -0
  239. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/subprocess_env.py +0 -0
  240. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/template_engine.py +0 -0
  241. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/shell/templates.py +0 -0
  242. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/CLAUDE.md.template +0 -0
  243. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/__init__.py +0 -0
  244. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/aider.conf.yml.template +0 -0
  245. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/commands/audit.md +0 -0
  246. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/commands/guard.md +0 -0
  247. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/config/context.md.jinja +0 -0
  248. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/config/pre-commit.yaml.jinja +0 -0
  249. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/context.md.template +0 -0
  250. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/cursorrules.template +0 -0
  251. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/examples/README.md +0 -0
  252. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/examples/conftest.py +0 -0
  253. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/examples/contracts.py +0 -0
  254. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/examples/core_shell.py +0 -0
  255. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/examples/workflow.md +0 -0
  256. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/hooks/PostToolUse.sh.jinja +0 -0
  257. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/hooks/PreToolUse.sh.jinja +0 -0
  258. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/hooks/Stop.sh.jinja +0 -0
  259. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/hooks/UserPromptSubmit.sh.jinja +0 -0
  260. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/hooks/__init__.py +0 -0
  261. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/manifest.toml +0 -0
  262. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/pre-commit-config.yaml.template +0 -0
  263. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/proposal.md.template +0 -0
  264. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/protocol/INVAR.md +0 -0
  265. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/skills/investigate/SKILL.md.jinja +0 -0
  266. {invar_tools-1.4.0 → invar_tools-1.5.0}/src/invar/templates/skills/propose/SKILL.md.jinja +0 -0
  267. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/__init__.py +0 -0
  268. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/conftest.py +0 -0
  269. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/integration/test_cli_flags.py +0 -0
  270. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/integration/test_dx55_regression.py +0 -0
  271. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/integration/test_dx56_sync.py +0 -0
  272. {invar_tools-1.4.0 → invar_tools-1.5.0}/tests/test_subprocess_env.py +0 -0
  273. {invar_tools-1.4.0 → invar_tools-1.5.0}/uv.lock +0 -0
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Invar hook wrapper (DX-57)
3
+ source "$(dirname "$0")/invar.PostToolUse.sh" "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Invar hook wrapper (DX-57)
3
+ source "$(dirname "$0")/invar.PreToolUse.sh" "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Invar hook wrapper (DX-57)
3
+ source "$(dirname "$0")/invar.Stop.sh" "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Invar hook wrapper (DX-57)
3
+ source "$(dirname "$0")/invar.UserPromptSubmit.sh" "$@"
@@ -0,0 +1,102 @@
1
+ #!/bin/bash
2
+ # Invar PostToolUse Hook
3
+ # Protocol: v5.0 | Generated: 2025-12-29
4
+ # DX-57: Git-based change detection with fallback
5
+
6
+ TOOL_NAME="$1"
7
+
8
+ # Check if hooks are disabled
9
+ [[ -f ".claude/hooks/.invar_disabled" ]] && exit 0
10
+
11
+ # Use session-specific state directory
12
+ STATE_DIR="${CLAUDE_STATE_DIR:-/tmp/invar_hooks_$(id -u)}"
13
+ mkdir -p "$STATE_DIR" 2>/dev/null
14
+
15
+ CHANGES_FILE="$STATE_DIR/changes"
16
+ LAST_GUARD="$STATE_DIR/last_guard"
17
+ LAST_CHECK_MARKER="$STATE_DIR/last_check"
18
+
19
+ # ============================================
20
+ # Reset state on guard run (MCP or CLI)
21
+ # ============================================
22
+ # MCP: invar_guard tool call
23
+ if [[ "$TOOL_NAME" == "mcp__invar__invar_guard" ]]; then
24
+ date +%s > "$LAST_GUARD"
25
+ rm -f "$CHANGES_FILE"
26
+ touch "$LAST_CHECK_MARKER"
27
+ exit 0
28
+ fi
29
+
30
+ # CLI: Bash command containing "invar guard"
31
+ if [[ "$TOOL_NAME" == "Bash" ]]; then
32
+ TOOL_INPUT="$2"
33
+ if echo "$TOOL_INPUT" | grep -qE '"command"[^}]*invar\s+guard'; then
34
+ date +%s > "$LAST_GUARD"
35
+ rm -f "$CHANGES_FILE"
36
+ touch "$LAST_CHECK_MARKER"
37
+ exit 0
38
+ fi
39
+ fi
40
+
41
+ # ============================================
42
+ # Detect changes (git with fallback)
43
+ # ============================================
44
+ is_git_repo() {
45
+ git rev-parse --git-dir >/dev/null 2>&1
46
+ }
47
+
48
+ detect_changes() {
49
+ if is_git_repo; then
50
+ # Primary: Git-based detection (includes staged + unstaged)
51
+ { git diff --name-only -- '*.py' 2>/dev/null; git diff --cached --name-only -- '*.py' 2>/dev/null; } | sort -u
52
+ elif [[ -f "$LAST_CHECK_MARKER" ]]; then
53
+ # Fallback: Timestamp-based detection (approximate)
54
+ find . -name "*.py" -newer "$LAST_CHECK_MARKER" -type f 2>/dev/null | \
55
+ grep -v __pycache__ | grep -v '.venv' | head -20
56
+ fi
57
+ # Update marker for next check
58
+ touch "$LAST_CHECK_MARKER" 2>/dev/null
59
+ }
60
+
61
+ # Track changes
62
+ CHANGED=$(detect_changes)
63
+ if [[ -n "$CHANGED" ]]; then
64
+ echo "$CHANGED" >> "$CHANGES_FILE"
65
+ sort -u "$CHANGES_FILE" -o "$CHANGES_FILE" 2>/dev/null
66
+ fi
67
+
68
+ # ============================================
69
+ # Smart trigger evaluation
70
+ # ============================================
71
+ CHANGE_COUNT=$(wc -l < "$CHANGES_FILE" 2>/dev/null | tr -d ' ' || echo 0)
72
+ LAST_TIME=$(cat "$LAST_GUARD" 2>/dev/null || echo 0)
73
+ NOW=$(date +%s)
74
+ ELAPSED=$((NOW - LAST_TIME))
75
+
76
+ SHOULD_REMIND=false
77
+ REASON=""
78
+
79
+ # Trigger 1: Accumulated changes (3+ files)
80
+ if [[ $CHANGE_COUNT -ge 3 ]]; then
81
+ SHOULD_REMIND=true
82
+ REASON="$CHANGE_COUNT files changed"
83
+ fi
84
+
85
+ # Trigger 2: Core file changed (high priority)
86
+ if grep -qE "/core/|/contracts/" "$CHANGES_FILE" 2>/dev/null; then
87
+ SHOULD_REMIND=true
88
+ REASON="core/ files modified"
89
+ fi
90
+
91
+ # Trigger 3: Time threshold (>5 min with changes)
92
+ if [[ $ELAPSED -gt 300 && $CHANGE_COUNT -gt 0 ]]; then
93
+ SHOULD_REMIND=true
94
+ REASON=">5 min since last guard"
95
+ fi
96
+
97
+ # Output reminder if triggered
98
+ if [[ "$SHOULD_REMIND" == "true" ]]; then
99
+ echo ""
100
+ echo "⚠️ Verification suggested: $REASON"
101
+ echo " Run: invar_guard --changed"
102
+ fi
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+ # Invar PreToolUse Hook
3
+ # Protocol: v5.0 | Generated: 2025-12-29
4
+ # DX-57: Smart blocking with auto-escape for pytest/crosshair
5
+
6
+ TOOL_NAME="$1"
7
+ TOOL_INPUT="$2"
8
+
9
+ # Check if hooks are disabled
10
+ [[ -f ".claude/hooks/.invar_disabled" ]] && exit 0
11
+
12
+ # Only process Bash commands
13
+ [[ "$TOOL_NAME" != "Bash" ]] && exit 0
14
+
15
+ # Parse command from JSON input
16
+ # Primary: jq (accurate), Fallback: grep/sed (basic)
17
+ if command -v jq &>/dev/null; then
18
+ CMD=$(echo "$TOOL_INPUT" | jq -r '.command // empty' 2>/dev/null)
19
+ else
20
+ # Fallback: Extract command field using grep/sed (handles simple cases)
21
+ CMD=$(echo "$TOOL_INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"\(.*\)"/\1/')
22
+ fi
23
+ [[ -z "$CMD" ]] && exit 0
24
+
25
+ # ============================================
26
+ # pytest blocking with smart escape
27
+ # ============================================
28
+ if echo "$CMD" | grep -qE '\bpytest\b|python.*-m\s+pytest\b'; then
29
+
30
+ # Auto-escape 1: Debugging mode (--pdb, --debug, --tb=long)
31
+ if echo "$CMD" | grep -qE '\-\-pdb|\-\-debug|\-\-tb='; then
32
+ echo "⚠️ pytest debugging allowed. Run invar_guard after."
33
+ exit 0
34
+ fi
35
+
36
+ # Auto-escape 2: External/vendor tests
37
+ if echo "$CMD" | grep -qE 'vendor/|third_party/|external/|node_modules/'; then
38
+ exit 0
39
+ fi
40
+
41
+ # Auto-escape 3: Explicit coverage collection
42
+ if echo "$CMD" | grep -qE '\-\-cov'; then
43
+ echo "⚠️ pytest coverage allowed. Run invar_guard for contract verification."
44
+ exit 0
45
+ fi
46
+
47
+ # Auto-escape 4: Environment variable override
48
+ if [[ "$INVAR_ALLOW_PYTEST" == "1" ]]; then
49
+ exit 0
50
+ fi
51
+
52
+ # Default: Block with helpful message
53
+ echo "❌ Use invar_guard instead of pytest"
54
+ echo " invar_guard = static + doctests + CrossHair + Hypothesis"
55
+ echo ""
56
+ echo " Auto-allowed: pytest --pdb (debug), pytest --cov (coverage)"
57
+ echo " Manual escape: INVAR_ALLOW_PYTEST=1 pytest ..."
58
+ exit 1
59
+ fi
60
+
61
+ # ============================================
62
+ # crosshair blocking (always redirect)
63
+ # ============================================
64
+ if echo "$CMD" | grep -qE '\bcrosshair\b'; then
65
+ if [[ "$INVAR_ALLOW_CROSSHAIR" == "1" ]]; then
66
+ exit 0
67
+ fi
68
+
69
+ echo "❌ Use invar_guard (includes CrossHair by default)"
70
+ echo " Manual escape: INVAR_ALLOW_CROSSHAIR=1 crosshair ..."
71
+ exit 1
72
+ fi
73
+
74
+ exit 0
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+ # Invar Stop Hook
3
+ # Protocol: v5.0 | Generated: 2025-12-29
4
+ # DX-57: Session cleanup and unverified changes warning
5
+
6
+ # Use session-specific state
7
+ STATE_DIR="${CLAUDE_STATE_DIR:-/tmp/invar_hooks_$(id -u)}"
8
+ CHANGES_FILE="$STATE_DIR/changes"
9
+
10
+ # Check for unverified changes
11
+ if [[ -f "$CHANGES_FILE" ]]; then
12
+ CHANGE_COUNT=$(wc -l < "$CHANGES_FILE" 2>/dev/null | tr -d ' ' || echo 0)
13
+ if [[ $CHANGE_COUNT -gt 0 ]]; then
14
+ echo ""
15
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
16
+ echo "⚠️ Invar: $CHANGE_COUNT Python files not verified"
17
+ echo " Run before commit: invar_guard --changed"
18
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
19
+ fi
20
+ fi
21
+
22
+ # Cleanup session state
23
+ rm -rf "$STATE_DIR" 2>/dev/null
@@ -0,0 +1,386 @@
1
+ #!/bin/bash
2
+ # Invar UserPromptSubmit Hook
3
+ # Protocol: v5.0 | Generated: 2025-12-29
4
+ # DX-57: Protocol refresh with full INVAR.md injection
5
+
6
+ USER_MESSAGE="$1"
7
+
8
+ # Check if hooks are disabled
9
+ [[ -f ".claude/hooks/.invar_disabled" ]] && exit 0
10
+
11
+ # Use session-specific state
12
+ STATE_DIR="${CLAUDE_STATE_DIR:-/tmp/invar_hooks_$(id -u)}"
13
+ mkdir -p "$STATE_DIR" 2>/dev/null
14
+
15
+ # Session detection: Reset if state is stale (>4 hours)
16
+ SESSION_MARKER="$STATE_DIR/session_start"
17
+ MAX_AGE_SECONDS=14400 # 4 hours
18
+
19
+ reset_session() {
20
+ rm -f "$STATE_DIR/msg_count" "$STATE_DIR/changes" 2>/dev/null
21
+ date +%s > "$SESSION_MARKER"
22
+ }
23
+
24
+ if [[ -f "$SESSION_MARKER" ]]; then
25
+ MARKER_TIME=$(cat "$SESSION_MARKER" 2>/dev/null || echo 0)
26
+ NOW=$(date +%s)
27
+ AGE=$((NOW - MARKER_TIME))
28
+ if [[ $AGE -gt $MAX_AGE_SECONDS ]]; then
29
+ reset_session
30
+ fi
31
+ else
32
+ reset_session
33
+ fi
34
+
35
+ COUNT_FILE="$STATE_DIR/msg_count"
36
+ COUNT=$(cat "$COUNT_FILE" 2>/dev/null || echo 0)
37
+ COUNT=$((COUNT + 1))
38
+ echo "$COUNT" > "$COUNT_FILE"
39
+
40
+ # ============================================
41
+ # Keyword triggers (independent of count)
42
+ # ============================================
43
+
44
+ # pytest intent → immediate correction
45
+ if echo "$USER_MESSAGE" | grep -qiE "run.*pytest|pytest.*test|用.*pytest"; then
46
+ echo "<system-reminder>Use invar_guard, not pytest.</system-reminder>"
47
+ fi
48
+
49
+ # Implementation intent → workflow reminder (after warmup)
50
+ if [[ $COUNT -gt 3 ]]; then
51
+ if echo "$USER_MESSAGE" | grep -qiE "^implement|^fix|^add|^实现|^修复|^添加"; then
52
+ echo "<system-reminder>USBV: Specify contracts → Build → Validate</system-reminder>"
53
+ fi
54
+ fi
55
+
56
+ # ============================================
57
+ # Progressive refresh based on message count
58
+ # ============================================
59
+
60
+ # Message 15: Lightweight checkpoint
61
+ if [[ $COUNT -eq 15 ]]; then
62
+ echo "<system-reminder>"
63
+ echo "Checkpoint: guard=verify, sig=contracts, USBV workflow."
64
+ echo "</system-reminder>"
65
+ fi
66
+
67
+ # Message 25+: Full INVAR.md injection every 10 messages
68
+ # SSOT: Inject entire protocol to ensure no content drift
69
+ if [[ $COUNT -ge 25 && $((COUNT % 10)) -eq 0 ]]; then
70
+ echo "<system-reminder>"
71
+ echo "=== Protocol Refresh (message $COUNT) ==="
72
+ echo ""
73
+ cat << 'INVAR_EOF'
74
+ <!--
75
+ ┌─────────────────────────────────────────────────────────────┐
76
+ │ INVAR-MANAGED FILE - DO NOT EDIT DIRECTLY │
77
+ │ │
78
+ │ This file is managed by Invar. Changes may be lost on │
79
+ │ `invar update`. Add project content to CLAUDE.md instead. │
80
+ └─────────────────────────────────────────────────────────────┘
81
+
82
+ License: CC-BY-4.0 (Creative Commons Attribution 4.0 International)
83
+ https://creativecommons.org/licenses/by/4.0/
84
+
85
+ You are free to share and adapt this document, provided you give
86
+ appropriate credit to the Invar project.
87
+ -->
88
+ # The Invar Protocol v5.0
89
+
90
+ > **"Trade structure for safety."**
91
+
92
+ ## Six Laws
93
+
94
+ | Law | Principle |
95
+ |-----|-----------|
96
+ | 1. Separation | Core (pure logic) / Shell (I/O) physically separate |
97
+ | 2. Contract Complete | @pre/@post + doctests uniquely determine implementation |
98
+ | 3. Context Economy | map → sig → code (only read what's needed) |
99
+ | 4. Decompose First | Break into sub-functions before implementing |
100
+ | 5. Verify Reflectively | Fail → Reflect (why?) → Fix → Verify |
101
+ | 6. Integrate Fully | Local correct ≠ Global correct; verify all paths |
102
+
103
+ ## Core/Shell Architecture
104
+
105
+ | Zone | Location | Requirements |
106
+ |------|----------|--------------|
107
+ | Core | `**/core/**` | @pre/@post, pure (no I/O), doctests |
108
+ | Shell | `**/shell/**` | `Result[T, E]` return type |
109
+
110
+ **Forbidden in Core:** `os`, `sys`, `subprocess`, `pathlib`, `open`, `requests`, `datetime.now`
111
+
112
+ ### Decision Tree: Core vs Shell
113
+
114
+ ```
115
+ Does this function...
116
+
117
+ ├─ Read or write files? ──────────────────→ Shell
118
+ ├─ Make network requests? ─────────────────→ Shell
119
+ ├─ Access current time (datetime.now)? ────→ Shell OR inject as parameter
120
+ ├─ Generate random values? ────────────────→ Shell OR inject as parameter
121
+ ├─ Print to console? ──────────────────────→ Shell (return data, Shell logs)
122
+ ├─ Access environment variables? ──────────→ Shell
123
+
124
+ └─ None of the above? ─────────────────────→ Core
125
+ ```
126
+
127
+ **Pattern:** Inject impure values as parameters:
128
+ ```python
129
+ # Core: receives 'now' as parameter (pure)
130
+ def is_expired(expiry: datetime, now: datetime) -> bool:
131
+ return now > expiry
132
+
133
+ # Shell calls with actual time
134
+ expired = is_expired(token.expiry, datetime.now())
135
+ ```
136
+
137
+ ## Core Example (Pure Logic)
138
+
139
+ ```python
140
+ from deal import pre, post
141
+
142
+ @pre(lambda price, discount: price > 0 and 0 <= discount <= 1)
143
+ @post(lambda result: result >= 0)
144
+ def discounted_price(price: float, discount: float) -> float:
145
+ """
146
+ >>> discounted_price(100, 0.2)
147
+ 80.0
148
+ >>> discounted_price(100, 0) # Edge: no discount
149
+ 100.0
150
+ """
151
+ return price * (1 - discount)
152
+ ```
153
+
154
+ **Self-test:** Can someone else write the exact same function from just @pre/@post + doctests?
155
+
156
+ ## Shell Example (I/O Operations)
157
+
158
+ ```python
159
+ from pathlib import Path
160
+ from returns.result import Result, Success, Failure
161
+
162
+ def read_config(path: Path) -> Result[dict, str]:
163
+ """Shell: handles I/O, returns Result for error handling."""
164
+ try:
165
+ import json
166
+ return Success(json.loads(path.read_text()))
167
+ except FileNotFoundError:
168
+ return Failure(f"File not found: {path}")
169
+ except json.JSONDecodeError as e:
170
+ return Failure(f"Invalid JSON: {e}")
171
+ ```
172
+
173
+ **Pattern:** Shell reads file → passes content to Core → returns Result.
174
+
175
+ More examples: `.invar/examples/`
176
+
177
+ ## Contract Rules
178
+
179
+ ### Lambda Signature (Critical)
180
+
181
+ ```python
182
+ # WRONG: Lambda only takes first parameter
183
+ @pre(lambda x: x >= 0)
184
+ def calculate(x: int, y: int = 0): ...
185
+
186
+ # CORRECT: Lambda must include ALL parameters (even defaults)
187
+ @pre(lambda x, y=0: x >= 0)
188
+ def calculate(x: int, y: int = 0): ...
189
+ ```
190
+
191
+ Guard's `param_mismatch` rule catches this as ERROR.
192
+
193
+ ### Meaningful Contracts
194
+
195
+ ```python
196
+ # Redundant - type hints already check this
197
+ @pre(lambda x: isinstance(x, int))
198
+ def calc(x: int): ...
199
+
200
+ # Meaningful - checks business logic
201
+ @pre(lambda x: x > 0)
202
+ def calc(x: int): ...
203
+
204
+ # Meaningful - checks relationship between params
205
+ @pre(lambda start, end: start < end)
206
+ def process_range(start: int, end: int): ...
207
+ ```
208
+
209
+ ### @post Scope
210
+
211
+ ```python
212
+ # WRONG: @post cannot access function parameters
213
+ @post(lambda result: result > x) # 'x' not available!
214
+ def calc(x: int) -> int: ...
215
+
216
+ # CORRECT: @post can only use 'result'
217
+ @post(lambda result: result >= 0)
218
+ def calc(x: int) -> int: ...
219
+ ```
220
+
221
+ ## Check-In (Required)
222
+
223
+ Your first message MUST display:
224
+
225
+ ```
226
+ ✓ Check-In: [project] | [branch] | [clean/dirty]
227
+ ```
228
+
229
+ Actions:
230
+ 1. Read `.invar/context.md` (Key Rules + Current State + Lessons Learned)
231
+ 2. Show one-line status
232
+
233
+ **Do NOT execute guard or map at Check-In.**
234
+ Guard is for VALIDATE phase and Final only.
235
+
236
+ This is your sign-in. The user sees it immediately.
237
+ No visible check-in = Session not started.
238
+
239
+ ## USBV Workflow (DX-32)
240
+
241
+ **U**nderstand → **S**pecify → **B**uild → **V**alidate
242
+
243
+ | Phase | Purpose | Activities |
244
+ |-------|---------|------------|
245
+ | UNDERSTAND | Know what and why | Intent, Inspect (invar sig/map), Constraints |
246
+ | SPECIFY | Define boundaries | @pre/@post, Design decomposition, Doctests |
247
+ | BUILD | Write code | Implement leaves, Compose |
248
+ | VALIDATE | Confirm correctness | invar guard, Review Gate, Reflect |
249
+
250
+ **Key:** Inspect before Contract. Depth varies naturally. Iterate when needed.
251
+
252
+ **Review Gate:** When Guard triggers `review_suggested` (escape hatches ≥3, security paths, low coverage), invoke `/review` before completion.
253
+
254
+ ## Visible Workflow (DX-30)
255
+
256
+ For complex tasks (3+ functions), show 3 checkpoints in TodoList:
257
+
258
+ ```
259
+ □ [UNDERSTAND] Task description, codebase context, constraints
260
+ □ [SPECIFY] Contracts (@pre/@post) and design decomposition
261
+ □ [VALIDATE] Guard results, Review Gate if triggered, integration status
262
+ ```
263
+
264
+ **BUILD is internal work** — not shown in TodoList.
265
+
266
+ **Show contracts before code.** Example:
267
+
268
+ ```python
269
+ [SPECIFY] calculate_discount:
270
+ @pre(lambda price, rate: price > 0 and 0 <= rate <= 1)
271
+ @post(lambda result: result >= 0)
272
+ def calculate_discount(price: float, rate: float) -> float: ...
273
+
274
+ [BUILD] Now coding...
275
+ ```
276
+
277
+ **When to use:** New features (3+ functions), architectural changes, Core modifications.
278
+ **Skip for:** Single-line fixes, documentation, trivial refactoring.
279
+
280
+ ## Task Completion
281
+
282
+ A task is complete only when ALL conditions are met:
283
+ - Check-In displayed: `✓ Check-In: [project] | [branch] | [clean/dirty]`
284
+ - Intent explicitly stated
285
+ - Contract written before implementation
286
+ - Final displayed: `✓ Final: guard PASS | <errors>, <warnings>`
287
+ - User requirement satisfied
288
+
289
+ **Missing any = Task incomplete.**
290
+
291
+ ## Markers
292
+
293
+ ### Entry Points
294
+
295
+ Entry points are framework callbacks (`@app.route`, `@app.command`) at Shell boundary.
296
+ - **Exempt** from `Result[T, E]` — must match framework signature
297
+ - **Keep thin** (max 15 lines) — delegate to Shell functions that return Result
298
+
299
+ Auto-detected by decorators. For custom callbacks:
300
+
301
+ ```python
302
+ # @shell:entry
303
+ def on_custom_event(data: dict) -> dict:
304
+ result = handle_event(data)
305
+ return result.unwrap_or({"error": "failed"})
306
+ ```
307
+
308
+ ### Shell Complexity
309
+
310
+ When shell function complexity is justified:
311
+
312
+ ```python
313
+ # @shell_complexity: Subprocess with error classification
314
+ def run_external_tool(...): ...
315
+
316
+ # @shell_orchestration: Multi-step pipeline coordination
317
+ def process_batch(...): ...
318
+ ```
319
+
320
+ ### Architecture Escape Hatch
321
+
322
+ When rule violation has valid architectural justification:
323
+
324
+ ```python
325
+ # @invar:allow shell_result: Framework callback signature fixed
326
+ def flask_handler(): ...
327
+ ```
328
+
329
+ **Valid rule names for @invar:allow:**
330
+ - `shell_result` — Shell function without Result return type
331
+ - `entry_point_too_thick` — Entry point exceeds 15 lines
332
+ - `forbidden_import` — I/O import in Core (rare, justify carefully)
333
+
334
+ Run `invar rules` for complete rule catalog with hints.
335
+
336
+ ## Commands
337
+
338
+ ```bash
339
+ invar guard # Full: static + doctests + CrossHair + Hypothesis
340
+ invar guard --static # Static only (quick debug, ~0.5s)
341
+ invar guard --changed # Modified files only
342
+ invar guard --coverage # Collect branch coverage
343
+ invar sig <file> # Show contracts + signatures
344
+ invar map --top 10 # Most-referenced symbols
345
+ invar rules # List all rules with detection/hints (JSON)
346
+ ```
347
+
348
+ ## Configuration
349
+
350
+ ```toml
351
+ # pyproject.toml or invar.toml
352
+ [tool.invar.guard]
353
+ core_paths = ["src/myapp/core"] # Default: ["src/core", "core"]
354
+ shell_paths = ["src/myapp/shell"] # Default: ["src/shell", "shell"]
355
+ max_file_lines = 500 # Default: 500 (warning at 80%)
356
+ max_function_lines = 50 # Default: 50
357
+ # Doctest lines are excluded from size calculations
358
+ ```
359
+
360
+ ## Troubleshooting
361
+
362
+ ### Size Limits (Agent Quick Reference)
363
+
364
+ | Rule | Limit | Fix |
365
+ |------|-------|-----|
366
+ | `function_too_long` | **50 lines** | Extract helper: `_impl()` + main with docstring |
367
+ | `file_too_long` | **500 lines** | Split by responsibility |
368
+ | `entry_point_too_thick` | **15 lines** | Delegate to Shell functions |
369
+
370
+ *Doctest lines excluded from counts. Limits configurable in `pyproject.toml`.*
371
+
372
+ ### Common Errors
373
+
374
+ | Symptom | Cause | Fix |
375
+ |---------|-------|-----|
376
+ | `param_mismatch` error | Lambda missing params | Include ALL params (even defaults) |
377
+ | `shell_result` error | Shell func no Result | Add Result[T,E] or @invar:allow |
378
+ | `is_failure()` not found | Wrong Result check | Use `isinstance(result, Failure)` |
379
+
380
+ ---
381
+
382
+ *Protocol v5.0 — USBV workflow (DX-32) | [Examples](.invar/examples/)*
383
+
384
+ INVAR_EOF
385
+ echo "</system-reminder>"
386
+ fi
@@ -67,6 +67,42 @@ def calculate(x: int) -> int:
67
67
  ... # Implementation comes in BUILD
68
68
  ```
69
69
 
70
+ #### Function-Level Gates (DX-63)
71
+
72
+ When creating new modules, use **incremental development**:
73
+
74
+ 1. Create ONE file
75
+ 2. Write contracts for all functions (body = `...`)
76
+ 3. Run `invar_guard(contracts_only=true)` to verify coverage
77
+ 4. Implement functions
78
+ 5. Run `invar_guard(changed=true)`
79
+ 6. Proceed to next file
80
+
81
+ ❌ Do NOT create multiple file skeletons at once
82
+ ❌ Do NOT "structure first, fill later"
83
+
84
+ **TodoList Pattern: Interleaved SPECIFY/BUILD**
85
+
86
+ For each function:
87
+ ```
88
+ □ [SPECIFY] Write contract for validate_input
89
+ □ [BUILD] Implement validate_input
90
+ □ [SPECIFY] Write contract for process_data
91
+ □ [BUILD] Implement process_data
92
+ ```
93
+
94
+ NOT:
95
+ ```
96
+ □ [SPECIFY] Write all contracts
97
+ □ [BUILD] Implement all functions
98
+ ```
99
+
100
+ **Violation Self-Check** — Before writing ANY implementation code:
101
+ 1. "Have I written the contract for THIS function?"
102
+ 2. "Have I shown it in my response?"
103
+ 3. "Have I run `invar_guard(contracts_only=true)`?"
104
+ If any NO → Stop. Write contract first.
105
+
70
106
  ### 3. BUILD
71
107
 
72
108
  **For complex tasks:** Enter Plan Mode first, get user approval.
@@ -273,8 +309,7 @@ Agent:
273
309
 
274
310
  ✓ Final: guard PASS | 0 errors, 1 warning
275
311
  ```
276
- <!--/invar:skill-->
277
- <!--invar:extensions-->
312
+ <!--/invar:skill--><!--invar:extensions-->
278
313
  <!-- ========================================================================
279
314
  EXTENSIONS REGION - USER EDITABLE
280
315
  Add project-specific extensions here. This section is preserved on update.
@@ -75,8 +75,7 @@ Before any workflow action:
75
75
 
76
76
  **Next step?**
77
77
  ```
78
- <!--/invar:skill-->
79
- <!--invar:extensions-->
78
+ <!--/invar:skill--><!--invar:extensions-->
80
79
  <!-- ========================================================================
81
80
  EXTENSIONS REGION - USER EDITABLE
82
81
  Add project-specific extensions here. This section is preserved on update.
@@ -86,8 +86,7 @@ Create `docs/proposals/DX-XX-[topic].md`:
86
86
  | Chooses option | /develop to implement |
87
87
  | Needs more info | /investigate for analysis |
88
88
  | Approves proposal | Document created |
89
- <!--/invar:skill-->
90
- <!--invar:extensions-->
89
+ <!--/invar:skill--><!--invar:extensions-->
91
90
  <!-- ========================================================================
92
91
  EXTENSIONS REGION - USER EDITABLE
93
92
  Add project-specific extensions here. This section is preserved on update.