activegraph 1.0.4__tar.gz → 1.0.5__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 (159) hide show
  1. {activegraph-1.0.4 → activegraph-1.0.5}/PKG-INFO +9 -1
  2. {activegraph-1.0.4 → activegraph-1.0.5}/README.md +7 -0
  3. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/__init__.py +1 -1
  4. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/PKG-INFO +9 -1
  5. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/SOURCES.txt +1 -0
  6. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/requires.txt +1 -0
  7. {activegraph-1.0.4 → activegraph-1.0.5}/pyproject.toml +7 -1
  8. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_doc_links.py +20 -0
  9. activegraph-1.0.5/tests/test_llms_txt.py +184 -0
  10. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/__main__.py +0 -0
  11. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/behaviors/__init__.py +0 -0
  12. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/behaviors/base.py +0 -0
  13. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/behaviors/decorators.py +0 -0
  14. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/cli/__init__.py +0 -0
  15. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/cli/main.py +0 -0
  16. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/cli/quickstart.py +0 -0
  17. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/__init__.py +0 -0
  18. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/clock.py +0 -0
  19. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/event.py +0 -0
  20. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/graph.py +0 -0
  21. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/ids.py +0 -0
  22. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/patch.py +0 -0
  23. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/core/view.py +0 -0
  24. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/errors.py +0 -0
  25. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/frame.py +0 -0
  26. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/__init__.py +0 -0
  27. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/anthropic.py +0 -0
  28. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/cache.py +0 -0
  29. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/errors.py +0 -0
  30. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/openai.py +0 -0
  31. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/parsing.py +0 -0
  32. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/prompt.py +0 -0
  33. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/provider.py +0 -0
  34. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/recorded.py +0 -0
  35. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/llm/types.py +0 -0
  36. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/__init__.py +0 -0
  37. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/logging.py +0 -0
  38. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/metrics.py +0 -0
  39. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/migration.py +0 -0
  40. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/prometheus.py +0 -0
  41. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/observability/status.py +0 -0
  42. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/__init__.py +0 -0
  43. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/__init__.py +0 -0
  44. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/behaviors.py +0 -0
  45. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/fixtures/__init__.py +0 -0
  46. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/fixtures/companies.py +0 -0
  47. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/object_types.py +0 -0
  48. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/prompts/document_researcher.md +0 -0
  49. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/prompts/memo_synthesizer.md +0 -0
  50. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/prompts/question_generator.md +0 -0
  51. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/prompts/risk_identifier.md +0 -0
  52. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/settings.py +0 -0
  53. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/diligence/tools.py +0 -0
  54. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/loader.py +0 -0
  55. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/packs/scaffold.py +0 -0
  56. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/policy.py +0 -0
  57. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/__init__.py +0 -0
  58. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/_live.py +0 -0
  59. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/behavior_graph.py +0 -0
  60. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/budget.py +0 -0
  61. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/config_errors.py +0 -0
  62. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/diff.py +0 -0
  63. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/errors.py +0 -0
  64. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/exec_errors.py +0 -0
  65. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/patterns.py +0 -0
  66. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/queue.py +0 -0
  67. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/registration_errors.py +0 -0
  68. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/registry.py +0 -0
  69. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/runtime.py +0 -0
  70. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/scheduler.py +0 -0
  71. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/runtime/view_builder.py +0 -0
  72. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/__init__.py +0 -0
  73. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/base.py +0 -0
  74. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/conformance.py +0 -0
  75. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/errors.py +0 -0
  76. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/memory.py +0 -0
  77. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/postgres.py +0 -0
  78. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/serde.py +0 -0
  79. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/sqlite.py +0 -0
  80. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/store/url.py +0 -0
  81. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/__init__.py +0 -0
  82. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/base.py +0 -0
  83. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/cache.py +0 -0
  84. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/context.py +0 -0
  85. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/decorators.py +0 -0
  86. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/errors.py +0 -0
  87. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/graph_query.py +0 -0
  88. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/recorded.py +0 -0
  89. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/tools/web_fetch.py +0 -0
  90. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/trace/__init__.py +0 -0
  91. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/trace/causal.py +0 -0
  92. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph/trace/printer.py +0 -0
  93. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/dependency_links.txt +0 -0
  94. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/entry_points.txt +0 -0
  95. {activegraph-1.0.4 → activegraph-1.0.5}/activegraph.egg-info/top_level.txt +0 -0
  96. {activegraph-1.0.4 → activegraph-1.0.5}/setup.cfg +0 -0
  97. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_activate_after.py +0 -0
  98. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_causal_cross_tool.py +0 -0
  99. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_cli.py +0 -0
  100. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_clock.py +0 -0
  101. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_diff.py +0 -0
  102. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_diligence_pack.py +0 -0
  103. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_diligence_with_tools.py +0 -0
  104. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_doc_site_reachable.py +0 -0
  105. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_errors_format.py +0 -0
  106. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_event.py +0 -0
  107. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_fork.py +0 -0
  108. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_graph.py +0 -0
  109. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_ids.py +0 -0
  110. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_anthropic.py +0 -0
  111. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_behavior.py +0 -0
  112. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_budget.py +0 -0
  113. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_causal.py +0 -0
  114. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_claim_extraction.py +0 -0
  115. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_default_model.py +0 -0
  116. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_determinism.py +0 -0
  117. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_failure.py +0 -0
  118. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_openai.py +0 -0
  119. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_prompt.py +0 -0
  120. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_provider_fixtures.py +0 -0
  121. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_replay.py +0 -0
  122. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_tool_loop.py +0 -0
  123. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_trace.py +0 -0
  124. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_trace_snapshot.py +0 -0
  125. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_llm_types.py +0 -0
  126. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_migration.py +0 -0
  127. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_observability_logging.py +0 -0
  128. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_observability_metrics.py +0 -0
  129. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_operate_example.py +0 -0
  130. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_pack_scaffold.py +0 -0
  131. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_packs.py +0 -0
  132. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_patch.py +0 -0
  133. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_pattern_matcher.py +0 -0
  134. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_pattern_parser.py +0 -0
  135. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_pattern_subscriptions.py +0 -0
  136. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_persistence.py +0 -0
  137. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_postgres_store.py +0 -0
  138. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_quickstart.py +0 -0
  139. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_quickstart_snapshot.py +0 -0
  140. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_replay.py +0 -0
  141. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_replay_trace_snapshot.py +0 -0
  142. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_requeue_unfired.py +0 -0
  143. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_resume_example.py +0 -0
  144. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_runtime.py +0 -0
  145. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_runtime_status.py +0 -0
  146. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_serde.py +0 -0
  147. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_store_conformance.py +0 -0
  148. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_store_url.py +0 -0
  149. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_tool_replay.py +0 -0
  150. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_tool_trace_snapshot.py +0 -0
  151. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_tools.py +0 -0
  152. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_trace.py +0 -0
  153. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_tutorial_snippets.py +0 -0
  154. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_v1_0_1_patches.py +0 -0
  155. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_v1_0_3_behavior_failed_ux.py +0 -0
  156. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_v1_0_3_tool_multiturn.py +0 -0
  157. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_version_sync.py +0 -0
  158. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_view.py +0 -0
  159. {activegraph-1.0.4 → activegraph-1.0.5}/tests/test_wheel_completeness.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: activegraph
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: An event-sourced reactive graph runtime for long-running, auditable, agentic systems.
5
5
  Author: Active Graph contributors
6
6
  License: MIT
@@ -49,6 +49,7 @@ Provides-Extra: docs
49
49
  Requires-Dist: mkdocs>=1.6; extra == "docs"
50
50
  Requires-Dist: mkdocs-material>=9.5; extra == "docs"
51
51
  Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
52
+ Requires-Dist: mkdocs-llmstxt>=0.2; extra == "docs"
52
53
 
53
54
  # Active Graph
54
55
 
@@ -219,6 +220,13 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
219
220
  per-error catalog.
220
221
  - **[10-minute tutorial](https://docs.activegraph.ai/quickstart/)** — install
221
222
  to a working custom behavior, including fork-and-diff.
223
+ - **AI coding assistants** — the docs are machine-readable at
224
+ [docs.activegraph.ai/llms.txt](https://docs.activegraph.ai/llms.txt)
225
+ (structured index) and
226
+ [docs.activegraph.ai/llms-full.txt](https://docs.activegraph.ai/llms-full.txt)
227
+ (concatenated full content), generated from the same source markdown
228
+ as the rendered site. Built for AI agents evaluating the framework
229
+ via Claude Code, Cursor, Replit, and similar tooling.
222
230
  - **[CHANGELOG.md](CHANGELOG.md)** — every release, with per-version
223
231
  migration notes.
224
232
  - **[CONTRACT.md](CONTRACT.md)** — locked design decisions, version
@@ -167,6 +167,13 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
167
167
  per-error catalog.
168
168
  - **[10-minute tutorial](https://docs.activegraph.ai/quickstart/)** — install
169
169
  to a working custom behavior, including fork-and-diff.
170
+ - **AI coding assistants** — the docs are machine-readable at
171
+ [docs.activegraph.ai/llms.txt](https://docs.activegraph.ai/llms.txt)
172
+ (structured index) and
173
+ [docs.activegraph.ai/llms-full.txt](https://docs.activegraph.ai/llms-full.txt)
174
+ (concatenated full content), generated from the same source markdown
175
+ as the rendered site. Built for AI agents evaluating the framework
176
+ via Claude Code, Cursor, Replit, and similar tooling.
170
177
  - **[CHANGELOG.md](CHANGELOG.md)** — every release, with per-version
171
178
  migration notes.
172
179
  - **[CONTRACT.md](CONTRACT.md)** — locked design decisions, version
@@ -222,4 +222,4 @@ __all__ = [
222
222
  "tool",
223
223
  ]
224
224
 
225
- __version__ = "1.0.4"
225
+ __version__ = "1.0.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: activegraph
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: An event-sourced reactive graph runtime for long-running, auditable, agentic systems.
5
5
  Author: Active Graph contributors
6
6
  License: MIT
@@ -49,6 +49,7 @@ Provides-Extra: docs
49
49
  Requires-Dist: mkdocs>=1.6; extra == "docs"
50
50
  Requires-Dist: mkdocs-material>=9.5; extra == "docs"
51
51
  Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
52
+ Requires-Dist: mkdocs-llmstxt>=0.2; extra == "docs"
52
53
 
53
54
  # Active Graph
54
55
 
@@ -219,6 +220,13 @@ in [`docs/concepts/relations.md`](https://docs.activegraph.ai/concepts/relations
219
220
  per-error catalog.
220
221
  - **[10-minute tutorial](https://docs.activegraph.ai/quickstart/)** — install
221
222
  to a working custom behavior, including fork-and-diff.
223
+ - **AI coding assistants** — the docs are machine-readable at
224
+ [docs.activegraph.ai/llms.txt](https://docs.activegraph.ai/llms.txt)
225
+ (structured index) and
226
+ [docs.activegraph.ai/llms-full.txt](https://docs.activegraph.ai/llms-full.txt)
227
+ (concatenated full content), generated from the same source markdown
228
+ as the rendered site. Built for AI agents evaluating the framework
229
+ via Claude Code, Cursor, Replit, and similar tooling.
222
230
  - **[CHANGELOG.md](CHANGELOG.md)** — every release, with per-version
223
231
  migration notes.
224
232
  - **[CONTRACT.md](CONTRACT.md)** — locked design decisions, version
@@ -120,6 +120,7 @@ tests/test_llm_tool_loop.py
120
120
  tests/test_llm_trace.py
121
121
  tests/test_llm_trace_snapshot.py
122
122
  tests/test_llm_types.py
123
+ tests/test_llms_txt.py
123
124
  tests/test_migration.py
124
125
  tests/test_observability_logging.py
125
126
  tests/test_observability_metrics.py
@@ -23,6 +23,7 @@ psycopg[binary]<4,>=3.1
23
23
  mkdocs>=1.6
24
24
  mkdocs-material>=9.5
25
25
  mkdocstrings[python]>=0.24
26
+ mkdocs-llmstxt>=0.2
26
27
 
27
28
  [llm]
28
29
  anthropic>=0.40
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "activegraph"
7
- version = "1.0.4"
7
+ version = "1.0.5"
8
8
  description = "An event-sourced reactive graph runtime for long-running, auditable, agentic systems."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -65,6 +65,12 @@ docs = [
65
65
  "mkdocs>=1.6",
66
66
  "mkdocs-material>=9.5",
67
67
  "mkdocstrings[python]>=0.24",
68
+ # v1.0.5 #1: generates /llms.txt and /llms-full.txt at the site
69
+ # root so AI coding assistants fetching docs.activegraph.ai get
70
+ # clean markdown instead of mkdocs-rendered HTML. Same maintainer
71
+ # as mkdocstrings; native dual-output via the `full_output:` config
72
+ # key. Surfaced by the v1.0.4 external user-test.
73
+ "mkdocs-llmstxt>=0.2",
68
74
  ]
69
75
 
70
76
  [project.scripts]
@@ -66,6 +66,22 @@ _DOCS_URL_RE = re.compile(
66
66
  )
67
67
 
68
68
 
69
+ # URLs that are generated at build time and do NOT correspond to a
70
+ # `docs/*.md` source file. These bypass the source-presence check
71
+ # because their existence is verified by a dedicated build-time gate
72
+ # rather than by source-tree presence.
73
+ #
74
+ # `/llms.txt` and `/llms-full.txt` (CONTRACT v1.0.5 #1) are emitted by
75
+ # the `mkdocs-llmstxt` plugin during `mkdocs build`; their existence
76
+ # and well-formedness are verified by `tests/test_llms_txt.py`. Any
77
+ # URL added here must have a paired build-time test asserting the
78
+ # file lands at the site root.
79
+ _BUILD_GENERATED_URLS = frozenset({
80
+ "/llms.txt",
81
+ "/llms-full.txt",
82
+ })
83
+
84
+
69
85
  def _strip_trailing_punct(s: str) -> str:
70
86
  """URLs in prose often end at sentence boundaries; trim trailing
71
87
  punctuation that isn't part of the URL."""
@@ -181,6 +197,10 @@ def test_every_referenced_docs_url_has_a_source_page() -> None:
181
197
  missing: dict[Path, list[tuple[str, list[Path]]]] = {}
182
198
  unmapped: list[tuple[str, list[Path]]] = []
183
199
  for url_path, sources in referenced.items():
200
+ if url_path in _BUILD_GENERATED_URLS:
201
+ # Existence guaranteed by tests/test_llms_txt.py at build
202
+ # time; no docs/*.md source to check.
203
+ continue
184
204
  expected = _url_to_source_path(url_path)
185
205
  if expected is None:
186
206
  unmapped.append((url_path, sources))
@@ -0,0 +1,184 @@
1
+ """llms.txt / llms-full.txt generation gate. CONTRACT v1.0.5 #1.
2
+
3
+ Builds the mkdocs site into a temp directory and asserts that
4
+ ``site/llms.txt`` and ``site/llms-full.txt`` exist after the build,
5
+ are well-formed (H1, blockquote summary, at least one H2 section),
6
+ and reference the nav-anchor pages named in CONTRACT v1.0.5 #1's
7
+ contract claim. The "content coverage" assertion is the boundary
8
+ guard against the failure mode where the build succeeds but emits
9
+ an llms.txt that contains only the H1 — file existence alone is
10
+ not sufficient.
11
+
12
+ Standing Rule §2 shape: the test anchors on the contract boundary
13
+ the v1.0.5 #1 amendment names ("both files exist after build and
14
+ are well-formed"), not on the implementation path of the
15
+ `mkdocs-llmstxt` plugin. If a future release swaps the plugin for
16
+ a custom build script or a different plugin, this test stays
17
+ correct without modification.
18
+
19
+ Marked ``slow`` because ``mkdocs build`` takes several seconds.
20
+ Local ``pytest`` skips it by default; the ``.github/workflows/docs.yml``
21
+ workflow runs it explicitly via ``pytest -m slow tests/test_llms_txt.py``.
22
+ The precedent is ``tests/test_doc_site_reachable.py`` (CONTRACT v1.1
23
+ #9), which uses the same marker for the same reason.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import shutil
29
+ import subprocess
30
+ from pathlib import Path
31
+
32
+ import pytest
33
+
34
+
35
+ REPO_ROOT = Path(__file__).resolve().parent.parent
36
+ MKDOCS_CONFIG = REPO_ROOT / "mkdocs.yml"
37
+
38
+
39
+ @pytest.fixture(scope="module")
40
+ def built_site(tmp_path_factory: pytest.TempPathFactory) -> Path:
41
+ """Run ``mkdocs build`` into a temp dir; return the site/ path.
42
+
43
+ Module-scoped: the build is shared across every assertion in
44
+ this file. The fixture skips the entire module if ``mkdocs`` is
45
+ not installed — keeps the gate honest about its hard dependency
46
+ on the docs-build environment without polluting unrelated test
47
+ runs.
48
+ """
49
+ if shutil.which("mkdocs") is None:
50
+ pytest.skip("mkdocs binary not available; install '.[docs]' to run")
51
+
52
+ site_dir = tmp_path_factory.mktemp("llms_txt_gate") / "site"
53
+ result = subprocess.run(
54
+ [
55
+ "mkdocs",
56
+ "build",
57
+ "--config-file",
58
+ str(MKDOCS_CONFIG),
59
+ "--site-dir",
60
+ str(site_dir),
61
+ "--clean",
62
+ ],
63
+ capture_output=True,
64
+ text=True,
65
+ cwd=REPO_ROOT,
66
+ )
67
+ if result.returncode != 0:
68
+ # Surface the actual mkdocs error so a failing gate is
69
+ # diagnosable from the CI log — not just "build failed."
70
+ pytest.fail(
71
+ "mkdocs build failed:\n"
72
+ f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
73
+ )
74
+ return site_dir
75
+
76
+
77
+ @pytest.mark.slow
78
+ def test_llms_txt_exists(built_site: Path) -> None:
79
+ """Contract: ``/llms.txt`` is generated at site root after build."""
80
+ llms = built_site / "llms.txt"
81
+ assert llms.is_file(), (
82
+ f"site/llms.txt missing after mkdocs build (expected at {llms}). "
83
+ "Check the `llmstxt` plugin block in mkdocs.yml."
84
+ )
85
+
86
+
87
+ @pytest.mark.slow
88
+ def test_llms_full_txt_exists(built_site: Path) -> None:
89
+ """Contract: ``/llms-full.txt`` is generated at site root after build."""
90
+ llms_full = built_site / "llms-full.txt"
91
+ assert llms_full.is_file(), (
92
+ f"site/llms-full.txt missing after mkdocs build (expected at {llms_full}). "
93
+ "Check the `full_output:` key in the `llmstxt` plugin block."
94
+ )
95
+
96
+
97
+ @pytest.mark.slow
98
+ def test_llms_txt_h1_and_blockquote(built_site: Path) -> None:
99
+ """Contract: ``llms.txt`` starts with the H1 and a blockquote summary.
100
+
101
+ The plugin derives the H1 from ``site_name`` and the blockquote
102
+ from ``site_description``. The assertion shape catches both the
103
+ plugin-not-running case and the case where ``mkdocs.yml`` loses
104
+ one of those keys (which would break the llms.txt convention
105
+ described at llmstxt.org).
106
+ """
107
+ content = (built_site / "llms.txt").read_text(encoding="utf-8")
108
+ lines = content.splitlines()
109
+
110
+ assert lines[0] == "# Active Graph", (
111
+ f"llms.txt does not start with the expected H1; got: {lines[0]!r}"
112
+ )
113
+ # The blockquote summary line — first non-empty line after the H1.
114
+ blockquote = next((ln for ln in lines[1:] if ln.strip()), "")
115
+ assert blockquote.startswith("> "), (
116
+ f"llms.txt does not carry a blockquote summary; got: {blockquote!r}"
117
+ )
118
+ assert len(blockquote) > len("> "), (
119
+ "llms.txt blockquote summary is empty after the '> ' marker"
120
+ )
121
+
122
+
123
+ @pytest.mark.slow
124
+ def test_llms_txt_has_section_headers(built_site: Path) -> None:
125
+ """Contract: ``llms.txt`` carries H2 sections (navigation aid shape)."""
126
+ content = (built_site / "llms.txt").read_text(encoding="utf-8")
127
+ h2_count = sum(1 for ln in content.splitlines() if ln.startswith("## "))
128
+ assert h2_count >= 4, (
129
+ f"llms.txt has {h2_count} H2 section headers; expected at least 4 "
130
+ "(Quickstart / Concepts / Cookbook / Reference at minimum). "
131
+ "A file with the H1 but no sections is not useful as a nav aid."
132
+ )
133
+
134
+
135
+ @pytest.mark.slow
136
+ def test_llms_txt_references_nav_anchor_pages(built_site: Path) -> None:
137
+ """Contract: ``llms.txt`` references the nav-anchor pages by name.
138
+
139
+ The v1.0.5 #1 amendment names three anchors for the coverage
140
+ check: ``concepts/graph.md``, ``quickstart.md``, and at least
141
+ one cookbook page. These are the load-bearing entry points for
142
+ a first-time AI reader walking the index; if any one is
143
+ missing, the file fails to deliver on its navigation-aid
144
+ purpose.
145
+ """
146
+ content = (built_site / "llms.txt").read_text(encoding="utf-8")
147
+
148
+ assert "concepts/graph/" in content, (
149
+ "llms.txt does not reference docs/concepts/graph.md — the "
150
+ "primary entry point for the framework's primitive model."
151
+ )
152
+ assert "quickstart/" in content, (
153
+ "llms.txt does not reference docs/quickstart.md — the "
154
+ "ten-minute tutorial that the README points new users at."
155
+ )
156
+ assert "cookbook/" in content, (
157
+ "llms.txt does not reference any cookbook page — at least "
158
+ "one of common-patterns.md / debugging.md / multi-run-scripts.md "
159
+ "is expected."
160
+ )
161
+
162
+
163
+ @pytest.mark.slow
164
+ def test_llms_full_txt_well_formed(built_site: Path) -> None:
165
+ """Contract: ``llms-full.txt`` carries the H1 + actual page bodies.
166
+
167
+ Asserts on a distinctive prose marker from ``docs/quickstart.md``
168
+ so a file that contains the H1 and blockquote but no concatenated
169
+ bodies fails. The marker is the tutorial's opening sentence,
170
+ voice-locked since v1.0-rc1 and unlikely to churn.
171
+ """
172
+ content = (built_site / "llms-full.txt").read_text(encoding="utf-8")
173
+
174
+ assert content.startswith("# Active Graph"), (
175
+ "llms-full.txt does not start with the expected H1"
176
+ )
177
+ # Distinctive phrase from docs/quickstart.md's first paragraph —
178
+ # if llms-full.txt contains it, the quickstart body made it
179
+ # into the concatenated output.
180
+ assert "Ten minutes from install to a working custom behavior" in content, (
181
+ "llms-full.txt does not contain the quickstart body marker; "
182
+ "the file may be the index-only output instead of the "
183
+ "concatenated full content."
184
+ )
File without changes