causal-continuity-engine 0.2.0__tar.gz → 0.3.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 (261) hide show
  1. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/CONTRIBUTING.md +5 -1
  2. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_attribution.py +12 -0
  3. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/AGENTS.md +12 -5
  4. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/CHANGELOG.md +34 -0
  5. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/CITATION.cff +2 -2
  6. {causal_continuity_engine-0.2.0/causal_continuity_engine.egg-info → causal_continuity_engine-0.3.0}/PKG-INFO +25 -7
  7. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/README.md +24 -6
  8. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/__init__.py +1 -1
  9. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/engine.py +121 -6
  10. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/extraction.py +100 -1
  11. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/invalidation.py +28 -5
  12. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0/causal_continuity_engine.egg-info}/PKG-INFO +25 -7
  13. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine.egg-info/SOURCES.txt +4 -0
  14. causal_continuity_engine-0.3.0/docs/MCP.md +270 -0
  15. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/RELEASE.md +3 -3
  16. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/REQUIREMENTS_COVERAGE.md +6 -6
  17. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/adr/ADR-INDEX.md +134 -0
  18. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_attribution_controls.py +39 -0
  19. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_extraction.py +3 -3
  20. causal_continuity_engine-0.3.0/tests/test_contradiction_notice.py +346 -0
  21. causal_continuity_engine-0.3.0/tests/test_extraction_negated_cue.py +93 -0
  22. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_isolated_review.py +6 -0
  23. causal_continuity_engine-0.3.0/tests/test_proof_rejection_reason.py +45 -0
  24. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_release_controls.py +25 -4
  25. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.editorconfig +0 -0
  26. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.gitattributes +0 -0
  27. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.githooks/commit-msg +0 -0
  28. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.githooks/pre-commit +0 -0
  29. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/CODEOWNERS +0 -0
  30. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  31. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  32. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  33. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/SECURITY.md +0 -0
  34. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/SUPPORT.md +0 -0
  35. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/dependabot.yml +0 -0
  36. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/pull_request_template.md +0 -0
  37. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/release.yml +0 -0
  38. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/ruleset.README.md +0 -0
  39. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/ruleset.json +0 -0
  40. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/bootstrap_tools.py +0 -0
  41. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/build_distributions.py +0 -0
  42. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_clean.py +0 -0
  43. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_content_marks.py +0 -0
  44. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_release_metadata.py +0 -0
  45. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_release_tag.py +0 -0
  46. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/check_stdlib.py +0 -0
  47. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/prepare_release_tag.py +0 -0
  48. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/render_api_docs.py +0 -0
  49. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/run_gates.py +0 -0
  50. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/run_isolated_review.py +0 -0
  51. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/verify_distributions.py +0 -0
  52. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/scripts/verify_public_schemas.py +0 -0
  53. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/tag-ruleset.json +0 -0
  54. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/ci.yml +0 -0
  55. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/commit-signature-audit.yml +0 -0
  56. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/content-integrity-pr.yml +0 -0
  57. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/content-integrity.yml +0 -0
  58. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/dependency-review.yml +0 -0
  59. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/no-ai-attribution.yml +0 -0
  60. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/release.yml +0 -0
  61. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.github/workflows/secret-scan.yml +0 -0
  62. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.gitignore +0 -0
  63. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.gitleaks.toml +0 -0
  64. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/.pre-commit-config.yaml +0 -0
  65. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/LICENSE.txt +0 -0
  66. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/MANIFEST.in +0 -0
  67. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/NOTICE +0 -0
  68. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/SPEC.md +0 -0
  69. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/benchmarks/__init__.py +0 -0
  70. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/benchmarks/continuitybench/__init__.py +0 -0
  71. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/benchmarks/continuitybench/run.py +0 -0
  72. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/benchmarks/continuitybench/scenarios.py +0 -0
  73. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/api.py +0 -0
  74. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/capabilities.py +0 -0
  75. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/capsule.py +0 -0
  76. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/cli.py +0 -0
  77. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/core.py +0 -0
  78. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/evidence.py +0 -0
  79. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/github.py +0 -0
  80. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/graph.py +0 -0
  81. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/lamport.py +0 -0
  82. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/learning.py +0 -0
  83. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/mcp.py +0 -0
  84. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/memory.py +0 -0
  85. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/ontology.py +0 -0
  86. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/partial.py +0 -0
  87. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/policy.py +0 -0
  88. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/proof.py +0 -0
  89. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/py.typed +0 -0
  90. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/redaction.py +0 -0
  91. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/resume.py +0 -0
  92. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/store.py +0 -0
  93. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine/verifiers.py +0 -0
  94. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine.egg-info/dependency_links.txt +0 -0
  95. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine.egg-info/entry_points.txt +0 -0
  96. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine.egg-info/requires.txt +0 -0
  97. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/causal_continuity_engine.egg-info/top_level.txt +0 -0
  98. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/API.md +0 -0
  99. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/CAPABILITIES.md +0 -0
  100. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/CONTENT-INGRESS-FIREWALL.md +0 -0
  101. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/PUBLIC-FLIP.md +0 -0
  102. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/REQUIREMENTS.md +0 -0
  103. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/RESEARCH-ROADMAP.md +0 -0
  104. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/TESTING.md +0 -0
  105. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/V0.1.5-TRUST-CLOSURE.md +0 -0
  106. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/assets/demo.svg +0 -0
  107. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/docs/assets/social-preview.png +0 -0
  108. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/examples/backfill_github.py +0 -0
  109. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/examples/quickstart.py +0 -0
  110. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/justfile +0 -0
  111. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/pyproject.toml +0 -0
  112. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/requirements-dev.in +0 -0
  113. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/requirements-dev.lock +0 -0
  114. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.anchor.v1.json +0 -0
  115. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.capsule.v1.json +0 -0
  116. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.capsule.v2.json +0 -0
  117. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.continuity-receipt.v1.json +0 -0
  118. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.continuity-receipt.v2.json +0 -0
  119. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.event.v1.json +0 -0
  120. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.proof-predicate.v1.json +0 -0
  121. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.proof-predicate.v2.json +0 -0
  122. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.proof.v1.json +0 -0
  123. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.proof.v2.json +0 -0
  124. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.recovery.v1.json +0 -0
  125. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.resume.v1.json +0 -0
  126. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/schemas/cce.resume.v2.json +0 -0
  127. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/setup.cfg +0 -0
  128. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/__init__.py +0 -0
  129. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/authority_helpers.py +0 -0
  130. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/schema_validation.py +0 -0
  131. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_api_contract.py +0 -0
  132. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_atomic_append.py +0 -0
  133. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_cli.py +0 -0
  134. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_consumers.py +0 -0
  135. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_criticality.py +0 -0
  136. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_producer.py +0 -0
  137. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_read_reuse.py +0 -0
  138. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_redteam.py +0 -0
  139. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_authority_withdrawal_classification.py +0 -0
  140. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_benchmark_authority.py +0 -0
  141. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_benchmark_cleanup.py +0 -0
  142. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_benchmark_runner.py +0 -0
  143. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_capsule_packet_clock.py +0 -0
  144. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_cli.py +0 -0
  145. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_conformance.py +0 -0
  146. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_content_marks.py +0 -0
  147. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_distribution_environment.py +0 -0
  148. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_engine_e2e.py +0 -0
  149. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_github_redaction.py +0 -0
  150. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_identity_compatibility.py +0 -0
  151. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_installed_contracts.py +0 -0
  152. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_installed_packet_bounds.py +0 -0
  153. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_instrument_validation.py +0 -0
  154. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_invalidation_resume.py +0 -0
  155. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_learning_partial_api.py +0 -0
  156. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_mcp_server.py +0 -0
  157. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_memory_extraction.py +0 -0
  158. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_obligation_boundaries.py +0 -0
  159. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_obligation_completeness.py +0 -0
  160. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_obligation_contract_repairs.py +0 -0
  161. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_byte_bounds.py +0 -0
  162. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_byte_transports.py +0 -0
  163. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_complete_trust.py +0 -0
  164. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_failure_boundaries.py +0 -0
  165. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_scope_receipts.py +0 -0
  166. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_scope_wire.py +0 -0
  167. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_packet_validity_frontier.py +0 -0
  168. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_processor_compatibility.py +0 -0
  169. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_proof_obligation_wire.py +0 -0
  170. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_readme_contract.py +0 -0
  171. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_receipt_spec_contract.py +0 -0
  172. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions.py +0 -0
  173. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round10_release.py +0 -0
  174. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round11_deep_integrity.py +0 -0
  175. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round12_release_blockers.py +0 -0
  176. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round13_jcs.py +0 -0
  177. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round14_state_integrity.py +0 -0
  178. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round15_capsule_invalidation_github.py +0 -0
  179. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round15_public_boundaries.py +0 -0
  180. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round15_storage_boundaries.py +0 -0
  181. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round16_cli_api_core_capabilities.py +0 -0
  182. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round17_backfill_layout.py +0 -0
  183. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round18_assumption_edits.py +0 -0
  184. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round18_co_assertion.py +0 -0
  185. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round18_journal.py +0 -0
  186. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round18_negation.py +0 -0
  187. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round18_source_order.py +0 -0
  188. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round2.py +0 -0
  189. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round3.py +0 -0
  190. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round4.py +0 -0
  191. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round5.py +0 -0
  192. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round6.py +0 -0
  193. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round7.py +0 -0
  194. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round8_boundaries.py +0 -0
  195. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round8_engine.py +0 -0
  196. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round8_portability.py +0 -0
  197. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round8_state.py +0 -0
  198. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round9_integrity.py +0 -0
  199. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_regressions_round9_scope_freshness.py +0 -0
  200. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_release_boundary_validation.py +0 -0
  201. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_resume_prose_origin.py +0 -0
  202. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_schema_release_identities.py +0 -0
  203. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_sqlite_lifecycle.py +0 -0
  204. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_store_graph.py +0 -0
  205. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_task_packet_review.py +0 -0
  206. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_task_packets.py +0 -0
  207. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/tests/test_trust.py +0 -0
  208. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/action_intent_wrong_type.json +0 -0
  209. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/actor_wrong_type.json +0 -0
  210. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_invalid_calendar_date.json +0 -0
  211. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_leap_second.json +0 -0
  212. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_lowercase_designators.json +0 -0
  213. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_missing_timezone.json +0 -0
  214. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_noncanonical_offset.json +0 -0
  215. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/created_at_space_separator.json +0 -0
  216. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/empty_required_authoritative_failure.json +0 -0
  217. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/empty_required_authoritative_pass.json +0 -0
  218. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/empty_required_no_results.json +0 -0
  219. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/empty_required_self_asserted_pass.json +0 -0
  220. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/generate.py +0 -0
  221. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/historical_proof_version.json +0 -0
  222. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/incomplete_failed_check.json +0 -0
  223. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/index.json +0 -0
  224. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/input_item_wrong_type.json +0 -0
  225. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/invalid_result_value.json +0 -0
  226. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/lamport_fingerprint_spoof.json +0 -0
  227. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/lamport_no_registry.json +0 -0
  228. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/lamport_truncated_key.json +0 -0
  229. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/missing_required_field.json +0 -0
  230. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/non_binary64_integer.json +0 -0
  231. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/obligation_duplicate.json +0 -0
  232. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/obligation_duplicate_task.json +0 -0
  233. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/obligation_missing.json +0 -0
  234. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/obligation_wrong_kind.json +0 -0
  235. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/obligation_wrong_task.json +0 -0
  236. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/policy_decision_wrong_type.json +0 -0
  237. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/retry_cannot_launder_a_failure.json +0 -0
  238. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/self_asserted_cannot_satisfy.json +0 -0
  239. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/self_assertion_cannot_poison_authoritative_pass.json +0 -0
  240. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/status_contradicts_contents.json +0 -0
  241. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/summary_contradicts_contents.json +0 -0
  242. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/tamper_statement.json +0 -0
  243. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/tamper_statement_resealed.json +0 -0
  244. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/task_id_under_unrelated_field.json +0 -0
  245. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unbound_task.json +0 -0
  246. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_action_intent_field.json +0 -0
  247. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_algorithm.json +0 -0
  248. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_evidence_context_field.json +0 -0
  249. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_execution_field.json +0 -0
  250. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_schema_version.json +0 -0
  251. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_top_level_field.json +0 -0
  252. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unknown_verification_field.json +0 -0
  253. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unverified_forged_body_no_key.json +0 -0
  254. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/unverified_hmac_no_key.json +0 -0
  255. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/valid_hmac.json +0 -0
  256. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/valid_hmac_jcs_edges.json +0 -0
  257. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/valid_hmac_unicode.json +0 -0
  258. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/valid_lamport.json +0 -0
  259. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/vectors/wrong_project.json +0 -0
  260. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/verifiers/__init__.py +0 -0
  261. {causal_continuity_engine-0.2.0 → causal_continuity_engine-0.3.0}/verifiers/verify_proof.py +0 -0
@@ -258,7 +258,11 @@ outcome, say what distinguishes it from "nothing happened".
258
258
 
259
259
  **No authorship attribution to tooling.** The `.githooks/` hooks and the CI
260
260
  attribution guard both reject it, in commit messages and in committed files.
261
- Do not work around them.
261
+ Do not work around them. This is a rule about credit, not secrecy: AI coding
262
+ assistants are used in this project's development, and every change is
263
+ reviewed, tested, signed off, and committed by the maintainer, who is
264
+ responsible for it. If AI tools did significant work on your patch, say so in
265
+ plain words in the pull request; never list a tool as an author or co-author.
262
266
 
263
267
  ## Security
264
268
 
@@ -76,6 +76,13 @@ _ARTIFACT_AI_CREDIT = re.compile(
76
76
  _TOOL_BYLINE = re.compile(
77
77
  rf"{_AI_ALIAS.pattern}\s*\((?:anthropic|openai)\)[.!]?"
78
78
  )
79
+ # An AI coding session appends provenance rather than credit: a trailer whose key
80
+ # names the tool ("<tool>-Session: <link>") and a bare link to the session. Neither
81
+ # names an actor, so the credit grammar above never reached them.
82
+ _AI_SESSION_KEY = re.compile(
83
+ rf"{_AI_ALIAS.pattern}[- ]session(?:[- ](?:id|link|url))?"
84
+ )
85
+ _AI_SESSION_LINK = re.compile(r"<?https?://claude\.ai/code/session_[a-z0-9_-]+/?>?")
79
86
  _TRAILER_KEYS = frozenset({
80
87
  "assisted-by",
81
88
  "authored-by",
@@ -233,6 +240,11 @@ def scan_prose(
233
240
  key, separator, value = line.partition(":")
234
241
  if separator and key.replace("_", "-") in _TRAILER_KEYS:
235
242
  reason = _actor_reason(value)
243
+ if reason is None and separator and _AI_SESSION_KEY.fullmatch(
244
+ key.replace("_", "-")):
245
+ reason = "tool/model session trailer"
246
+ if reason is None and _AI_SESSION_LINK.fullmatch(line):
247
+ reason = "tool/model session link"
236
248
  if reason is None:
237
249
  match = _CREDIT.fullmatch(line) or _ROLE.fullmatch(line)
238
250
  if match is not None:
@@ -14,9 +14,13 @@ history and a bi-temporal causal graph. Event-derived projections are
14
14
  rebuildable only while source payloads remain inside the retention window;
15
15
  runtime records are authenticated/audited rather than replay-derived. Memory
16
16
  tiers L0–L4 and token-budgeted Resume Packets are what
17
- an agent actually receives on resume. When a requirement changes or evidence
18
- contradicts an assumption, causal invalidation computes the blast radius
19
- over typed edges, bounds it, and classifies it deterministically. When policy
17
+ an agent actually receives on resume. When a requirement changes, when the
18
+ source that stated an assumption withdraws it, or when an integration records
19
+ contradicting evidence through `engine.invalidation.fire(...)`, causal
20
+ invalidation computes the blast radius over typed edges, bounds it, and
21
+ classifies it deterministically. It does not read prose for meaning; a trusted
22
+ explicit negation of an active assumption's own wording raises a human-gated
23
+ notice (ADR-133). When policy
20
24
  requires proof, a task cannot be marked complete without a signed proof envelope
21
25
  whose required verifiers actually ran — `Engine.complete_task` has twenty-two
22
26
  independently instrumented rejection gates, including current confirmed authority,
@@ -325,8 +329,11 @@ below what it is about to do (ADR-048).
325
329
 
326
330
  Never add AI authorship credit to a commit message, PR title or body, code
327
331
  comment, documentation file, changelog, or example. No co-author trailers,
328
- no "generated with" lines, no tool bylines, no mention of AI involvement in
329
- this codebase anywhere.
332
+ no "generated with" lines, no tool bylines. This is a rule about credit, not
333
+ secrecy: `.github/CONTRIBUTING.md` states plainly that AI coding assistants
334
+ are used in development and that the maintainer reviews, tests, and commits
335
+ every change. Disclosure in plain words is allowed; crediting a tool as an
336
+ author is not.
330
337
 
331
338
  This is enforced, not requested. `.githooks/commit-msg` and
332
339
  `.githooks/pre-commit` block it locally (`core.hooksPath` is set to
@@ -9,6 +9,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  No unreleased changes.
11
11
 
12
+ ## 0.3.0 — 2026-10-16
13
+
14
+ ### Added
15
+
16
+ - **A trusted explicit negation of an active assumption raises a human-gated
17
+ notice.** An OWNER, MEMBER or COLLABORATOR sentence that restates an active
18
+ assumption with one negator ("the feed is NOT ordered by timestamp"), negates
19
+ its cue ("we no longer assume …") or calls it wrong fires a
20
+ `contradictory_evidence` invalidation in `pending_confirmation`: nothing
21
+ changes until the owner records `confirm_invalidation` or
22
+ `reject_invalidation` through `authority --request`, with operands from
23
+ `Engine.authority_invalidation`. The notice appears in the resume packet and
24
+ makes `check` report `action_required`. The rule is closed and lexical; other
25
+ wordings are outside it (ADR-133).
26
+
27
+ ### Changed
28
+
29
+ - **Breaking: processor compatibility advances to `cce-processor/1.10.0` and
30
+ extraction to `cce-deterministic/1.5.0`.** Preserve older projected stores and
31
+ their SQLite sidecars after stopping writers. Re-ingest retained sources into
32
+ a separate new store/project and review its proposals before confirming them.
33
+ Older projections do not upgrade in place; erased source payloads cannot be
34
+ reconstructed by replay (ADR-114, ADR-132).
35
+
36
+ ### Fixed
37
+
38
+ - **A negated assumption cue no longer proposes the assumption it denies.**
39
+ "We no longer assume X", "We do not assume X" and "nothing relies on X"
40
+ extracted the same positive proposal as "We assume X", so a sentence written
41
+ to retire an assumption offered it for confirmation. The extractor now
42
+ abstains when a negator stands immediately before the cue. A prohibition or
43
+ decision form still proposes the constraint or decision the author wrote;
44
+ other wordings of a denial are outside this closed rule (ADR-132).
45
+
12
46
  ## 0.2.0 — 2026-09-23
13
47
 
14
48
  ### Changed
@@ -8,8 +8,8 @@ authors:
8
8
  - given-names: Thomas
9
9
  family-names: Albrecht
10
10
  alias: The Quantum Falcon
11
- version: 0.2.0
12
- date-released: 2026-09-23
11
+ version: 0.3.0
12
+ date-released: 2026-10-16
13
13
  license: Apache-2.0
14
14
  repository-code: https://github.com/thequantumfalcon/causal-continuity-engine
15
15
  url: https://github.com/thequantumfalcon/causal-continuity-engine
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: causal-continuity-engine
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Causal Continuity Engine - continuity, causal invalidation, and proof for long-running coding agents
5
5
  Author: Thomas Albrecht
6
6
  License-Expression: Apache-2.0
@@ -71,9 +71,16 @@ and neither is an unexamined green check.
71
71
  CCE addresses each with a mechanism rather than a better prompt. Decisions,
72
72
  assumptions, requirements and evidence become nodes in a bi-temporal causal
73
73
  graph over an application-enforced append-only, hash-chained event log, so
74
- *why* is a queryable edge and not a memory. When a requirement changes or evidence contradicts an assumption, the
74
+ *why* is a queryable edge and not a memory. When a requirement changes, when the
75
+ source that stated an assumption withdraws it, or when an integration records
76
+ contradicting evidence through `engine.invalidation.fire(...)`, the
75
77
  blast radius is computed over typed edges, bounded, and classified
76
- deterministically. When policy requires proof, a task cannot be marked complete
78
+ deterministically. CCE does not read prose for meaning. It does recognize one
79
+ closed form: a trusted comment that restates an active assumption with a
80
+ negator, like the one above, raises a human-gated notice that changes nothing
81
+ until the owner confirms or rejects it; a differently worded dispute proposes
82
+ nothing and fires nothing.
83
+ When policy requires proof, a task cannot be marked complete
77
84
  without a signed envelope whose required verifiers actually ran — the rejection gates are
78
85
  mechanically enumerated and mutation-tested below.
79
86
 
@@ -97,7 +104,9 @@ command are already used by an unrelated published project.
97
104
  ## Use it from an editor
98
105
 
99
106
  `cce-engine mcp` speaks the Model Context Protocol over stdio, so an MCP client
100
- can read a project's control state directly. Four read-only tools:
107
+ can read a project's control state directly. [docs/MCP.md](docs/MCP.md) walks the
108
+ whole path on this repository, from install to a connected agent, and shows how
109
+ the continuity check runs in CI on a runner that holds the store. Four read-only tools:
101
110
  `resume_packet`, `list_assumptions`, `list_invalidations`, `continuity_check`.
102
111
 
103
112
  The subcommand first ships in 0.1.6. The published 0.1.0, 0.1.2 and 0.1.3
@@ -165,15 +174,15 @@ with `.\.venv\Scripts\python.exe -m pip install causal-continuity-engine`
165
174
  and invoke the environment's `cce-engine.exe` by its absolute path in the
166
175
  commands below. After activation, `cce-engine --help` checks the installation.
167
176
 
168
- **Upgrading an existing project:** this release uses processor 1.9.0 and
169
- extractor 1.4.0; projections written by 0.1.6's processor 1.8.0 or earlier
177
+ **Upgrading an existing project:** this release uses processor 1.10.0 and
178
+ extractor 1.5.0; projections written by 0.2.0's processor 1.9.0 or earlier
170
179
  processors are refused. There is no in-place projection or authority upgrade. Stop writers,
171
180
  preserve the complete old project state (including its database and any SQLite
172
181
  sidecars), and re-ingest retained sources into a separate new project/store.
173
182
  Review and explicitly confirm the new proposals there; old prose is not approval.
174
183
  Do not remove markers or sidecars to bypass refusal. Payloads already removed
175
184
  by retention cannot be reconstructed from their digests. New users can simply
176
- start a fresh project. See [the upgrade notes for this release](CHANGELOG.md#020--2026-09-23);
185
+ start a fresh project. See [the upgrade notes for this release](CHANGELOG.md#030--2026-10-16);
177
186
  the [0.1.6 notes](CHANGELOG.md#016--2026-09-20) remain historical.
178
187
 
179
188
  To run this exact checkout instead of the published package, or to work on the
@@ -191,6 +200,12 @@ python -m pip install -e .
191
200
  On Windows, use the PowerShell environment-creation and activation commands
192
201
  above, then `python -m pip install -e .` from the checkout.
193
202
 
203
+ The editable install is enough for the engine but not for the test suite,
204
+ which fails at collection without the hash-locked development tools. Install
205
+ them with `python -m pip install -r requirements-dev.lock` (or `just setup`),
206
+ then run `python -m pytest tests -q`. The full suite can take around fifteen
207
+ minutes.
208
+
194
209
  Global flags come **before** the subcommand: `cce-engine --dir <path> <subcommand>`.
195
210
 
196
211
  ```bash
@@ -899,6 +914,9 @@ condition.
899
914
  - [docs/PUBLIC-FLIP.md](docs/PUBLIC-FLIP.md) — the register of every control
900
915
  that could not be enabled while this repository was private, with the exact
901
916
  command that turned each one on at the flip.
917
+ - [docs/MCP.md](docs/MCP.md) — connecting an agent over MCP, walked end to end on
918
+ this repository: install, init, ingest real events, confirm, verify, packet, check,
919
+ client configuration, and the freshness rule.
902
920
  - [docs/RELEASE.md](docs/RELEASE.md) — the reproducible, signed, immutable-ready
903
921
  release procedure and its continuity-bound attestation roadmap.
904
922
  - [docs/RESEARCH-ROADMAP.md](docs/RESEARCH-ROADMAP.md) — standards-backed
@@ -36,9 +36,16 @@ and neither is an unexamined green check.
36
36
  CCE addresses each with a mechanism rather than a better prompt. Decisions,
37
37
  assumptions, requirements and evidence become nodes in a bi-temporal causal
38
38
  graph over an application-enforced append-only, hash-chained event log, so
39
- *why* is a queryable edge and not a memory. When a requirement changes or evidence contradicts an assumption, the
39
+ *why* is a queryable edge and not a memory. When a requirement changes, when the
40
+ source that stated an assumption withdraws it, or when an integration records
41
+ contradicting evidence through `engine.invalidation.fire(...)`, the
40
42
  blast radius is computed over typed edges, bounded, and classified
41
- deterministically. When policy requires proof, a task cannot be marked complete
43
+ deterministically. CCE does not read prose for meaning. It does recognize one
44
+ closed form: a trusted comment that restates an active assumption with a
45
+ negator, like the one above, raises a human-gated notice that changes nothing
46
+ until the owner confirms or rejects it; a differently worded dispute proposes
47
+ nothing and fires nothing.
48
+ When policy requires proof, a task cannot be marked complete
42
49
  without a signed envelope whose required verifiers actually ran — the rejection gates are
43
50
  mechanically enumerated and mutation-tested below.
44
51
 
@@ -62,7 +69,9 @@ command are already used by an unrelated published project.
62
69
  ## Use it from an editor
63
70
 
64
71
  `cce-engine mcp` speaks the Model Context Protocol over stdio, so an MCP client
65
- can read a project's control state directly. Four read-only tools:
72
+ can read a project's control state directly. [docs/MCP.md](docs/MCP.md) walks the
73
+ whole path on this repository, from install to a connected agent, and shows how
74
+ the continuity check runs in CI on a runner that holds the store. Four read-only tools:
66
75
  `resume_packet`, `list_assumptions`, `list_invalidations`, `continuity_check`.
67
76
 
68
77
  The subcommand first ships in 0.1.6. The published 0.1.0, 0.1.2 and 0.1.3
@@ -130,15 +139,15 @@ with `.\.venv\Scripts\python.exe -m pip install causal-continuity-engine`
130
139
  and invoke the environment's `cce-engine.exe` by its absolute path in the
131
140
  commands below. After activation, `cce-engine --help` checks the installation.
132
141
 
133
- **Upgrading an existing project:** this release uses processor 1.9.0 and
134
- extractor 1.4.0; projections written by 0.1.6's processor 1.8.0 or earlier
142
+ **Upgrading an existing project:** this release uses processor 1.10.0 and
143
+ extractor 1.5.0; projections written by 0.2.0's processor 1.9.0 or earlier
135
144
  processors are refused. There is no in-place projection or authority upgrade. Stop writers,
136
145
  preserve the complete old project state (including its database and any SQLite
137
146
  sidecars), and re-ingest retained sources into a separate new project/store.
138
147
  Review and explicitly confirm the new proposals there; old prose is not approval.
139
148
  Do not remove markers or sidecars to bypass refusal. Payloads already removed
140
149
  by retention cannot be reconstructed from their digests. New users can simply
141
- start a fresh project. See [the upgrade notes for this release](CHANGELOG.md#020--2026-09-23);
150
+ start a fresh project. See [the upgrade notes for this release](CHANGELOG.md#030--2026-10-16);
142
151
  the [0.1.6 notes](CHANGELOG.md#016--2026-09-20) remain historical.
143
152
 
144
153
  To run this exact checkout instead of the published package, or to work on the
@@ -156,6 +165,12 @@ python -m pip install -e .
156
165
  On Windows, use the PowerShell environment-creation and activation commands
157
166
  above, then `python -m pip install -e .` from the checkout.
158
167
 
168
+ The editable install is enough for the engine but not for the test suite,
169
+ which fails at collection without the hash-locked development tools. Install
170
+ them with `python -m pip install -r requirements-dev.lock` (or `just setup`),
171
+ then run `python -m pytest tests -q`. The full suite can take around fifteen
172
+ minutes.
173
+
159
174
  Global flags come **before** the subcommand: `cce-engine --dir <path> <subcommand>`.
160
175
 
161
176
  ```bash
@@ -864,6 +879,9 @@ condition.
864
879
  - [docs/PUBLIC-FLIP.md](docs/PUBLIC-FLIP.md) — the register of every control
865
880
  that could not be enabled while this repository was private, with the exact
866
881
  command that turned each one on at the flip.
882
+ - [docs/MCP.md](docs/MCP.md) — connecting an agent over MCP, walked end to end on
883
+ this repository: install, init, ingest real events, confirm, verify, packet, check,
884
+ client configuration, and the freshness rule.
867
885
  - [docs/RELEASE.md](docs/RELEASE.md) — the reproducible, signed, immutable-ready
868
886
  release procedure and its continuity-bound attestation roadmap.
869
887
  - [docs/RESEARCH-ROADMAP.md](docs/RESEARCH-ROADMAP.md) — standards-backed
@@ -7,7 +7,7 @@ authenticated runtime records are not replay-derived (ADR-001). Storage is
7
7
  SQLite implementing the PostgreSQL-first relational adjacency design (ADR-011).
8
8
  """
9
9
 
10
- __version__ = "0.2.0"
10
+ __version__ = "0.3.0"
11
11
 
12
12
  SCHEMA_VERSIONS = {
13
13
  "anchor": "cce.anchor.v1",
@@ -65,7 +65,7 @@ from .evidence import (
65
65
  run_determinism_probe,
66
66
  run_mutation_probe,
67
67
  )
68
- from .extraction import DeterministicExtractor, normalize_statement
68
+ from .extraction import DeterministicExtractor, explicit_negations, normalize_statement
69
69
  from .github import (
70
70
  TRUSTED_ASSOCIATIONS,
71
71
  WEBHOOK_BODY_MAX_BYTES,
@@ -104,12 +104,14 @@ from .store import (
104
104
  )
105
105
  from .verifiers import VerifierRunner, VerifierSpec, record_verification
106
106
 
107
- PROCESSOR_VERSION = "cce-processor/1.9.0"
107
+ PROCESSOR_VERSION = "cce-processor/1.10.0"
108
108
  _AUTHORITY_SCHEMA = "cce.authority-decision.v1"
109
109
  _AUTHORITY_SOURCE = "cce:authority-decision:v1"
110
110
  _AUTHORITY_EXTRACTOR = "cce.authority-decision"
111
111
  _PROPOSAL_SCHEMA = "cce.proposal-id.v1"
112
112
  _AUTHORITY_KINDS = frozenset({"requirement", "constraint", "decision", "assumption", "task"})
113
+ _INVALIDATION_OPERATIONS = frozenset({"confirm_invalidation", "reject_invalidation"})
114
+ _NOTICE_SCHEMA = "cce.contradiction-notice.v1"
113
115
  # Version of the statement normalization contract that feeds stable_node_id.
114
116
  # It is deliberately separate from the extractor version: pattern behavior can
115
117
  # change without changing identity, while an identity change survives forever
@@ -141,8 +143,8 @@ _CONTINUITY_STATE_FIELDS = (
141
143
  _AUTHORIZED_ASSOCIATIONS = TRUSTED_ASSOCIATIONS
142
144
 
143
145
  _VOLATILE_PROJECTION_KEYS = {
144
- "captured_at", "checked_at", "completed_at", "composed_at", "created_at",
145
- "decided_at", "fired_at", "generated_at", "packet_generation_time",
146
+ "captured_at", "checked_at", "completed_at", "composed_at", "confirmed_at",
147
+ "created_at", "decided_at", "fired_at", "generated_at", "packet_generation_time",
146
148
  "ran_at", "started_at", "superseded_at",
147
149
  }
148
150
  _INTERNAL_RANDOM_ID = re.compile(
@@ -171,6 +173,15 @@ def _authority_request(request: dict) -> dict:
171
173
  required.add("authority_scope")
172
174
  optional = {"note"}
173
175
  binding = "confirmation"
176
+ elif isinstance(operation, str) and operation in _INVALIDATION_OPERATIONS:
177
+ # The human gate of CI-005, as a canonical decision: accepting or
178
+ # rejecting a pending invalidation binds the exact pending revision,
179
+ # so a notice re-fired or already decided under another request
180
+ # cannot be confirmed by a stale operand set (ADR-133).
181
+ required = common | {"invalidation_id", "expected_invalidation_version",
182
+ "expected_invalidation_digest"}
183
+ optional = {"note"}
184
+ binding = "invalidation"
174
185
  else:
175
186
  raise ValueError("unknown authority operation")
176
187
  if not required <= request.keys() or request.keys() - required - optional:
@@ -212,6 +223,8 @@ def _authority_request(request: dict) -> dict:
212
223
 
213
224
 
214
225
  def _confirmation_id(event: dict, request: dict) -> str:
226
+ if request["operation"] in _INVALIDATION_OPERATIONS:
227
+ return request["invalidation_id"]
215
228
  if request["operation"] != "confirm":
216
229
  return request["confirmation_id"]
217
230
  basis = [_AUTHORITY_SCHEMA, event["tenant_id"], event["project_id"], event["event_id"]]
@@ -2451,6 +2464,27 @@ class Engine:
2451
2464
  "expected_confirmation_digest": digest_obj(state),
2452
2465
  "authority_scope": state["authority_scope"]}
2453
2466
 
2467
+ def authority_invalidation(self, project_id: str, invalidation_id: str) -> dict:
2468
+ """Return exact request operands for a pending invalidation; this is not
2469
+ an authorization operation (ADR-133).
2470
+
2471
+ The digest covers the pending revision's semantic data with processor
2472
+ wall clocks removed, so the same operands bind on live ingest and on
2473
+ canonical replay.
2474
+ """
2475
+ self._require_project(project_id)
2476
+ try:
2477
+ inv = self.graph.get(invalidation_id, tenant_id=self.tenant_id,
2478
+ project_id=project_id, entity_type="invalidation")
2479
+ except KeyError:
2480
+ raise ValueError("unknown invalidation") from None
2481
+ if inv["status"] != "pending_confirmation":
2482
+ raise ValueError("invalidation is not pending confirmation")
2483
+ return {"invalidation_id": invalidation_id,
2484
+ "expected_invalidation_version": inv["version"],
2485
+ "expected_invalidation_digest": digest_obj(self._projection_value(
2486
+ inv["data"], {}, anonymize_unknown_ids=False))}
2487
+
2454
2488
  def authority_is_current(self, node: dict) -> bool:
2455
2489
  """Canonical authority witness, not human authentication or status resolution.
2456
2490
 
@@ -2484,6 +2518,11 @@ class Engine:
2484
2518
  def _validate_authority_operands(self, project_id: str, request: dict) -> dict | None:
2485
2519
  if request["tenant_id"] != self.tenant_id or request["project_id"] != project_id:
2486
2520
  raise PermissionError("authority request is outside the local project")
2521
+ if request["operation"] in _INVALIDATION_OPERATIONS:
2522
+ binding = self.authority_invalidation(project_id, request["invalidation_id"])
2523
+ if any(request[key] != value for key, value in binding.items()):
2524
+ raise ValueError("authority request does not bind the pending invalidation")
2525
+ return None
2487
2526
  if request["operation"] == "confirm":
2488
2527
  binding = self.authority_proposal(project_id, request["proposal_id"])
2489
2528
  if any(request[key] != value for key, value in binding.items()):
@@ -2710,6 +2749,15 @@ class Engine:
2710
2749
  def _process_authority_decision(self, event: dict, report: dict) -> None:
2711
2750
  request = self._authority_payload(event)
2712
2751
  project_id = event["project_id"]
2752
+ if request["operation"] in _INVALIDATION_OPERATIONS:
2753
+ self._validate_authority_operands(project_id, request)
2754
+ inv = self.invalidation.confirm(
2755
+ request["invalidation_id"], actor="owner-local",
2756
+ accept=request["operation"] == "confirm_invalidation",
2757
+ event_id=event["event_id"])
2758
+ report["invalidations"].append(inv["node_id"])
2759
+ report["created"].append({"node_id": inv["node_id"], "kind": "invalidation"})
2760
+ return
2713
2761
  state = self._validate_authority_operands(project_id, request)
2714
2762
  node_id = _confirmation_id(event, request)
2715
2763
  criticality = None
@@ -3182,6 +3230,8 @@ class Engine:
3182
3230
  if quarantined_block:
3183
3231
  self.store.audit(actor="extractor", action="injection.quarantined",
3184
3232
  object_id=node.id, detail=item.statement[:200])
3233
+ if not quarantined_block and authority_rank(authority) >= authority_rank("human_intent"):
3234
+ self._notice_explicit_negations(event, text, report)
3185
3235
  if not quarantined_block:
3186
3236
  # Read producer history rather than current mutable metadata. An
3187
3237
  # unrelated field edit or equal restatement does not withdraw this
@@ -3231,6 +3281,56 @@ class Engine:
3231
3281
  valid_to=event["recorded_at"], event_id=event_id)
3232
3282
  return not quarantined_block
3233
3283
 
3284
+ def _notice_explicit_negations(self, event: dict, text: str, report: dict) -> None:
3285
+ """Raise a human-gated notice when trusted prose explicitly negates an
3286
+ active assumption (ADR-133).
3287
+
3288
+ "The feed is NOT ordered by timestamp" or "we no longer assume the feed
3289
+ is ordered by timestamp", from an author the project trusts, fires a
3290
+ `contradictory_evidence` invalidation in `pending_confirmation`: the
3291
+ assumption and everything built on it are untouched until the owner
3292
+ confirms or rejects the notice through the authority grammar (CI-005).
3293
+ One notice per assumption; a rejected notice is not re-raised for the
3294
+ same sentence. This reads nothing for meaning: only the assumption's
3295
+ own wording with one negator, or a negated assumption cue, counts.
3296
+ """
3297
+ project_id = event["project_id"]
3298
+ active = [node for node in self.graph.current(
3299
+ project_id, "assumption", status=["active", "supported"], tenant_id=self.tenant_id)
3300
+ if self.authority_is_current(node)]
3301
+ if not active:
3302
+ return
3303
+ denied = explicit_negations(
3304
+ text, {node["node_id"]: node["data"].get("statement", "") for node in active})
3305
+ if not denied:
3306
+ return
3307
+ recorded = self.graph.current(
3308
+ project_id, "invalidation", status=["open", "pending_confirmation", "rejected"],
3309
+ tenant_id=self.tenant_id)
3310
+ for node_id in sorted(denied):
3311
+ try:
3312
+ reason = validate_human_text(
3313
+ denied[node_id][:300], field="invalidation reason", max_length=300)
3314
+ except ValueError:
3315
+ # The sentence carried control characters; the notice still
3316
+ # stands, the quoted text does not.
3317
+ reason = "explicit negation of the assumption (sentence withheld)"
3318
+ if any(inv["data"].get("trigger_type") == "contradictory_evidence"
3319
+ and inv["data"].get("target_node_id") == node_id
3320
+ and (inv["status"] != "rejected" or inv["data"].get("reason") == reason)
3321
+ for inv in recorded):
3322
+ continue
3323
+ # The identity derives from the ingest event and the target, so a
3324
+ # later canonical decision that names it replays onto the same node.
3325
+ notice_id = "inv_" + digest_obj(
3326
+ [_NOTICE_SCHEMA, self.tenant_id, project_id, event["event_id"], node_id])[7:31]
3327
+ inv = self.invalidation.fire(
3328
+ tenant_id=self.tenant_id, project_id=project_id, target_node_id=node_id,
3329
+ trigger_type="contradictory_evidence", trigger_confidence=0.9,
3330
+ reason=reason, event_id=event["event_id"], requires_human=True,
3331
+ invalidation_id=notice_id)
3332
+ report["invalidations"].append(inv["node_id"])
3333
+
3234
3334
  @staticmethod
3235
3335
  def _initial_status(entity_type: str, item, existing) -> str | None:
3236
3336
  if existing is not None:
@@ -4554,8 +4654,23 @@ class Engine:
4554
4654
  + "; ".join(shape_errors))
4555
4655
  check = verify_envelope(proof, self.signer)
4556
4656
  if not check["valid"]:
4557
- raise PermissionError("completion rejected: proof envelope invalid"
4558
- " or tampered")
4657
+ # Name the check that failed (PA-002). One undifferentiated
4658
+ # message made an envelope under an unregistered key, or one
4659
+ # whose recorded status no longer matches its contents, read
4660
+ # exactly like an edited body, so the operator could not tell
4661
+ # which of the four to repair. The gate phrase itself is
4662
+ # unchanged: tests/test_instrument_validation.py keys on it.
4663
+ failed = [name for name, ok in (
4664
+ ("digest", check.get("digest_ok")),
4665
+ ("signature", check.get("signature_ok")),
4666
+ ("authenticity", check.get("authentic")),
4667
+ ("sufficiency", check.get("sufficiency_ok", True)),
4668
+ ) if not ok]
4669
+ detail = check.get("reason")
4670
+ raise PermissionError(
4671
+ "completion rejected: proof envelope invalid or tampered"
4672
+ f" (failed: {', '.join(failed) or 'unclassified'})"
4673
+ + (f": {detail}" if detail else ""))
4559
4674
  if proof.get("status") != "verified":
4560
4675
  raise PermissionError(
4561
4676
  f"completion rejected: proof status is {proof.get('status')!r},"
@@ -31,7 +31,7 @@ from .core import canonical_json, strict_json_loads
31
31
  from .ontology import AUTHORITY_RANK, authority_rank
32
32
 
33
33
  EXTRACTOR_NAME = "cce-deterministic"
34
- EXTRACTOR_VERSION = "1.4.0"
34
+ EXTRACTOR_VERSION = "1.5.0"
35
35
 
36
36
 
37
37
  @dataclass
@@ -92,6 +92,17 @@ _CLAUSE_TAIL = r"(?:[^.\n]|\.(?!\s|\Z)|" + _SOFT_WRAP + r")"
92
92
  # statement is worse than dropping one: nothing downstream can tell.
93
93
  _GAP = r"(?:[ \t]|" + _SOFT_WRAP + r")+"
94
94
 
95
+ # A cue that is itself negated denies the assumption instead of making it.
96
+ # "We no longer assume X" and "nothing relies on X" extracted the same positive
97
+ # proposal as "We assume X", so a sentence written to retire an assumption
98
+ # could, once confirmed, re-assert it. The negator has to stand immediately
99
+ # before the cue (allowing only a pronoun or "to" in between); a `not`
100
+ # elsewhere in the sentence is not read as denial (ADR-132).
101
+ _NEGATED_CUE = re.compile(
102
+ r"(?:\bnot|n['\u2019]t|\bcannot|\bnever|\bno[ \t]+longer|\bstop(?:s|ped)?|"
103
+ r"\bwithout|\bnothing|\bnobody|\bno[ \t]+one)"
104
+ r"(?:[ \t]+(?:we|you|they|it|to|be))*[ \t]*\Z", re.I)
105
+
95
106
  # Patterns: (kind, regex, base_confidence)
96
107
  _PATTERNS: list[tuple[str, re.Pattern, float]] = [
97
108
  ("assumption", re.compile(
@@ -233,6 +244,10 @@ class DeterministicExtractor:
233
244
  if _boundary_has_mark_or_zwj_continuation(text, raw_after):
234
245
  result.abstained += 1
235
246
  continue
247
+ if kind == "assumption" and _NEGATED_CUE.search(
248
+ prose[max(0, m.start() - 40):m.start()]):
249
+ result.abstained += 1
250
+ continue
236
251
  statement = _clean(
237
252
  text[offsets[m.start("s")]:offsets[m.end("s")]])
238
253
  if not _plausible(statement):
@@ -364,6 +379,90 @@ def normalize_statement(statement: str) -> str:
364
379
  return re.sub(r"\s+", " ", s).strip()
365
380
 
366
381
 
382
+ # An explicit negation of a statement: the statement itself with exactly one
383
+ # negator inserted after an auxiliary ("the feed is NOT ordered"), or a negated
384
+ # assumption cue ("we no longer assume the feed is ordered"), or the assumption
385
+ # named and called wrong. This is the same closed lexical control as ADR-119,
386
+ # aimed at assumptions: it reads nothing for meaning, so "the feed arrives
387
+ # unordered" is outside it and stays outside it on purpose (ADR-133).
388
+ _SENTENCE_END = re.compile(r"(?<=[.!?])\s+|\n+")
389
+ _AUXILIARY_NEGATION = re.compile(
390
+ r"\b(is|are|was|were|does|do|did|can|will|has|have|had|should|must|shall|would|could)"
391
+ r"(?:\s+not|n['\u2019]t|\s+never|\s+no\s+longer)\b", re.I)
392
+ _IRREGULAR_NEGATION = (
393
+ (re.compile(r"\bcannot\b", re.I), "can"),
394
+ (re.compile(r"\bwon['\u2019]t\b", re.I), "will"),
395
+ )
396
+ _NEGATED_CUE_CLAUSE = re.compile(
397
+ r"(?:\bnot|n['\u2019]t|\bcannot|\bnever|\bno\s+longer|\bstop(?:s|ped)?|\bwithout|"
398
+ r"\bnothing|\bnobody|\bno\s+one)(?:\s+(?:we|you|they|it|to|be))*\s+"
399
+ r"(?:assum(?:e|es|ing|ption)(?:\s+that)?|rel(?:y|ies|ying)\s+on(?:\s+the\s+fact\s+that)?|"
400
+ r"depend(?:s|ing)?\s+on(?:\s+the\s+fact\s+that)?)\s+(?P<s>.+)", re.I)
401
+ _ASSUMPTION_CALLED_WRONG = re.compile(
402
+ r"\bthe\s+assumption\s+that\s+(?P<s>.+?)\s+(?:is|was)\s+"
403
+ r"(?:wrong|false|incorrect|invalid|no\s+longer\s+(?:true|valid))\b", re.I)
404
+
405
+
406
+ def _denial_candidates(sentence: str) -> list[str]:
407
+ """Statements a single sentence explicitly denies, before normalization."""
408
+ found = []
409
+ cue = _NEGATED_CUE_CLAUSE.search(sentence)
410
+ if cue:
411
+ found.append(cue.group("s"))
412
+ wrong = _ASSUMPTION_CALLED_WRONG.search(sentence)
413
+ if wrong:
414
+ found.append(wrong.group("s"))
415
+ positive, count = _AUXILIARY_NEGATION.subn(r"\1", sentence, count=1)
416
+ if count == 0:
417
+ for pattern, auxiliary in _IRREGULAR_NEGATION:
418
+ positive, count = pattern.subn(auxiliary, sentence, count=1)
419
+ if count:
420
+ break
421
+ if count == 1 and not _AUXILIARY_NEGATION.search(positive):
422
+ found.append(positive)
423
+ return found
424
+
425
+
426
+ def explicit_negations(text: str, statements: dict[str, str]) -> dict[str, str]:
427
+ """Map each statement key that `text` explicitly denies to the denying
428
+ sentence (ADR-133).
429
+
430
+ `statements` maps an opaque key to a statement, normally an active
431
+ assumption's node id to its statement. Only prose is read: fenced code,
432
+ quotes and HTML comments are masked exactly as for extraction, and format
433
+ controls are removed before matching. Comparison uses the extractor's
434
+ normalized statement identity, so case, punctuation and stop words do not
435
+ matter but every content word does.
436
+ """
437
+ if not text or not statements:
438
+ return {}
439
+ wanted = {}
440
+ for key, statement in statements.items():
441
+ normalized = normalize_statement(statement or "")
442
+ if normalized:
443
+ wanted.setdefault(normalized, []).append(key)
444
+ if not wanted:
445
+ return {}
446
+ visible, _ = _without_invisibles(text)
447
+ denied: dict[str, str] = {}
448
+ for raw in _SENTENCE_END.split(_prose_only(visible)):
449
+ sentence = re.sub(r"\s+", " ", raw).strip().rstrip(".!?;:,")
450
+ if not sentence:
451
+ continue
452
+ # "Correction: the feed is not ordered" and "Checked the producer today:
453
+ # the feed is not ordered" carry one leading label or clause ending in a
454
+ # colon; it is not part of the statement being denied. The clause is
455
+ # bounded so a colon deep inside unrelated prose cannot manufacture one.
456
+ unlabelled = re.sub(r"^[^:]{1,80}:\s+", "", sentence)
457
+ candidates = _denial_candidates(sentence)
458
+ if unlabelled != sentence:
459
+ candidates += _denial_candidates(unlabelled)
460
+ for candidate in candidates:
461
+ for key in wanted.get(normalize_statement(candidate.rstrip(".!?;:,")), ()):
462
+ denied.setdefault(key, sentence)
463
+ return denied
464
+
465
+
367
466
  # Regions of a comment that are not the author asserting something. A fenced
368
467
  # block is code, and code comments are full of modal verbs; a blockquote is
369
468
  # someone else's words, frequently quoted in order to disagree with them; an