linkedparticles-core 1.187.2__tar.gz → 1.190.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 (222) hide show
  1. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/PKG-INFO +1 -1
  2. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/context.jsonld +2 -0
  3. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/interchange.schema.json +2 -0
  4. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/particle.schema.json +10 -0
  5. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/shacl/ParticleShape.ttl +19 -0
  6. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/config.py +162 -7
  7. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/contract.py +10 -0
  8. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/schema.py +171 -3
  9. linkedparticles_core-1.190.0/particles/core/validity.py +392 -0
  10. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/general.py +209 -4
  11. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/interchange/codec.py +10 -0
  12. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/__init__.py +4 -0
  13. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/registry.py +35 -5
  14. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/pyproject.toml +1 -1
  15. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_validity_extraction.py +200 -0
  16. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/LICENSE +0 -0
  17. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/NOTICE +0 -0
  18. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/README.md +0 -0
  19. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/conformance/README.md +0 -0
  20. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/conformance/profile.yaml +0 -0
  21. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/conformance/similarity_vectors.json +0 -0
  22. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/shacl/CorpusSnapshotShape.ttl +0 -0
  23. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/shacl/ProvenanceChainShape.ttl +0 -0
  24. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/shacl/SubjectShape.ttl +0 -0
  25. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/shacl/TrustStatementShape.ttl +0 -0
  26. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/artifacts/schemas/trust_lens.schema.json +0 -0
  27. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/constraints.txt +0 -0
  28. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/__init__.py +0 -0
  29. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/__init__.py +0 -0
  30. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/_resources.py +0 -0
  31. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/fixtures.py +0 -0
  32. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/jsonschema.py +0 -0
  33. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/profile.py +0 -0
  34. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/runner.py +0 -0
  35. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/shacl.py +0 -0
  36. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/types.py +0 -0
  37. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/conformance/validator.py +0 -0
  38. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/__init__.py +0 -0
  39. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/_resources.py +0 -0
  40. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/cascade_gate.py +0 -0
  41. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/claims.py +0 -0
  42. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/closure_measure.py +0 -0
  43. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/conflict_resolution.py +0 -0
  44. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/conflict_review.py +0 -0
  45. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/consolidation_cadence.py +0 -0
  46. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/contradiction_disclosure.py +0 -0
  47. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/duplicate_key.py +0 -0
  48. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/equivalence.py +0 -0
  49. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/fingerprint.py +0 -0
  50. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/generics.py +0 -0
  51. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/granularity.py +0 -0
  52. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/held_release.py +0 -0
  53. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/jsonld_context.py +0 -0
  54. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/modality.py +0 -0
  55. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/observer_scope.py +0 -0
  56. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/past_value.py +0 -0
  57. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/predicate_profile.py +0 -0
  58. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/probe_verdict.py +0 -0
  59. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/progress.py +0 -0
  60. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/provenance.py +0 -0
  61. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/reanchor.py +0 -0
  62. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/scoring/__init__.py +0 -0
  63. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/scoring/confidence.py +0 -0
  64. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/scoring/decay.py +0 -0
  65. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/scoring/relevance.py +0 -0
  66. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/scoring/utility.py +0 -0
  67. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/spend_budget.py +0 -0
  68. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/stance.py +0 -0
  69. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/status.py +0 -0
  70. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/vocabulary.py +0 -0
  71. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/vocabulary_jsonld.py +0 -0
  72. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/core/vocabulary_report.py +0 -0
  73. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/egress.py +0 -0
  74. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/embeddings.py +0 -0
  75. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/__init__.py +0 -0
  76. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/agent.py +0 -0
  77. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/append_delta.py +0 -0
  78. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/calibration.py +0 -0
  79. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/components.py +0 -0
  80. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/docstrings.py +0 -0
  81. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/github/__init__.py +0 -0
  82. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/github/_shared.py +0 -0
  83. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/github/gist.py +0 -0
  84. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/github/pages.py +0 -0
  85. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/github/repo.py +0 -0
  86. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/hackernews.py +0 -0
  87. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/incremental.py +0 -0
  88. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/journal.py +0 -0
  89. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/mastodon.py +0 -0
  90. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/mcp_memory.py +0 -0
  91. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/migration_preview.py +0 -0
  92. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/modality.py +0 -0
  93. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/nomisma.py +0 -0
  94. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/numista/__init__.py +0 -0
  95. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/numista/_shared.py +0 -0
  96. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/numista/coin.py +0 -0
  97. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/numista/issuer.py +0 -0
  98. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/numista/listing.py +0 -0
  99. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/polarity.py +0 -0
  100. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/property_keys.py +0 -0
  101. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/rdf.py +0 -0
  102. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/reddit.py +0 -0
  103. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/registry.py +0 -0
  104. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/scope.py +0 -0
  105. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/session_state.py +0 -0
  106. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/structure.py +0 -0
  107. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/subject_gate.py +0 -0
  108. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/subject_scope.py +0 -0
  109. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/taxonomy.py +0 -0
  110. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/tool_turns.py +0 -0
  111. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/trust_lens.py +0 -0
  112. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/vocabulary.py +0 -0
  113. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/extraction/wikidata.py +0 -0
  114. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/http.py +0 -0
  115. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/interchange/__init__.py +0 -0
  116. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/interchange/jsonl.py +0 -0
  117. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/interchange/yaml_ld.py +0 -0
  118. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/adapters/__init__.py +0 -0
  119. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/adapters/anthropic.py +0 -0
  120. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/adapters/local.py +0 -0
  121. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/adapters/openai_compat.py +0 -0
  122. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/batch_budget.py +0 -0
  123. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/breaker.py +0 -0
  124. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/client.py +0 -0
  125. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/errors.py +0 -0
  126. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/fencing.py +0 -0
  127. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/logprobs.py +0 -0
  128. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/pool.py +0 -0
  129. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/replay.py +0 -0
  130. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/spend_budget.py +0 -0
  131. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/llm/usage.py +0 -0
  132. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/py.typed +0 -0
  133. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/render/__init__.py +0 -0
  134. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/render/markdown.py +0 -0
  135. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/secrets.py +0 -0
  136. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/url_canonical.py +0 -0
  137. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/particles/url_safety.py +0 -0
  138. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/__init__.py +0 -0
  139. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/_client_fixtures.py +0 -0
  140. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/_upstream.py +0 -0
  141. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/MANIFEST.yaml +0 -0
  142. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/hackernews-thread-001/content.bin +0 -0
  143. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/hackernews-thread-001/manifest.yaml +0 -0
  144. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/hackernews-thread-001/snapshot.json +0 -0
  145. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/journal-entry-001/content.bin +0 -0
  146. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/journal-entry-001/manifest.yaml +0 -0
  147. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/journal-entry-001/snapshot.json +0 -0
  148. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mastodon-thread-001/content.bin +0 -0
  149. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mastodon-thread-001/manifest.yaml +0 -0
  150. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mastodon-thread-001/snapshot.json +0 -0
  151. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mcp-memory-export-001/content.bin +0 -0
  152. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mcp-memory-export-001/manifest.yaml +0 -0
  153. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/mcp-memory-export-001/snapshot.json +0 -0
  154. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/nomisma-concept-001/content.bin +0 -0
  155. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/nomisma-concept-001/manifest.yaml +0 -0
  156. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/nomisma-concept-001/snapshot.json +0 -0
  157. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-coin-001/content.bin +0 -0
  158. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-coin-001/manifest.yaml +0 -0
  159. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-coin-001/snapshot.json +0 -0
  160. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-issuer-001/content.bin +0 -0
  161. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-issuer-001/manifest.yaml +0 -0
  162. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-issuer-001/snapshot.json +0 -0
  163. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-listing-001/content.bin +0 -0
  164. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-listing-001/manifest.yaml +0 -0
  165. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/numista-listing-001/snapshot.json +0 -0
  166. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/rdf-graph-001/content.bin +0 -0
  167. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/rdf-graph-001/manifest.yaml +0 -0
  168. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/rdf-graph-001/snapshot.json +0 -0
  169. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/reddit-thread-001/content.bin +0 -0
  170. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/reddit-thread-001/manifest.yaml +0 -0
  171. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/reddit-thread-001/snapshot.json +0 -0
  172. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/taxonomy-coins-001/content.bin +0 -0
  173. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/taxonomy-coins-001/manifest.yaml +0 -0
  174. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/taxonomy-coins-001/snapshot.json +0 -0
  175. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/trust-lens-001/content.bin +0 -0
  176. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/trust-lens-001/manifest.yaml +0 -0
  177. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/trust-lens-001/snapshot.json +0 -0
  178. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-001/content.bin +0 -0
  179. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-001/manifest.yaml +0 -0
  180. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-001/snapshot.json +0 -0
  181. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-002/content.bin +0 -0
  182. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-002/manifest.yaml +0 -0
  183. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-article-002/snapshot.json +0 -0
  184. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-essay-001/content.bin +0 -0
  185. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-essay-001/manifest.yaml +0 -0
  186. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-essay-001/snapshot.json +0 -0
  187. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-interview-001/content.bin +0 -0
  188. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-interview-001/manifest.yaml +0 -0
  189. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/web-interview-001/snapshot.json +0 -0
  190. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/wikidata-entity-001/content.bin +0 -0
  191. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/wikidata-entity-001/manifest.yaml +0 -0
  192. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conformance/fixtures/wikidata-entity-001/snapshot.json +0 -0
  193. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/conftest.py +0 -0
  194. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/fixtures/scope/normative-rule-file.md +0 -0
  195. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/fixtures/scope/normative-rule-file.recorded.json +0 -0
  196. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/fixtures/scope/rule-file-agents-md.md +0 -0
  197. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/fixtures/scope/rule-file-agents-md.recorded.json +0 -0
  198. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_calibration_isotonic.py +0 -0
  199. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_confidence.py +0 -0
  200. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_config_client_sections.py +0 -0
  201. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_config_sample.py +0 -0
  202. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_conformance_fixture_gen.py +0 -0
  203. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_conformance_similarity_vectors.py +0 -0
  204. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_conformance_validator.py +0 -0
  205. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_core_contradiction_disclosure.py +0 -0
  206. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_core_held_release.py +0 -0
  207. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_core_observer_scope.py +0 -0
  208. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_core_provenance.py +0 -0
  209. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_core_vocabulary.py +0 -0
  210. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_egress_policy.py +0 -0
  211. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_embeddings.py +0 -0
  212. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_extraction_agent.py +0 -0
  213. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_extraction_vision.py +0 -0
  214. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_interchange_schema.py +0 -0
  215. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_llm_batch_budget.py +0 -0
  216. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_llm_logprobs.py +0 -0
  217. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_llm_pool.py +0 -0
  218. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_llm_replay.py +0 -0
  219. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_llm_usage.py +0 -0
  220. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_schema.py +0 -0
  221. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_secrets.py +0 -0
  222. {linkedparticles_core-1.187.2 → linkedparticles_core-1.190.0}/tests/test_url_canonical.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: linkedparticles-core
3
- Version: 1.187.2
3
+ Version: 1.190.0
4
4
  Summary: Store-free client library for Particles: define, validate, serialize, and extract provenance-tracked belief particles
5
5
  Project-URL: Homepage, https://linkedparticles.org
6
6
  Project-URL: Documentation, https://docs.linkedparticles.org
@@ -63,6 +63,8 @@
63
63
  "particleType": { "@id": "particles:particleType" },
64
64
  "assertionModality": { "@id": "particles:assertionModality" },
65
65
  "validUntil": { "@id": "psum:duration", "@type": "xsd:dateTime" },
66
+ "validFrom": { "@id": "particles:validFrom", "@type": "xsd:dateTime" },
67
+ "validKind": { "@id": "particles:validKind" },
66
68
  "supersedes": { "@id": "particles:supersedes", "@type": "@id" },
67
69
  "subjectIds": { "@id": "particles:subject", "@type": "@id", "@container": "@set" },
68
70
  "subjects": { "@id": "particles:subjects", "@container": "@list" },
@@ -85,6 +85,8 @@
85
85
  },
86
86
  "uncertaintyKind": { "type": "string" },
87
87
  "validUntil": { "type": "string", "format": "date-time" },
88
+ "validFrom": { "type": "string", "format": "date-time" },
89
+ "validKind": { "type": "string", "enum": ["EVENT", "STATE"] },
88
90
  "supersedes": { "type": "string" },
89
91
  "basis": {
90
92
  "type": "object",
@@ -100,6 +100,16 @@
100
100
  "format": "date-time",
101
101
  "description": "Core: expiry for time-bounded claims (lazy query-time filter, §9.3)"
102
102
  },
103
+ "valid_from": {
104
+ "type": ["string", "null"],
105
+ "format": "date-time",
106
+ "description": "Core: when the claim begins to hold in the world, the counterpart of valid_until; read by the window route (§9.3), never by reconciliation"
107
+ },
108
+ "valid_kind": {
109
+ "type": ["string", "null"],
110
+ "enum": ["EVENT", "STATE", null],
111
+ "description": "Core: how the world-time pair is read; EVENT is a moment (the point [valid_from, valid_from], never with a valid_until), STATE a span"
112
+ },
103
113
  "supersedes": {
104
114
  "type": ["string", "null"],
105
115
  "format": "uuid",
@@ -112,6 +112,25 @@ particles:ParticleShape
112
112
  sh:message "assertion_modality, when present, must be FALSIFIABLE, EVALUATIVE, EXPERIENTIAL, or CONSTITUTIVE." ;
113
113
  ] ;
114
114
 
115
+ # validKind — optional reading of the world-time pair: EVENT is
116
+ # a moment, STATE a span. An EVENT never carries a validUntil — a
117
+ # cardinality across two properties the shape cannot express without a
118
+ # SPARQL constraint, so the Core model validator enforces it.
119
+ sh:property [
120
+ sh:path particles:validKind ;
121
+ sh:maxCount 1 ;
122
+ sh:datatype xsd:string ;
123
+ sh:in ( "EVENT" "STATE" ) ;
124
+ sh:message "valid_kind, when present, must be EVENT or STATE." ;
125
+ ] ;
126
+
127
+ sh:property [
128
+ sh:path particles:validFrom ;
129
+ sh:maxCount 1 ;
130
+ sh:datatype xsd:dateTime ;
131
+ sh:message "valid_from, when present, must be a single xsd:dateTime." ;
132
+ ] ;
133
+
115
134
  # canonicalForm — optional marker of which of the prose/structured pair is
116
135
  # the assertion; PROSE default. STRUCTURED additionally requires
117
136
  # a structuredClaim to be present — a cardinality the shape cannot express
@@ -409,7 +409,9 @@ class ProviderSelection(BaseModel):
409
409
  """
410
410
 
411
411
  provider: str = "anthropic"
412
- model: str = "claude-sonnet-4-6"
412
+ # The compiled default since 1.189.0, when it moved from claude-sonnet-4-6
413
+ # behind measurements registered before the runs.
414
+ model: str = "claude-sonnet-5-5"
413
415
 
414
416
 
415
417
  class OpenAICompatProviderConfig(BaseModel):
@@ -572,6 +574,27 @@ _LIST_PRICES_PER_MTOK: dict[str, tuple[float, float]] = {
572
574
  }
573
575
 
574
576
 
577
+ # Measured estimate factors that ship for the compiled default and the model it
578
+ # replaced, keyed like ``llm.price_per_mtok``. Characters per input
579
+ # token follow the shipped convention: the old tokenizer's 4.0 rule of thumb
580
+ # divided by the measured token ratio against claude-sonnet-4-6 (1.3437 on one
581
+ # run's byte-identical extraction prompts, token-counting endpoint, 2026-10-13).
582
+ # The output scale is relative to claude-sonnet-5, which the audit's per-call
583
+ # figures were fitted on: claude-sonnet-5-5 wrote 59,760 output tokens on the
584
+ # 2026-10 provider survey's 18 calls against claude-sonnet-5's 111,950 on the same
585
+ # calls (0.534). The extraction figure is the memory benchmark's 6,700 scaled the
586
+ # same way. The claude-sonnet-4-6 entries come from paired run
587
+ # (docs/benchmarks/default-model-adr0317-2026-10-13.md).
588
+ _SHIPPED_CHARS_PER_TOKEN: dict[str, float] = {"claude-sonnet-5-5": 2.98}
589
+ _SHIPPED_OUTPUT_SCALE: dict[str, float] = {
590
+ "claude-sonnet-5-5": 0.534,
591
+ "claude-sonnet-4-6": 0.800,
592
+ }
593
+ _SHIPPED_EXTRACTION_OUTPUT_TOKENS: dict[str, int] = {
594
+ "claude-sonnet-5-5": 3_578,
595
+ "claude-sonnet-4-6": 5_363,
596
+ }
597
+
575
598
  _T = TypeVar("_T")
576
599
 
577
600
 
@@ -591,6 +614,23 @@ def lookup_by_model(
591
614
  return None, None
592
615
 
593
616
 
617
+ #: Every completion purpose, in the order the operator-facing lines print them.
618
+ #: A literal here because config cannot import ``particles.llm``;
619
+ #: tests/test_config.py pins it equal to ``particles.llm.LLMPurpose``.
620
+ LLM_PURPOSES: tuple[str, ...] = (
621
+ "extraction",
622
+ "semantic_lint",
623
+ "verification",
624
+ "subject_resolution",
625
+ "query_response",
626
+ "synthesis",
627
+ "abstraction",
628
+ "benchmark",
629
+ "benchmark_answer",
630
+ "use_judge",
631
+ )
632
+
633
+
594
634
  def _default_prices() -> dict[str, TokenPrice]:
595
635
  return {
596
636
  model: TokenPrice(input=inp, output=out)
@@ -603,9 +643,11 @@ class LLMConfig(BaseModel):
603
643
 
604
644
  ``default`` is the fallback pairing; each purpose may override it. The
605
645
  operator can thus route high-volume ``extraction`` to a cheap model while
606
- keeping low-volume ``synthesis`` on a hosted one. All compiled defaults are
607
- ``claude-sonnet-4-6`` so the out-of-the-box behaviour is unchanged from
608
- before the port existed.
646
+ keeping low-volume ``synthesis`` on a hosted one. Every purpose falls back
647
+ to the compiled default, ``claude-sonnet-5-5`` since 1.189.0 (
648
+ ``claude-sonnet-4-6`` before), except ``use_judge``, which carries its own
649
+ compiled selection. ``particles config validate`` names the
650
+ default in force and where it came from.
609
651
 
610
652
  The legacy ``extraction.model`` / ``wiki.model`` keys are migrated into
611
653
  this section by ``_migrate_legacy_keys`` (``extraction.model`` →
@@ -712,6 +754,30 @@ class LLMConfig(BaseModel):
712
754
  override = getattr(self, purpose, None)
713
755
  return override if isinstance(override, ProviderSelection) else self.default
714
756
 
757
+ def selection_source(self, purpose: str) -> str:
758
+ """Where ``purpose``'s selection comes from, as an operator reads it.
759
+
760
+ ``"llm.<purpose>"`` when the configuration sets that purpose,
761
+ ``"compiled llm.<purpose>"`` for a purpose with its own compiled
762
+ selection that the configuration leaves alone (``use_judge``),
763
+ ``"llm.default"`` when the purpose falls back to a configured default,
764
+ and ``"compiled default"`` when nothing set it. ``"default"`` itself
765
+ reads as either of the last two.
766
+ """
767
+ if purpose != "default" and getattr(self, purpose, None) is not None:
768
+ if purpose in self.model_fields_set:
769
+ return f"llm.{purpose}"
770
+ return f"compiled llm.{purpose}"
771
+ return "llm.default" if "default" in self.model_fields_set else "compiled default"
772
+
773
+ def routed_elsewhere(self) -> dict[str, ProviderSelection]:
774
+ """The purposes whose selection differs from ``default``, in purpose order."""
775
+ return {
776
+ purpose: sel
777
+ for purpose in LLM_PURPOSES
778
+ if (sel := self.for_purpose(purpose)) != self.default
779
+ }
780
+
715
781
  @model_validator(mode="after")
716
782
  def _validate_provider_wiring(self) -> LLMConfig:
717
783
  """Fail dangling provider names / adapter kinds at config load.
@@ -976,6 +1042,16 @@ class ModalityRegenerationConfig(BaseModel):
976
1042
  commit_interval: int = Field(default=25, ge=1)
977
1043
 
978
1044
 
1045
+ class ValidityBackfillConfig(BaseModel):
1046
+ """Defaults for ``particles validity``, the prose-date default for ``valid_from``.
1047
+
1048
+ The pass makes no model call, so it has no rate limit; ``commit_interval``
1049
+ is how often it commits, so an interrupt costs seconds of work.
1050
+ """
1051
+
1052
+ commit_interval: int = Field(default=200, ge=1)
1053
+
1054
+
979
1055
  class ExtractionPolarityConfig(BaseModel):
980
1056
  """LLM-semantic claim-polarity classification of candidates (cap. 1).
981
1057
 
@@ -1777,6 +1853,10 @@ class SubjectsConfig(BaseModel):
1777
1853
  # claude-sonnet-4-6; on an ordinary-prose gold set, 0.898 to 0.918 against
1778
1854
  # 0.735, 45 of 49 names judged at about US$0.20 per 100 names, no correct
1779
1855
  # link lost on either (docs/benchmarks/subject-resolution-judge-2026-10-01.md).
1856
+ # Re-measured 2026-10-13 on claude-sonnet-5-5, the compiled default since
1857
+ # 1.189.0, at 7 hits: 1.000 on the suite and 0.939 on the ordinary-prose
1858
+ # set, no correct link lost, at about US$0.09 and US$0.20 per 100 names
1859
+ # (docs/benchmarks/default-model-adr0317-2026-10-13.md).
1780
1860
  # With no LLM key, an open breaker, or an unusable reply it takes the top
1781
1861
  # hit, so `top_hit` is also what an offline store gets. Set `top_hit` to
1782
1862
  # resolve with no model call at all.
@@ -3061,7 +3141,12 @@ class AuditConfig(BaseModel):
3061
3141
  # measured ~3,600 output tokens per memory-note call on claude-haiku-4-5
3062
3142
  # against ~6,700 on claude-sonnet-5, so routing ``llm.extraction`` to haiku
3063
3143
  # warrants an entry of about 0.55. A model with no entry is scaled by 1.
3064
- estimate_output_scale_by_model: dict[str, float] = Field(default_factory=dict)
3144
+ # Ships with measured entries for the compiled default and the model it
3145
+ # replaced (``_SHIPPED_OUTPUT_SCALE``); operator entries merge
3146
+ # over them key by key.
3147
+ estimate_output_scale_by_model: dict[str, float] = Field(
3148
+ default_factory=lambda: dict(_SHIPPED_OUTPUT_SCALE)
3149
+ )
3065
3150
  # Each call's output is taken to fall anywhere within this fraction of its
3066
3151
  # expected figure. The expected range runs from every call at (1 - spread)
3067
3152
  # to every call at (1 + spread), and a call whose output would pass
@@ -3108,6 +3193,14 @@ class AuditConfig(BaseModel):
3108
3193
  # Wall time per second reading: the measured runs read 22 flags in ~41 s.
3109
3194
  estimate_seconds_per_verification: float = Field(default=2.0, ge=0.0)
3110
3195
 
3196
+ @field_validator("estimate_output_scale_by_model", mode="before")
3197
+ @classmethod
3198
+ def _merge_over_shipped_scales(cls, value: Any) -> Any:
3199
+ """Operator entries replace the shipped ones key by key; the rest stay."""
3200
+ if isinstance(value, dict):
3201
+ return {**_SHIPPED_OUTPUT_SCALE, **value}
3202
+ return value
3203
+
3111
3204
  @field_validator("estimate_output_scale_by_model")
3112
3205
  @classmethod
3113
3206
  def _non_negative_output_scale(cls, value: dict[str, float]) -> dict[str, float]:
@@ -3524,8 +3617,10 @@ class MemoryBenchmarkConfig(BaseModel):
3524
3617
  # on claude-sonnet-5 but ~3.6k on claude-haiku-4-5 — so an arm routing
3525
3618
  # ``llm.extraction`` elsewhere is mis-estimated ~2x on the write side
3526
3619
  # without an entry here. The rendered estimate says which source it used.
3620
+ # Ships with measured entries for the compiled default and the model it
3621
+ # replaced; operator entries merge over them key by key.
3527
3622
  estimate_output_tokens_per_extraction_call_by_model: dict[str, int] = Field(
3528
- default_factory=dict
3623
+ default_factory=lambda: dict(_SHIPPED_EXTRACTION_OUTPUT_TOKENS)
3529
3624
  )
3530
3625
  # Characters per input token — the factor the estimate and the pre-flight
3531
3626
  # context-window check divide prompt bytes by. The scalar is the classic
@@ -3536,8 +3631,30 @@ class MemoryBenchmarkConfig(BaseModel):
3536
3631
  # lookup as ``llm.price_per_mtok`` — is what keeps their input projection and
3537
3632
  # their window verdict honest. The write side is keyed on the extraction
3538
3633
  # model, the answer side and the window check on the answer model.
3634
+ # The map ships one entry, for the compiled default, and the
3635
+ # scalar stays the old tokenizer's 4.0, so a model with no entry (a local
3636
+ # or OpenAI-compatible one included) keeps the factor it always had.
3637
+ # Operator entries merge over the shipped one key by key.
3539
3638
  chars_per_token: float = Field(default=4.0, gt=0.0)
3540
- chars_per_token_by_model: dict[str, float] = Field(default_factory=dict)
3639
+ chars_per_token_by_model: dict[str, float] = Field(
3640
+ default_factory=lambda: dict(_SHIPPED_CHARS_PER_TOKEN)
3641
+ )
3642
+
3643
+ @field_validator("estimate_output_tokens_per_extraction_call_by_model", mode="before")
3644
+ @classmethod
3645
+ def _merge_over_shipped_output_tokens(cls, value: Any) -> Any:
3646
+ """Operator entries replace the shipped ones key by key; the rest stay."""
3647
+ if isinstance(value, dict):
3648
+ return {**_SHIPPED_EXTRACTION_OUTPUT_TOKENS, **value}
3649
+ return value
3650
+
3651
+ @field_validator("chars_per_token_by_model", mode="before")
3652
+ @classmethod
3653
+ def _merge_over_shipped_chars_per_token(cls, value: Any) -> Any:
3654
+ """Operator entries replace the shipped ones key by key; the rest stay."""
3655
+ if isinstance(value, dict):
3656
+ return {**_SHIPPED_CHARS_PER_TOKEN, **value}
3657
+ return value
3541
3658
 
3542
3659
  @field_validator("estimate_output_tokens_per_extraction_call_by_model")
3543
3660
  @classmethod
@@ -3735,6 +3852,31 @@ class LeakageBenchmarkConfig(BaseModel):
3735
3852
  confirm_call_threshold: int = Field(default=50, ge=0)
3736
3853
 
3737
3854
 
3855
+ class UpdateGateBenchmarkConfig(BaseModel):
3856
+ """The trip-versus-move case and the update-gate pair sets.
3857
+
3858
+ Run counts and safety margins only. The pipeline under test runs the
3859
+ compiled defaults with every purpose pinned to the chosen model: nothing
3860
+ here re-tunes extraction, reconciliation or consolidation. There is no
3861
+ confirm threshold: every live run asks before its first call, because
3862
+ neither suite has a free live path.
3863
+ """
3864
+
3865
+ # The record's protocol (item 5): the bar is stated only when a
3866
+ # run uses these counts.
3867
+ move_runs: int = Field(default=3, ge=0)
3868
+ trip_alone_runs: int = Field(default=2, ge=0)
3869
+ # Samples per pair in the update-gate replay; the sets were registered at 3.
3870
+ samples: int = Field(default=3, ge=1)
3871
+ # The per-run ceiling --max-usd defaults to, as a multiple of a measured
3872
+ # estimate (a profile or the 2026-10-12 record). An upper-bound estimate is
3873
+ # already a ceiling and is used as is.
3874
+ max_usd_multiplier: float = Field(default=4.0, ge=1.0)
3875
+ # How long one run's child may take before it is stopped and reported
3876
+ # incomplete. A live run took a few minutes on 2026-10-12.
3877
+ run_timeout_seconds: float = Field(default=1800.0, gt=0.0)
3878
+
3879
+
3738
3880
  class BenchmarkConfig(BaseModel):
3739
3881
  """Benchmark-harness run persistence (family).
3740
3882
 
@@ -3903,6 +4045,7 @@ class ParticlesConfig(BaseModel):
3903
4045
  extraction_stance: ExtractionStanceConfig = Field(default_factory=ExtractionStanceConfig)
3904
4046
  extraction_polarity: ExtractionPolarityConfig = Field(default_factory=ExtractionPolarityConfig)
3905
4047
  extraction_validity: ExtractionValidityConfig = Field(default_factory=ExtractionValidityConfig)
4048
+ validity_backfill: ValidityBackfillConfig = Field(default_factory=ValidityBackfillConfig)
3906
4049
  extraction_vision: ExtractionVisionConfig = Field(default_factory=ExtractionVisionConfig)
3907
4050
  structured_claim: StructuredClaimConfig = Field(default_factory=StructuredClaimConfig)
3908
4051
  rdf: RdfConfig = Field(default_factory=RdfConfig)
@@ -3956,6 +4099,9 @@ class ParticlesConfig(BaseModel):
3956
4099
  default_factory=RelevanceFloorBenchmarkConfig
3957
4100
  )
3958
4101
  benchmark_leakage: LeakageBenchmarkConfig = Field(default_factory=LeakageBenchmarkConfig)
4102
+ benchmark_update_gate: UpdateGateBenchmarkConfig = Field(
4103
+ default_factory=UpdateGateBenchmarkConfig
4104
+ )
3959
4105
  benchmark: BenchmarkConfig = Field(default_factory=BenchmarkConfig)
3960
4106
  refetch_floors: dict[str, int] = Field(default_factory=lambda: dict(_DEFAULT_REFETCH_FLOORS))
3961
4107
  local_refresh: LocalRefreshConfig = Field(default_factory=LocalRefreshConfig)
@@ -4130,6 +4276,15 @@ _ENV_OVERRIDES: list[tuple[str, str, str]] = [
4130
4276
  ]
4131
4277
 
4132
4278
 
4279
+ def env_override_names() -> frozenset[str]:
4280
+ """Every environment variable that overrides a config field.
4281
+
4282
+ A benchmark that runs the SDK in a child process with a private environment
4283
+ removes these, so no override of the operator's reaches the run.
4284
+ """
4285
+ return frozenset(name for name, _section, _field in _ENV_OVERRIDES)
4286
+
4287
+
4133
4288
  # ---------------------------------------------------------------------------
4134
4289
  # Loader
4135
4290
  # ---------------------------------------------------------------------------
@@ -103,6 +103,16 @@ CONTRACT: list[FieldContract] = [
103
103
  ),
104
104
  FieldContract("uncertainty_kind", FieldTier.OPTIONAL, "Extension A free-text label"),
105
105
  FieldContract("valid_until", FieldTier.OPTIONAL, "Time-bounded claims only"),
106
+ FieldContract(
107
+ "valid_from",
108
+ FieldTier.OPTIONAL,
109
+ "Claims with a stated world-time start; the window route's input",
110
+ ),
111
+ FieldContract(
112
+ "valid_kind",
113
+ FieldTier.OPTIONAL,
114
+ "EVENT (a moment) or STATE (a span); how valid_from and valid_until are read",
115
+ ),
106
116
  FieldContract("supersedes", FieldTier.OPTIONAL, "Set by reindex, not by extractors"),
107
117
  FieldContract(
108
118
  "tags",
@@ -112,6 +112,19 @@ class AssertionModality(StrEnum):
112
112
  CONSTITUTIVE = "CONSTITUTIVE" # a rule a document establishes ("a Particle MUST …")
113
113
 
114
114
 
115
+ class ValidKind(StrEnum):
116
+ """How a claim's world-time interval is read.
117
+
118
+ An ``EVENT`` is a moment: its interval is the point ``[valid_from,
119
+ valid_from]``, and it never carries a ``valid_until``. A ``STATE`` is a
120
+ span from ``valid_from`` until it closes (its own ``valid_until``, or for a
121
+ retired claim the close rule), either end open.
122
+ """
123
+
124
+ EVENT = "EVENT"
125
+ STATE = "STATE"
126
+
127
+
115
128
  class ParticleType(StrEnum):
116
129
  CLAIM = "CLAIM"
117
130
  REVIEW = "REVIEW" # human review annotation records
@@ -596,6 +609,14 @@ class Particle(BaseModel):
596
609
  # (revision chain). Kept in declaration order for serialization
597
610
  # stability.
598
611
  valid_until: datetime | None = None
612
+ # Core (§6.2): the world-time axis beside the assertion-time
613
+ # one. ``valid_from`` is when the claim begins to hold; ``valid_kind``
614
+ # says whether it is a moment (EVENT) or a span (STATE). Read-time
615
+ # material for the window route and nothing else: never an input to
616
+ # reconciliation, ranking, or confidence. How a value was set rides on
617
+ # ``properties`` (``extraction:valid_from_basis`` and its siblings).
618
+ valid_from: datetime | None = None
619
+ valid_kind: ValidKind | None = None
599
620
  supersedes: str | None = None
600
621
  # Extension A field — stored but not processed by Core logic
601
622
  basis: dict[str, Any] | None = None
@@ -667,6 +688,28 @@ class Particle(BaseModel):
667
688
  raise ValueError("canonical_form=STRUCTURED requires a structured_claim")
668
689
  return self
669
690
 
691
+ @model_validator(mode="after")
692
+ def _check_validity_interval(self) -> Particle:
693
+ """The world-time interval is well-formed.
694
+
695
+ A start after the end is no interval, and a moment has one end, so an
696
+ EVENT never carries a ``valid_until``.
697
+ """
698
+ if self.valid_from is not None and self.valid_until is not None:
699
+ start = (
700
+ self.valid_from if self.valid_from.tzinfo else self.valid_from.replace(tzinfo=UTC)
701
+ )
702
+ end = (
703
+ self.valid_until
704
+ if self.valid_until.tzinfo
705
+ else self.valid_until.replace(tzinfo=UTC)
706
+ )
707
+ if start > end:
708
+ raise ValueError("valid_from must not be after valid_until")
709
+ if self.valid_kind is ValidKind.EVENT and self.valid_until is not None:
710
+ raise ValueError("an EVENT is a moment and carries no valid_until")
711
+ return self
712
+
670
713
 
671
714
  def is_truth_apt(particle: Particle) -> bool:
672
715
  """Whether the engine should apply truth-semantics to this particle.
@@ -831,8 +874,9 @@ class ExtractorCalibration(BaseModel):
831
874
  pipeline applies the record only when the current extraction
832
875
  provider/model matches; on mismatch the particle falls back to
833
876
  ``calibration_source=EXTRACTOR_DIRECT``. ``None`` is a legacy record
834
- (fitted before this key existed); it is treated as the historical
835
- default ``anthropic:claude-sonnet-4-6``.
877
+ (fitted before this key existed); it is treated as
878
+ ``anthropic:claude-sonnet-4-6``, the compiled default before 1.189.0
879
+ and the model those records were fitted on.
836
880
  heldout_suite_id: Benchmark suite(s) whose recorded runs the fit was
837
881
  scored on out of sample, ``+``-joined like
838
882
  ``benchmark_suite_id``. ``None`` on a record persisted before the
@@ -1178,6 +1222,37 @@ class StructuralGroupBy(StrEnum):
1178
1222
  OBJECT = "object"
1179
1223
 
1180
1224
 
1225
+ class TimeWindow(BaseModel):
1226
+ """An interval of world time a query asks about.
1227
+
1228
+ Either end may be open; a window with both ends equal is a point, the
1229
+ ``--on`` form. Timezone-naive values are read as UTC, the store's
1230
+ convention.
1231
+ """
1232
+
1233
+ start: datetime | None = None
1234
+ end: datetime | None = None
1235
+
1236
+ @model_validator(mode="after")
1237
+ def _check_bounds(self) -> TimeWindow:
1238
+ if self.start is None and self.end is None:
1239
+ raise ValueError("a time window needs a start or an end")
1240
+ # Naive values are read as UTC, the store's convention (the as_of
1241
+ # validator does the same), so the two ends and the claims compare.
1242
+ if self.start is not None and self.start.tzinfo is None:
1243
+ self.start = self.start.replace(tzinfo=UTC)
1244
+ if self.end is not None and self.end.tzinfo is None:
1245
+ self.end = self.end.replace(tzinfo=UTC)
1246
+ if self.start is not None and self.end is not None and self.start > self.end:
1247
+ raise ValueError("a time window's start must not be after its end")
1248
+ return self
1249
+
1250
+ @property
1251
+ def is_point(self) -> bool:
1252
+ """True for the ``--on`` form: both ends set and equal."""
1253
+ return self.start is not None and self.end is not None and self.start == self.end
1254
+
1255
+
1181
1256
  class QueryRequest(BaseModel):
1182
1257
  # Bounded so an oversized question can't drive an arbitrarily large prompt
1183
1258
  # into the paid query-response completion (security review F6). 8192 chars is
@@ -1234,6 +1309,14 @@ class QueryRequest(BaseModel):
1234
1309
  # is rejected by the validator below (HTTP 422; CLI/MCP surface the
1235
1310
  # message).
1236
1311
  as_of: datetime | None = None
1312
+ # the world-time window — select the claims that began in,
1313
+ # or were in force during, this interval of world time. Unset (None) is
1314
+ # byte-for-byte today's read. With a question it prefilters the candidate
1315
+ # set before scoring and ``top_k``; without one it selects the
1316
+ # deterministic listing ordered by ``valid_from``. Composes with
1317
+ # ``as_of`` (world time, then believed-at-T visibility). A window read
1318
+ # admits retired claims for the period they held.
1319
+ window: TimeWindow | None = None
1237
1320
  # read through a project observer — only beliefs that are global
1238
1321
  # or were observed in this project are candidates. ``None`` (the default) is
1239
1322
  # the store-wide view, for which the read is byte-identical to before. The
@@ -1310,6 +1393,18 @@ class QueryRequest(BaseModel):
1310
1393
  or self.list_vocabulary
1311
1394
  or self.is_aggregate
1312
1395
  or (self.question is None and self.has_claim_filters)
1396
+ or self.is_window_listing
1397
+ )
1398
+
1399
+ @property
1400
+ def is_window_listing(self) -> bool:
1401
+ """True for the window listing: a window, no question, no claim filter
1402
+ or aggregate. Ordered by ``valid_from``, no LLM call."""
1403
+ return (
1404
+ self.window is not None
1405
+ and self.question is None
1406
+ and not self.has_claim_filters
1407
+ and not self.is_aggregate
1313
1408
  )
1314
1409
 
1315
1410
  @model_validator(mode="after")
@@ -1318,7 +1413,13 @@ class QueryRequest(BaseModel):
1318
1413
  raise ValueError(
1319
1414
  "a query needs a question, a structural claim filter "
1320
1415
  "(predicate / object_*), an aggregate (count / group_by), "
1321
- "list_predicates, or list_vocabulary"
1416
+ "a time window, list_predicates, or list_vocabulary "
1417
+ ""
1418
+ )
1419
+ if self.window is not None and (self.list_predicates or self.list_vocabulary):
1420
+ raise ValueError(
1421
+ "a time window selects claims; it does not combine with the "
1422
+ "predicate or vocabulary listings"
1322
1423
  )
1323
1424
  if self.is_aggregate and self.question is not None:
1324
1425
  raise ValueError(
@@ -1537,6 +1638,63 @@ class AsOfNote(BaseModel):
1537
1638
  successor: AsOfSuccessor | None = None
1538
1639
 
1539
1640
 
1641
+ class WindowReading(StrEnum):
1642
+ """Why a claim is in a world-time window.
1643
+
1644
+ ``BEGAN``: its ``valid_from`` lies in the window, whatever its kind; what
1645
+ "what happened in the week" asks. ``IN_FORCE``: a STATE that started
1646
+ before the window and had not closed. ``HELD_AT``: a STATE whose start is
1647
+ unknown but which is known to hold at its anchor (basis ``as-of``), read as
1648
+ in force from there. ``WITHIN``: a claim anchored to a coarse unit (an
1649
+ EVENT somewhere in a month or a year, or a STATE spanning one) whose unit
1650
+ intersects the window.
1651
+ """
1652
+
1653
+ BEGAN = "BEGAN"
1654
+ IN_FORCE = "IN_FORCE"
1655
+ HELD_AT = "HELD_AT"
1656
+ WITHIN = "WITHIN"
1657
+
1658
+
1659
+ class WindowHit(BaseModel):
1660
+ """How one hit sits in the requested world-time window (§4).
1661
+
1662
+ A **response model, not particle substrate**: computed at read time,
1663
+ never stored. ``closed_at`` and ``close_basis`` say when a STATE's
1664
+ interval ended and which rule said so (its own ``valid_until``, its
1665
+ successor's ``valid_from``, or its retirement instant); both None for an
1666
+ open interval and for an EVENT. ``retired`` carries the note for
1667
+ a retired claim admitted for the period it held. ``restates`` names the
1668
+ retired original this hit stood in for.
1669
+ """
1670
+
1671
+ reading: WindowReading
1672
+ valid_from: datetime
1673
+ # None for an unkinded claim, admitted under the ``BEGAN`` reading only.
1674
+ valid_kind: ValidKind | None = None
1675
+ closed_at: datetime | None = None
1676
+ close_basis: Literal["valid_until", "successor", "retired_at"] | None = None
1677
+ retired: AsOfNote | None = None
1678
+ restates: str | None = None
1679
+
1680
+
1681
+ class WindowCoverage(BaseModel):
1682
+ """What a window read could and could not judge.
1683
+
1684
+ Disclosure only. ``undated`` are candidates with no ``valid_from``;
1685
+ ``unkinded`` carry a date but no kind and so match the ``BEGAN`` reading
1686
+ only; ``retired_admitted`` is how many retired claims the window let in;
1687
+ ``restated`` how many of those yielded to a successor in the set.
1688
+ """
1689
+
1690
+ candidates: int
1691
+ admitted: int
1692
+ undated: int
1693
+ unkinded: int
1694
+ retired_admitted: int
1695
+ restated: int
1696
+
1697
+
1540
1698
  class RelevanceNote(BaseModel):
1541
1699
  """The question-level relevance disclosure for a semantic query.
1542
1700
 
@@ -1873,6 +2031,16 @@ class QueryResponse(BaseModel):
1873
2031
  # reconstructible (rung 4). Born-retired rows are never counted. Always 0
1874
2032
  # on a normal query.
1875
2033
  as_of_excluded_undatable: int = 0
2034
+ # echo of the request's world-time window; None on a read with
2035
+ # no window.
2036
+ window: TimeWindow | None = None
2037
+ # per-result window readings, query-time only. Parallel to
2038
+ # ``particles`` (``window_hits[i]`` annotates ``particles[i]``). Empty
2039
+ # unless the request set ``window``.
2040
+ window_hits: list[WindowHit | None] = Field(default_factory=list)
2041
+ # what the window could and could not judge. None on a read
2042
+ # with no window. Disclosure only.
2043
+ window_coverage: WindowCoverage | None = None
1876
2044
  # set when the request named an ``observer_project``; None on a
1877
2045
  # store-wide read. Disclosure only.
1878
2046
  observer_scope: ObserverScopeNote | None = None