agentfluent 0.9.0__tar.gz → 0.11.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 (393) hide show
  1. agentfluent-0.11.0/.claude/claude-security-guidance.md +69 -0
  2. agentfluent-0.11.0/.claude/hooks/guard_append_only.py +173 -0
  3. agentfluent-0.11.0/.claude/loop.config.md +119 -0
  4. agentfluent-0.11.0/.claude/security-patterns.json +40 -0
  5. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/settings.json +17 -0
  6. agentfluent-0.11.0/.claude/skills/release-loop/SKILL.md +44 -0
  7. agentfluent-0.11.0/.claude/skills/release-loop/loop-engine.md +592 -0
  8. agentfluent-0.11.0/.claude/specs/agent-sdk-session-format-findings.md +312 -0
  9. agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analysis.md +164 -0
  10. agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analyze.json +127576 -0
  11. agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analyze.table.txt +2823 -0
  12. agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analysis.md +165 -0
  13. agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analyze.json +155136 -0
  14. agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analyze.table.txt +3022 -0
  15. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/decisions.md +315 -0
  16. agentfluent-0.11.0/.claude/specs/prd-agent-sdk-discovery.md +135 -0
  17. agentfluent-0.11.0/.claude/specs/prd-continuous-eval.md +174 -0
  18. agentfluent-0.11.0/.claude/specs/prd-loop-engineering.md +295 -0
  19. agentfluent-0.11.0/.claude/specs/prd-loop-plugin.md +153 -0
  20. agentfluent-0.11.0/.claude/specs/prd-pricing-genai-migration.md +151 -0
  21. agentfluent-0.11.0/.claude/specs/prd-v0.10.md +227 -0
  22. agentfluent-0.11.0/.claude/specs/prd-v0.11.md +202 -0
  23. agentfluent-0.11.0/.claude/specs/value-review-437-verbosity.md +182 -0
  24. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/ci.yml +1 -1
  25. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/release-please.yml +1 -1
  26. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/security-review.yml +9 -2
  27. {agentfluent-0.9.0 → agentfluent-0.11.0}/.gitignore +10 -0
  28. agentfluent-0.11.0/.release-please-manifest.json +3 -0
  29. {agentfluent-0.9.0 → agentfluent-0.11.0}/CHANGELOG.md +54 -0
  30. {agentfluent-0.9.0 → agentfluent-0.11.0}/CLAUDE.md +44 -7
  31. {agentfluent-0.9.0 → agentfluent-0.11.0}/CONTRIBUTING.md +18 -0
  32. {agentfluent-0.9.0 → agentfluent-0.11.0}/PKG-INFO +38 -30
  33. {agentfluent-0.9.0 → agentfluent-0.11.0}/README.md +36 -29
  34. agentfluent-0.11.0/docs/COST_MODEL.md +74 -0
  35. agentfluent-0.11.0/docs/EFFICIENCY_DIAGNOSTICS_RESEARCH.md +130 -0
  36. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/GLOSSARY.md +170 -3
  37. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/ROADMAP.md +42 -7
  38. agentfluent-0.11.0/docs/TOKEN_ACCOUNTING.md +725 -0
  39. agentfluent-0.11.0/images/demo-analyze.svg +462 -0
  40. agentfluent-0.11.0/images/demo-diagnostics.svg +428 -0
  41. agentfluent-0.11.0/images/demo-diff.svg +474 -0
  42. agentfluent-0.11.0/images/demo-subagents.svg +167 -0
  43. {agentfluent-0.9.0 → agentfluent-0.11.0}/pyproject.toml +8 -1
  44. agentfluent-0.11.0/research/agent-sdk-probe/FINDINGS.md +607 -0
  45. agentfluent-0.11.0/research/agent-sdk-probe/README.md +107 -0
  46. agentfluent-0.11.0/research/agent-sdk-probe/agent.py +269 -0
  47. agentfluent-0.11.0/research/agent-sdk-probe/fixture.txt +7 -0
  48. agentfluent-0.11.0/research/agent-sdk-probe/probe.py +38 -0
  49. agentfluent-0.11.0/research/agent-sdk-probe/run_matrix.py +208 -0
  50. agentfluent-0.11.0/research/agent-sdk-probe/sampledata/code.py +7 -0
  51. agentfluent-0.11.0/research/agent-sdk-probe/sampledata/data.csv +6 -0
  52. agentfluent-0.11.0/research/agent-sdk-probe/sampledata/notes.txt +12 -0
  53. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/extractor.py +3 -0
  54. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/models.py +29 -0
  55. agentfluent-0.11.0/src/agentfluent/analytics/_genai_source.py +97 -0
  56. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/pipeline.py +53 -3
  57. agentfluent-0.11.0/src/agentfluent/analytics/pricing.py +254 -0
  58. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/tokens.py +72 -17
  59. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/analyze.py +5 -1
  60. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/helpers.py +42 -0
  61. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/table.py +63 -2
  62. agentfluent-0.11.0/src/agentfluent/config/hook_inspector.py +142 -0
  63. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/models.py +21 -0
  64. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/parser.py +37 -1
  65. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/paths.py +17 -0
  66. agentfluent-0.11.0/src/agentfluent/core/session.py +369 -0
  67. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_complexity.py +73 -5
  68. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/correlator.py +219 -33
  69. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/delegation.py +5 -5
  70. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/model_routing.py +200 -56
  71. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/models.py +9 -0
  72. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/parent_workload.py +2 -1
  73. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/pipeline.py +38 -2
  74. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/signals.py +71 -11
  75. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/trace_signals.py +37 -39
  76. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/loader.py +32 -0
  77. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/terms.yaml +159 -3
  78. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/parser.py +9 -7
  79. agentfluent-0.11.0/src/agentfluent/traces/sidecar.py +125 -0
  80. agentfluent-0.11.0/tests/fixtures/hooks/inline_bash_timing.yaml +15 -0
  81. agentfluent-0.11.0/tests/fixtures/hooks/no_post_hook.yaml +9 -0
  82. agentfluent-0.11.0/tests/fixtures/hooks/secrets_hook.py +18 -0
  83. agentfluent-0.11.0/tests/fixtures/hooks/timing_hook.py +15 -0
  84. agentfluent-0.11.0/tests/fixtures/nested_session/README.md +54 -0
  85. agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-leaf0001.jsonl +4 -0
  86. agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-leaf0001.meta.json +1 -0
  87. agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-worker001.jsonl +6 -0
  88. agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-worker001.meta.json +1 -0
  89. agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1.jsonl +4 -0
  90. agentfluent-0.11.0/tests/fixtures/sdk_session/README.md +70 -0
  91. agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1/subagents/agent-child0000001.jsonl +6 -0
  92. agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1/subagents/agent-child0000001.meta.json +5 -0
  93. agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1.jsonl +4 -0
  94. agentfluent-0.11.0/tests/fixtures/session_cli_entrypoint.jsonl +2 -0
  95. agentfluent-0.11.0/tests/unit/cli/test_session_kind_indicator.py +186 -0
  96. agentfluent-0.11.0/tests/unit/test_append_only_guard.py +267 -0
  97. agentfluent-0.11.0/tests/unit/test_complexity.py +51 -0
  98. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_correlator.py +239 -13
  99. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_dedup.py +23 -0
  100. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_diagnostics_pipeline.py +143 -1
  101. agentfluent-0.11.0/tests/unit/test_entrypoint_classification.py +218 -0
  102. agentfluent-0.11.0/tests/unit/test_genai_source.py +105 -0
  103. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_loader.py +35 -0
  104. agentfluent-0.11.0/tests/unit/test_hook_inspector.py +137 -0
  105. agentfluent-0.11.0/tests/unit/test_loop_skill_drift.py +155 -0
  106. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_model_routing.py +90 -0
  107. agentfluent-0.11.0/tests/unit/test_model_routing_sdk_main.py +282 -0
  108. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_cost.py +18 -2
  109. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parser.py +177 -0
  110. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_paths.py +16 -0
  111. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_pipeline.py +33 -0
  112. agentfluent-0.11.0/tests/unit/test_pricing.py +550 -0
  113. agentfluent-0.11.0/tests/unit/test_sdk_session_fixture.py +157 -0
  114. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_session_models.py +54 -0
  115. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_signals.py +100 -1
  116. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tokens.py +138 -3
  117. agentfluent-0.11.0/tests/unit/test_totaltokens_semantics.py +191 -0
  118. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_trace_signals.py +111 -5
  119. agentfluent-0.11.0/tests/unit/test_traces_nested_fixture.py +110 -0
  120. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_parser.py +141 -0
  121. agentfluent-0.11.0/tests/unit/test_traces_sidecar.py +186 -0
  122. agentfluent-0.11.0/tests/unit/tools/__init__.py +0 -0
  123. agentfluent-0.11.0/tests/unit/tools/test_dogfood_cli_runner.py +338 -0
  124. agentfluent-0.11.0/tests/unit/tools/test_dogfood_model_rotation.py +95 -0
  125. agentfluent-0.11.0/tests/unit/tools/test_dogfood_paths.py +126 -0
  126. agentfluent-0.11.0/tests/unit/tools/test_dogfood_runner_main.py +56 -0
  127. agentfluent-0.11.0/tools/__init__.py +6 -0
  128. agentfluent-0.11.0/tools/dogfood_runner/README.md +175 -0
  129. agentfluent-0.11.0/tools/dogfood_runner/__init__.py +11 -0
  130. agentfluent-0.11.0/tools/dogfood_runner/cli_runner.py +426 -0
  131. agentfluent-0.11.0/tools/dogfood_runner/install-cron.sh +125 -0
  132. agentfluent-0.11.0/tools/dogfood_runner/paths.py +168 -0
  133. agentfluent-0.11.0/tools/dogfood_runner/runner.py +270 -0
  134. {agentfluent-0.9.0 → agentfluent-0.11.0}/uv.lock +277 -5
  135. agentfluent-0.9.0/.github/workflows/claude-review.yml +0 -65
  136. agentfluent-0.9.0/.release-please-manifest.json +0 -3
  137. agentfluent-0.9.0/images/demo-analyze.svg +0 -426
  138. agentfluent-0.9.0/images/demo-diagnostics.svg +0 -445
  139. agentfluent-0.9.0/images/demo-diff.svg +0 -466
  140. agentfluent-0.9.0/images/demo-subagents.svg +0 -167
  141. agentfluent-0.9.0/src/agentfluent/analytics/pricing.py +0 -116
  142. agentfluent-0.9.0/src/agentfluent/core/session.py +0 -186
  143. agentfluent-0.9.0/tests/unit/test_pricing.py +0 -141
  144. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/agents/anthropic-research.md +0 -0
  145. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/agents/candidate-verifier.md +0 -0
  146. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/hooks/block_secret_reads.py +0 -0
  147. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/hooks/detect_secrets_in_output.py +0 -0
  148. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/skills/promote-candidates/SKILL.md +0 -0
  149. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/.gitkeep +0 -0
  150. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-04-29-agent-portfolio-analysis.md +0 -0
  151. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-05-17-v07-dogfood-analysis.md +0 -0
  152. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-05-30-v08-dogfood/analysis.md +0 -0
  153. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/findings.md +0 -0
  154. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/labels.tsv +0 -0
  155. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/sample.tsv +0 -0
  156. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/script.py +0 -0
  157. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/402-calibration/calibration.md +0 -0
  158. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/402-calibration/pairs.tsv +0 -0
  159. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/calibration.md +0 -0
  160. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/detections.tsv +0 -0
  161. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/export.py +0 -0
  162. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/sample.py +0 -0
  163. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/tune.py +0 -0
  164. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-mvp.md +0 -0
  165. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.3.md +0 -0
  166. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.5.md +0 -0
  167. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.6.md +0 -0
  168. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.7.md +0 -0
  169. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.8.md +0 -0
  170. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.9.md +0 -0
  171. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/plan-100-mcp-assessment.md +0 -0
  172. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/plan-116-mcp-extraction.md +0 -0
  173. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-advanced-tool-use-diagnostics.md +0 -0
  174. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-context-fork-loop-detection.md +0 -0
  175. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-date-range-filtering.md +0 -0
  176. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-glossary.md +0 -0
  177. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-mvp.md +0 -0
  178. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-quality-axis.md +0 -0
  179. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-research-pipeline-phase-3.md +0 -0
  180. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-tier3-github-enrichment.md +0 -0
  181. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-tool-name-normalization.md +0 -0
  182. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.3.md +0 -0
  183. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.5.md +0 -0
  184. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.6.md +0 -0
  185. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.7.md +0 -0
  186. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.8.md +0 -0
  187. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.9.md +0 -0
  188. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/research/anthropic-feature-watch.md +0 -0
  189. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/research-update-2026-04-15.md +0 -0
  190. {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/v0.4-scope-review.md +0 -0
  191. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  192. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  193. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  194. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/dependabot.yml +0 -0
  195. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/dependabot-auto-merge.yml +0 -0
  196. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/milestone-docs-issue.yml +0 -0
  197. {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/pr-template-check.yml +0 -0
  198. {agentfluent-0.9.0 → agentfluent-0.11.0}/.python-version +0 -0
  199. {agentfluent-0.9.0 → agentfluent-0.11.0}/LICENSE +0 -0
  200. {agentfluent-0.9.0 → agentfluent-0.11.0}/SECURITY.md +0 -0
  201. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/AGENT_ANALYTICS_RESEARCH.md +0 -0
  202. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/RAG_OVER_TOOLS_RESEARCH.md +0 -0
  203. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/SECURITY.md +0 -0
  204. {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/codefluent_cli_review_042526.md +0 -0
  205. {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-analyze.svg +0 -0
  206. {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-config-check.svg +0 -0
  207. {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-diagnostics.svg +0 -0
  208. {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-subagents.svg +0 -0
  209. {agentfluent-0.9.0 → agentfluent-0.11.0}/images/demo-config-check.svg +0 -0
  210. {agentfluent-0.9.0 → agentfluent-0.11.0}/release-please-config.json +0 -0
  211. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/README.md +0 -0
  212. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/build_notebook.py +0 -0
  213. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/quality_labels.json +0 -0
  214. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/threshold_validation.ipynb +0 -0
  215. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/generate_glossary_md.py +0 -0
  216. {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/generate_readme_screenshots.py +0 -0
  217. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/__init__.py +0 -0
  218. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/__init__.py +0 -0
  219. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/__init__.py +0 -0
  220. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/agent_metrics.py +0 -0
  221. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/tools.py +0 -0
  222. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/__init__.py +0 -0
  223. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/_time_args.py +0 -0
  224. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/__init__.py +0 -0
  225. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/config_check.py +0 -0
  226. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/diff_cmd.py +0 -0
  227. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/explain.py +0 -0
  228. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/list_cmd.py +0 -0
  229. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/report.py +0 -0
  230. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/report_renderers.py +0 -0
  231. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/exit_codes.py +0 -0
  232. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/__init__.py +0 -0
  233. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/diff_table.py +0 -0
  234. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/json_output.py +0 -0
  235. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/main.py +0 -0
  236. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/__init__.py +0 -0
  237. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/mcp_discovery.py +0 -0
  238. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/retention.py +0 -0
  239. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/scanner.py +0 -0
  240. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/scoring.py +0 -0
  241. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/__init__.py +0 -0
  242. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/discovery.py +0 -0
  243. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/filtering.py +0 -0
  244. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/timeutil.py +0 -0
  245. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/__init__.py +0 -0
  246. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_clustering.py +0 -0
  247. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_git_helpers.py +0 -0
  248. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/agent_audit.py +0 -0
  249. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/aggregation.py +0 -0
  250. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/builtin_actions.py +0 -0
  251. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/git_signals.py +0 -0
  252. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/github_signals.py +0 -0
  253. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/mcp_assessment.py +0 -0
  254. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/quality_signals.py +0 -0
  255. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/tool_orchestration.py +0 -0
  256. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/__init__.py +0 -0
  257. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/compute.py +0 -0
  258. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/loader.py +0 -0
  259. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/models.py +0 -0
  260. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/__init__.py +0 -0
  261. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/cache.py +0 -0
  262. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/client.py +0 -0
  263. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/consent.py +0 -0
  264. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/detection.py +0 -0
  265. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/models.py +0 -0
  266. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/__init__.py +0 -0
  267. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/models.py +0 -0
  268. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/render.py +0 -0
  269. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/py.typed +0 -0
  270. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/__init__.py +0 -0
  271. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/discovery.py +0 -0
  272. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/linker.py +0 -0
  273. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/models.py +0 -0
  274. {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/retry.py +0 -0
  275. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/__init__.py +0 -0
  276. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/_builders.py +0 -0
  277. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/conftest.py +0 -0
  278. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/.gitkeep +0 -0
  279. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/empty_prompt.md +0 -0
  280. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/no_frontmatter.md +0 -0
  281. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/no_tools.md +0 -0
  282. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/vague_description.md +0 -0
  283. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/well_configured.md +0 -0
  284. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/mcp/claude_user_only.json +0 -0
  285. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/mcp/claude_user_with_disabled.json +0 -0
  286. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/parent_workload_session.jsonl +0 -0
  287. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/report/analyze_snapshot.json +0 -0
  288. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/report/expected_report.md +0 -0
  289. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_basic.jsonl +0 -0
  290. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_block_per_line.jsonl +0 -0
  291. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_malformed.jsonl +0 -0
  292. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_skip_types.jsonl +0 -0
  293. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_streaming_dupes.jsonl +0 -0
  294. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_with_agent.jsonl +0 -0
  295. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_with_tool_calls.jsonl +0 -0
  296. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-basic.jsonl +0 -0
  297. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-empty.jsonl +0 -0
  298. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-errors.jsonl +0 -0
  299. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-large.jsonl +0 -0
  300. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-malformed.jsonl +0 -0
  301. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-retry.jsonl +0 -0
  302. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-streaming-dupes.jsonl +0 -0
  303. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-stuck.jsonl +0 -0
  304. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/__init__.py +0 -0
  305. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_agent_extraction.py +0 -0
  306. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_analytics.py +0 -0
  307. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_analyze_session_scope.py +0 -0
  308. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_config_assessment.py +0 -0
  309. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_diagnostics.py +0 -0
  310. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_git_signals_real.py +0 -0
  311. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_real_sessions.py +0 -0
  312. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_subagent_traces.py +0 -0
  313. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/__init__.py +0 -0
  314. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/__init__.py +0 -0
  315. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/_recommendation_helpers.py +0 -0
  316. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/conftest.py +0 -0
  317. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_active_duration_display.py +0 -0
  318. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_agent_duration_cell.py +0 -0
  319. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_github_flag.py +0 -0
  320. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_session_scope.py +0 -0
  321. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_since_until.py +0 -0
  322. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_axis_json_contract.py +0 -0
  323. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_axis_labels.py +0 -0
  324. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_claude_config_dir.py +0 -0
  325. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_cleanup_warning_cli.py +0 -0
  326. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_cost_labeling.py +0 -0
  327. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_deep_diagnostics_formatting.py +0 -0
  328. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diagnostics_default.py +0 -0
  329. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diagnostics_smoke.py +0 -0
  330. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_by_model_table.py +0 -0
  331. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_cmd.py +0 -0
  332. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_summary.py +0 -0
  333. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_table_signed.py +0 -0
  334. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_environment_warning_banner.py +0 -0
  335. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_exit_codes.py +0 -0
  336. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_explain.py +0 -0
  337. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_glossary_footer.py +0 -0
  338. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_help.py +0 -0
  339. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_json_alias.py +0 -0
  340. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_json_output.py +0 -0
  341. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_list_since_until.py +0 -0
  342. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_min_severity_filter.py +0 -0
  343. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_offload_candidates_formatting.py +0 -0
  344. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_output_modes.py +0 -0
  345. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_parse_warnings.py +0 -0
  346. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_recommendations_top_n.py +0 -0
  347. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_cmd.py +0 -0
  348. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_golden.py +0 -0
  349. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_pipeline.py +0 -0
  350. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_renderers.py +0 -0
  351. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_tier3_degraded_banner.py +0 -0
  352. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_time_args.py +0 -0
  353. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_verbose_signal_message.py +0 -0
  354. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/diff/__init__.py +0 -0
  355. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/diff/test_compute.py +0 -0
  356. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_audit.py +0 -0
  357. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_metrics.py +0 -0
  358. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_models.py +0 -0
  359. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_clustering.py +0 -0
  360. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_core_filtering.py +0 -0
  361. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_delegation.py +0 -0
  362. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_delegation_yaml_draft.py +0 -0
  363. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_diagnostics.py +0 -0
  364. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_discovery.py +0 -0
  365. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_extractor.py +0 -0
  366. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_git_signals.py +0 -0
  367. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_cache.py +0 -0
  368. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_client.py +0 -0
  369. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_consent.py +0 -0
  370. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_detection.py +0 -0
  371. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_signals_ci_failure.py +0 -0
  372. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_signals_density.py +0 -0
  373. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_drift.py +0 -0
  374. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_render.py +0 -0
  375. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_mcp_assessment.py +0 -0
  376. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_mcp_discovery.py +0 -0
  377. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_cluster.py +0 -0
  378. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_extract.py +0 -0
  379. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_quality_signals.py +0 -0
  380. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_recommendation_aggregation.py +0 -0
  381. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_retention.py +0 -0
  382. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_scanner.py +0 -0
  383. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_scoring.py +0 -0
  384. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_smoke.py +0 -0
  385. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_timeutil.py +0 -0
  386. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tool_inventory_audit.py +0 -0
  387. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tool_orchestration.py +0 -0
  388. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tools.py +0 -0
  389. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_discovery.py +0 -0
  390. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_fixtures.py +0 -0
  391. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_linker.py +0 -0
  392. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_models.py +0 -0
  393. {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_retry.py +0 -0
@@ -0,0 +1,69 @@
1
+ # Security Guidance — AgentFluent
2
+
3
+ Threat model and review checklist for the `security-guidance` plugin's
4
+ LLM-backed review layers (end-of-turn diff review and commit/push review).
5
+
6
+ > **Status:** Currently dormant. The LLM layers are disabled
7
+ > (`ENABLE_CODE_SECURITY_REVIEW=0` in `.claude/settings.json`), so only the
8
+ > free deterministic pattern layer runs today. This file activates the moment
9
+ > those layers are turned on — keep it in sync with the CI workflow's
10
+ > `custom-security-scan-instructions` in `.github/workflows/security-review.yml`
11
+ > so the in-session review and the PR-time review share one threat model.
12
+
13
+ ## What AgentFluent is
14
+
15
+ A local-first Python CLI that reads user session data and agent definitions
16
+ from `~/.claude/projects/` and `~/.claude/agents/`. It stores no credentials and
17
+ transmits no user data, and runs no web server, HTML rendering, or webview. Its
18
+ **only** network egress and subprocess use is the `gh` / `git` CLI (the Tier 3
19
+ GitHub-signals feature in `src/agentfluent/github/` and `diagnostics/`), invoked
20
+ list-form with `shell=False`. No in-process HTTP client
21
+ (`requests`/`httpx`/`urllib`/`socket`) is imported anywhere outside
22
+ `src/agentfluent/github/`.
23
+
24
+ ## Attack surfaces to focus on
25
+
26
+ 1. **JSONL parser** (`src/agentfluent/core/parser.py`) — reads arbitrary
27
+ user-controlled content. Check for unsafe deserialization, resource-exhaustion
28
+ on malformed/oversized input (unbounded reads, pathological nesting), and any
29
+ code path that evaluates or executes parsed strings.
30
+ 2. **Agent definition parser** (`src/agentfluent/config/scanner.py`) — parses
31
+ YAML frontmatter. Must use `yaml.safe_load` (never `yaml.load`) to prevent
32
+ arbitrary code execution via YAML tags.
33
+ 3. **Path handling in discovery** (`src/agentfluent/core/discovery.py`) —
34
+ iterates user directories. Check for path traversal, symlink following that
35
+ escapes the projects root, and TOCTOU races between stat and open.
36
+ 4. **CLI argument parsing** (`src/agentfluent/cli/`) — user strings from
37
+ `--project`, `--agent`, `--session` are matched against discovered data.
38
+ Ensure none reach a shell, `subprocess`, or any eval-like API.
39
+
40
+ ## Project-specific sensitivities
41
+
42
+ - **Secret hygiene in output.** AgentFluent reads session transcripts that may
43
+ contain credential-looking values. Flag any new code path that echoes raw tool
44
+ output, renders un-redacted file contents, or logs values matching
45
+ `KEY|TOKEN|SECRET|PASSWORD`. (Two enforcement hooks already guard secret reads
46
+ and output — see `docs/SECURITY.md`.)
47
+ - **Untrusted strings into rendering.** Sanitize user-controlled strings before
48
+ passing them to Rich/terminal formatting; never interpolate them into shell
49
+ commands.
50
+
51
+ ## Low priority (typically non-issues here)
52
+
53
+ No web server, no HTML rendering, no webview, no authentication surface. The only
54
+ sanctioned network egress is the `gh` / `git` CLI (`src/agentfluent/github/`,
55
+ `diagnostics/`); findings that assume an in-process HTTP or web attack surface
56
+ elsewhere are almost certainly false positives — note them but rank low.
57
+
58
+ ## Review checklist
59
+
60
+ - [ ] No `eval`, `exec`, `os.system`, `subprocess(..., shell=True)`, or
61
+ `compile()` on parsed/user-derived strings.
62
+ - [ ] YAML loaded only via `yaml.safe_load`; no `pickle.load` /
63
+ `pickle.loads` on file or session content.
64
+ - [ ] User-supplied paths are resolved and confined to their expected root
65
+ (no `..` escape, no symlink escape).
66
+ - [ ] Parser bounds memory/CPU on malformed input — no unbounded buffering.
67
+ - [ ] No credential-shaped value is logged, printed, or rendered un-redacted.
68
+ - [ ] New external calls (file I/O, any future network) are wrapped in
69
+ try/except with user-friendly, non-leaking error messages.
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook: protect append-only spec logs from entry-dropping writes.
3
+
4
+ Receives the PreToolUse event JSON on stdin. For a `Write` targeting a
5
+ registered append-only file (currently `.claude/specs/decisions.md`), it
6
+ compares the set of decision-entry IDs (`## Dxxx` headings) already on disk
7
+ against the set in the proposed `content`. If the write would drop any
8
+ existing entry, the call is denied.
9
+
10
+ Background: an append-only decision log was once clobbered by an agent whose
11
+ `Write` tool does full-file replacement -- a pm intending to append one
12
+ `Dxxx` entry replaced the entire file, dropping D001-D042 in the working copy
13
+ (GitHub issue #500). This hook is the durable, agent-agnostic guard.
14
+
15
+ Scope and bypass surface (deliberately bounded):
16
+ - Guards the `Write` tool only. `Edit` is surgical (it cannot easily drop the
17
+ whole file) and simulating its result to detect drops is fragile, so it is
18
+ not guarded.
19
+ - It does NOT cover `Bash` redirection (`cat > file`, `tee`, `sed -i`), which
20
+ an agent with Bash could use to clobber the file. This hook therefore
21
+ protects *entry existence against full-file Writes*, not body content, and
22
+ is not an absolute "any tool" guarantee. A Bash-command scan is the natural
23
+ follow-up extension if that guarantee is needed.
24
+
25
+ Emits a JSON decision on stdout and exits 0 (the modern pattern); exit 2 +
26
+ stderr is the fail-closed fallback for an unparseable event, matching
27
+ `block_secret_reads.py`.
28
+
29
+ Cross-platform: stdlib only, no shell dependencies.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ import re
36
+ import sys
37
+ from pathlib import Path, PurePath
38
+ from typing import Any
39
+
40
+ # Registry of append-only files to protect, keyed by path SUFFIX (not bare
41
+ # basename, to avoid false positives on unrelated `decisions.md` files
42
+ # elsewhere in the tree). Each value is the anchored, multiline regex that
43
+ # captures the entry IDs whose existence must be preserved across a write.
44
+ # To protect another append-only spec, add its suffix + ID pattern here.
45
+ #
46
+ # The ID capture includes an optional `-<suffix>` segment so suffixed entries
47
+ # (e.g. `## D038-A:`) are tracked as distinct IDs. Without it, `D\d+` would
48
+ # truncate `D038-A` to `D038`, collapsing it with a sibling `## D038:` entry
49
+ # and silently allowing one of them to be dropped (the exact #500 hazard).
50
+ DECISION_ID_PATTERN = re.compile(r"^##\s+(D\d+(?:-[A-Za-z0-9]+)?)", re.MULTILINE)
51
+
52
+ APPEND_ONLY_FILES: dict[str, re.Pattern[str]] = {
53
+ ".claude/specs/decisions.md": DECISION_ID_PATTERN,
54
+ }
55
+
56
+ DENY_REASON = (
57
+ "Blocked by AgentFluent append-only guard hook "
58
+ "(.claude/hooks/guard_append_only.py). "
59
+ "This Write would drop one or more existing entries from an append-only "
60
+ "log, which is a data-loss risk (see issue #500). "
61
+ "The guard protects entry existence (`## Dxxx` headings), not body text -- "
62
+ "editing the body of an existing entry is fine as long as no entry is "
63
+ "removed. To add an entry, append to the file (e.g. read the current "
64
+ "content and write it back with the new entry appended, or use Edit), "
65
+ "preserving every existing `## Dxxx` heading."
66
+ )
67
+
68
+
69
+ def normalize(path_str: str) -> str:
70
+ """Return a forward-slash path string for suffix matching."""
71
+ return PurePath(path_str).as_posix()
72
+
73
+
74
+ def match_registered_file(path_str: str) -> re.Pattern[str] | None:
75
+ """Return the ID pattern for a registered append-only file, else None."""
76
+ if not path_str:
77
+ return None
78
+ normalized = normalize(path_str)
79
+ for suffix, pattern in APPEND_ONLY_FILES.items():
80
+ # Require a path-component boundary so an unrelated file whose tail
81
+ # merely ends in the suffix string (e.g. `.../vendor.claude/specs/
82
+ # decisions.md`) is not mistaken for the protected log.
83
+ if normalized == suffix or normalized.endswith("/" + suffix):
84
+ return pattern
85
+ return None
86
+
87
+
88
+ def extract_ids(text: str, pattern: re.Pattern[str]) -> set[str]:
89
+ """Extract the set of entry IDs from text using the registered pattern."""
90
+ return set(pattern.findall(text))
91
+
92
+
93
+ def evaluate(
94
+ existing_content: str, proposed_content: str, pattern: re.Pattern[str]
95
+ ) -> tuple[bool, str]:
96
+ """Decide whether a write should be blocked. Pure (no I/O).
97
+
98
+ Returns (blocked, reason). Blocks when any ID present in the existing
99
+ content is absent from the proposed content.
100
+ """
101
+ existing_ids = extract_ids(existing_content, pattern)
102
+ if not existing_ids:
103
+ # Nothing to protect yet (file exists but has no recognized entries).
104
+ return False, ""
105
+ proposed_ids = extract_ids(proposed_content, pattern)
106
+ missing = sorted(existing_ids - proposed_ids)
107
+ if missing:
108
+ return True, f"{DENY_REASON} (would drop: {', '.join(missing)})"
109
+ return False, ""
110
+
111
+
112
+ def check(event: dict[str, Any]) -> tuple[bool, str]:
113
+ """Inspect a PreToolUse event; return (blocked, reason)."""
114
+ if event.get("tool_name", "") != "Write":
115
+ return False, ""
116
+
117
+ tool_input = event.get("tool_input") or {}
118
+ path = tool_input.get("file_path") or ""
119
+ pattern = match_registered_file(path)
120
+ if pattern is None:
121
+ return False, ""
122
+
123
+ proposed_content = tool_input.get("content") or ""
124
+
125
+ try:
126
+ existing_content = Path(path).read_text(encoding="utf-8")
127
+ except FileNotFoundError:
128
+ # New file: there is nothing to drop.
129
+ return False, ""
130
+ except (OSError, UnicodeDecodeError) as e:
131
+ # A real read error on an existing protected file means we cannot
132
+ # verify the write is safe. Fail closed -- deny rather than risk a
133
+ # silent clobber. (Such errors on a local file are vanishingly rare.)
134
+ return True, (
135
+ f"{DENY_REASON} (could not read the existing file to verify no "
136
+ f"entries are dropped: {e})"
137
+ )
138
+
139
+ return evaluate(existing_content, proposed_content, pattern)
140
+
141
+
142
+ def emit_decision(decision: str, reason: str) -> None:
143
+ payload = {
144
+ "hookSpecificOutput": {
145
+ "hookEventName": "PreToolUse",
146
+ "permissionDecision": decision,
147
+ "permissionDecisionReason": reason,
148
+ }
149
+ }
150
+ sys.stdout.write(json.dumps(payload))
151
+
152
+
153
+ def main() -> int:
154
+ try:
155
+ event = json.load(sys.stdin)
156
+ except (json.JSONDecodeError, ValueError) as e:
157
+ # Fail closed: if we can't parse the event we can't confirm the write
158
+ # is safe, so deny rather than allow through a malformed event.
159
+ print(
160
+ f"guard_append_only: failed to parse hook event JSON, "
161
+ f"denying by default: {e}",
162
+ file=sys.stderr,
163
+ )
164
+ return 2
165
+
166
+ blocked, reason = check(event)
167
+ if blocked:
168
+ emit_decision("deny", reason)
169
+ return 0
170
+
171
+
172
+ if __name__ == "__main__":
173
+ sys.exit(main())
@@ -0,0 +1,119 @@
1
+ # Loop config — AgentFluent
2
+
3
+ Per-project bindings for the supervised dev loop. `loop-engine.md` (the generic engine)
4
+ references every value below **by parameter name**; this file is the only surface a porting
5
+ project edits. A non-Python / non-`src/` / non-GitHub project revises **all four sections
6
+ below** (parameters, architect triggers, source layout, security routing) — never the engine.
7
+
8
+ See `.claude/skills/release-loop/loop-engine.md` for the operating procedure and semantics.
9
+
10
+ ---
11
+
12
+ ## 1. Project parameters
13
+
14
+ The binding table. The engine names each parameter in `CAPS`; the values here are AgentFluent's.
15
+
16
+ | Parameter | AgentFluent value | Notes |
17
+ |-----------|-------------------|-------|
18
+ | `BACKLOG_SOURCE` | GitHub milestone (e.g. `v0.11.0`) via `gh` | could be a label (an epic is the label case — `gh issue list --label epic:<name>`), or a local `TODO.md` |
19
+ | `SCOPE_AGENT` | `pm` (user-global subagent) | answers scope/priority/requirements questions; remove if project has none |
20
+ | `DESIGN_AGENT` | `architect` (user-global subagent) | reviews plans pre-implementation; remove if none |
21
+ | `CODE_REVIEW` | `/code-review` (Claude Code **built-in skill**) | independent post-impl review; verified 2026-06-29. The repo's `/review`/`/simplify` (CLAUDE.md) are alternatives, not this. |
22
+ | `SECURITY_REVIEW` | local `/security-review` (built-in skill) for `.claude/`-only; else `needs-security-review` label → `security-review.yml` | see §4 below |
23
+ | `VERIFY` | `/verify` (built-in skill) | runtime behavior check when an AC needs proof-by-running |
24
+ | `PRIORITY_LABELS` | `priority:high > priority:medium > priority:low`; tiebreak: issue number ascending | drives selection (engine → Select / Initialization) |
25
+ | `ARCHITECT_TRIGGERS` | see §2 below | **project-specific — edit when porting** |
26
+ | `SOURCE_LAYOUT` | see §3 below | router uses this; **edit when porting** |
27
+ | `TEST_CMD` | `uv run pytest -m "not integration"` | |
28
+ | `LINT_CMD` | `uv run ruff check src/ tests/` | |
29
+ | `TYPE_CMD` | `uv run mypy src/agentfluent/` | |
30
+ | `CI_STATUS_CMD` | `gh pr checks <PR>` | |
31
+ | `BRANCH_FMT` | `feature/<n>-slug` / `fix/<n>-slug` | from CLAUDE.md |
32
+ | `COMMIT_CONV` | Conventional Commits; `.claude/**`→`chore:`/`docs:` | see commit-scope rule below |
33
+ | `PR_TEMPLATE` | `.github/PULL_REQUEST_TEMPLATE.md` (must replicate) | |
34
+ | `MERGE_METHOD` | squash, `--delete-branch`, explicit `--subject` scope | |
35
+ | `APPEND_ONLY_FILES` | `.claude/specs/decisions.md` | guarded by the C1 hook |
36
+ | `PERMISSION_POSTURE` | background agents validate-only → parent implements | see hard constraints below |
37
+ | `LEDGER_ROOT` | `.claude/loop/` | **gitignored** — local working state, never committed |
38
+ | `RELEASE_SCHEME` | SemVer via release-please; PyPI artifact | the engine's merge gate reads "≤ patch bump or no bump"; a project with **no** release cycle (e.g. an epic-sourced run) treats every change as "no release-artifact bump" |
39
+
40
+ **Workflow conventions (from CLAUDE.md).** Branch from `main`; PR with passing CI before
41
+ merge (naming `BRANCH_FMT`). PR body **must replicate** `PR_TEMPLATE` (CI's `PR Template Check`
42
+ rejects otherwise). Tests required for code changes; no regressions; mypy strict on `src/`.
43
+
44
+ **Commit-scope rule.** `.claude/**` changes are maintainer-only tooling → `chore:`/`docs:`,
45
+ never `feat:`/`fix:` (avoids release-please mis-bumps). The orchestrator sets the squash
46
+ **`--subject` scope explicitly**, not inheriting the PR title.
47
+
48
+ **Hard constraints that shape the loop here (project-specific unless noted):**
49
+ 1. Background/non-interactive agents are **validate-only** — `settings.local.json` withholds
50
+ Edit/Write/git/gh from agents that can't prompt. The parent (interactive) thread does all
51
+ implementation + git/gh; no fan-out of implementation. *(A project without this restriction
52
+ could parallelize iterations via worktrees — see engine roadmap.)*
53
+ 2. *(General, Claude Code)* Subagents can't invoke subagents — the orchestrator drives
54
+ `SCOPE_AGENT`/`DESIGN_AGENT` directly.
55
+ 3. CI is gated on `branches:[main]`; stacked PRs break it → one PR at a time, each from `main`.
56
+ 4. `SCOPE_AGENT`/`DESIGN_AGENT` are user-global, not repo-tracked; editing them yields no PR
57
+ and needs a session restart.
58
+
59
+ ---
60
+
61
+ ## 2. `ARCHITECT_TRIGGERS`
62
+
63
+ Fire `DESIGN_AGENT` when the plan: touches shared models (`SessionMessage`, `AgentInvocation`);
64
+ changes a cross-module interface; adds a new diagnostics rule, correlation logic, or analytics
65
+ pipeline; **or the orchestrator is unsure.** Bias toward calling it (read-only, cheap vs. a bad
66
+ implementation). Skip for docs and trivial research.
67
+
68
+ ---
69
+
70
+ ## 3. `SOURCE_LAYOUT` — router signals
71
+
72
+ The project-specific inputs to the engine's Route classification (engine → Router):
73
+
74
+ - **Package layout:** code in `src/agentfluent/`; tests in `tests/`; research/throwaway
75
+ scaffolding lives **outside** `src/` with **no runtime-dep leakage** into the package.
76
+ - **`docs` label / path:** GitHub label `documentation`, change confined to `docs/`/markdown.
77
+ - **`research` label / path:** GitHub label `research` or an epic named `*-discovery`; "artifact
78
+ under study is data"; no test-coverage gate.
79
+ - **`stub-defer` marker:** issue body says "NOT implementation-ready" / is a tracking stub
80
+ (e.g. #469, D041 — carried, not implemented).
81
+ - **`code` (default):** `bug`/`enhancement` touching `src/` → full pipeline.
82
+
83
+ ---
84
+
85
+ ## 4. Security routing (host-repo / GitHub specifics)
86
+
87
+ The engine's step-10 security gate reads its *specifics* from here (engine keeps the gate's
88
+ position + confidence-bar discipline):
89
+
90
+ - **`.claude/`-only change** → run local `SECURITY_REVIEW` (`/security-review`). The labeled
91
+ workflow (`security-review.yml`) **excludes** `.claude/`, so the label does nothing here; the
92
+ local skill needs `git remote set-head origin -a` if it errors on `origin/HEAD...`.
93
+ - **Otherwise, sensitive surface touched** → apply the `needs-security-review` label **only when
94
+ dev-complete** (the workflow triggers on `[labeled]`, not on push — labeling early leaves later
95
+ commits unreviewed). Skip for docs/no-surface changes.
96
+
97
+ **Mechanical rules (both learned in the #500 pilot):** `.claude/`-only → local review, not the
98
+ label (workflow excludes `.claude/`; local skill needs `origin/HEAD` set); squash `--subject`
99
+ scope set explicitly (`chore:` for `.claude/**`), not inherited from the PR title.
100
+
101
+ ---
102
+
103
+ ## 5. Project examples referenced by the engine's guidance
104
+
105
+ The engine states its principles generically; these are the AgentFluent instances it draws on,
106
+ kept here so the engine stays project-agnostic:
107
+
108
+ - **Post-hoc token/cost analyzer** (engine journal step, `tokens=deferred`): AgentFluent itself,
109
+ run over the loop's own JSONL corpus.
110
+ - **Externally-cited-source fidelity check** (engine Plan step): a research-scout candidate feed
111
+ (`anthropic-feature-watch`) is one such source. The #437 / decision **D046** episode is the
112
+ worked example — a feature extrapolated past what its source established.
113
+ - **Cheap-falsifier-caught-late** (engine Plan step): the #437 corpus pass caught a misdirected
114
+ feature, but only post-hoc.
115
+ - **Notes-as-decision, not-evidence** (engine Triage step + ledger discipline): the v0.10.0
116
+ row-12 resume-instability failure is the worked example of mutable live evidence in Notes
117
+ destabilizing resume.
118
+ - **First route graduation** (engine merge gate, `graduated-routes`): decision **D047** graduated
119
+ `docs`+`research`.
@@ -0,0 +1,40 @@
1
+ {
2
+ "_comment": "Project-specific custom patterns for the security-guidance plugin's free pattern layer. Complements (does not duplicate) the 25 built-in rules. Authored from the architect review on issue #524. JSON (not YAML) so the plugin's hook runtime never needs PyYAML. Globs use a leading wildcard because the hook matches against the absolute path with its leading slash stripped (file_path.lstrip('/') in check_patterns), and fnmatch anchors at the start.",
3
+ "patterns": [
4
+ {
5
+ "rule_name": "no-shell-true",
6
+ "regex": "shell\\s*=\\s*True",
7
+ "paths": ["*src/agentfluent/*.py"],
8
+ "exclude_paths": ["*/tests/*"],
9
+ "reminder": "AgentFluent never invokes a shell. Use list-form subprocess.run([...], shell=False); never shell=True. CLI args (--project/--agent/--session) must not reach a shell."
10
+ },
11
+ {
12
+ "rule_name": "no-dynamic-subprocess-args",
13
+ "regex": "subprocess\\.(run|Popen|call|check_output|check_call)\\(\\s*f[\"']",
14
+ "paths": ["*src/agentfluent/*.py"],
15
+ "exclude_paths": ["*/tests/*"],
16
+ "reminder": "Pass subprocess args as a list of literals/typed values, never an f-string or string-concatenated command. Building the command from interpolated input reintroduces injection even with shell=False."
17
+ },
18
+ {
19
+ "rule_name": "no-inprocess-http-client",
20
+ "regex": "(?:^|\\n)\\s*(import|from)\\s+(requests|httpx|urllib\\.request|urllib3|http\\.client|socket|aiohttp|ftplib|smtplib)\\b",
21
+ "paths": ["*src/agentfluent/*.py"],
22
+ "exclude_paths": ["*/tests/*", "*src/agentfluent/github/*"],
23
+ "reminder": "AgentFluent's only network egress is the gh CLI (Tier 3 GitHub signals). Do not import an in-process HTTP client. If you are adding a network feature, route it through the gh subprocess path or open a design discussion first."
24
+ },
25
+ {
26
+ "rule_name": "no-archive-extractall",
27
+ "substrings": [".extractall(", "tarfile.open(", "zipfile.ZipFile("],
28
+ "paths": ["*src/agentfluent/*.py"],
29
+ "exclude_paths": ["*/tests/*"],
30
+ "reminder": "AgentFluent reads JSONL, never archives. extractall() is a zip-slip / tar-slip path-traversal sink. If you genuinely need extraction, validate every member path stays within the target dir."
31
+ },
32
+ {
33
+ "rule_name": "no-render-secret-shaped-values",
34
+ "regex": "(print|log(ger)?\\.\\w+|console\\.print)\\([^)]*(api_key|api-key|secret|password)",
35
+ "paths": ["*src/agentfluent/*.py"],
36
+ "exclude_paths": ["*/tests/*"],
37
+ "reminder": "Session transcripts can contain secret-shaped values. Never print/log a variable named api_key/secret/password un-redacted. Redact at the boundary; see docs/SECURITY.md."
38
+ }
39
+ ]
40
+ }
@@ -9,6 +9,15 @@
9
9
  "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/block_secret_reads.py\""
10
10
  }
11
11
  ]
12
+ },
13
+ {
14
+ "matcher": "Write",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard_append_only.py\""
19
+ }
20
+ ]
12
21
  }
13
22
  ],
14
23
  "PostToolUse": [
@@ -32,5 +41,13 @@
32
41
  ]
33
42
  }
34
43
  ]
44
+ },
45
+ "enabledPlugins": {
46
+ "security-guidance@claude-plugins-official": true
47
+ },
48
+ "env": {
49
+ "ENABLE_CODE_SECURITY_REVIEW": "0",
50
+ "SECURITY_REVIEW_MODEL": "claude-sonnet-4-6",
51
+ "SG_AGENTIC_MODEL": "claude-sonnet-4-6"
35
52
  }
36
53
  }
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: release-loop
3
+ description: Run one routed iteration of the supervised dev loop over a backlog (milestone/label). Selects the next unblocked issue, routes it, drives plan→architect→implement→review→merge with human gates on uncertainty, and journals to the ledger. Invoke once per issue; re-invoke (or drive via /loop) for the next. Use when the user wants to work a backlog as a loop, "run the release loop", or "do the next issue".
4
+ ---
5
+
6
+ # Release Loop — orchestrator (ONE issue per invocation)
7
+
8
+ You are the orchestrator of a supervised dev loop. Each invocation handles exactly ONE issue
9
+ end-to-end, journals, and stops. **State lives in the ledger, not your context** — so a fresh
10
+ invocation resumes correctly.
11
+
12
+ The operating procedure and all semantics live in two sibling files. Sibling files are read on
13
+ demand, not auto-injected, so **your literal first step is to read both** — do not act on the
14
+ invariants below without them:
15
+
16
+ 1. **`${CLAUDE_PROJECT_DIR}/.claude/loop.config.md`** — the per-project bindings (what
17
+ `BACKLOG_SOURCE`, `SCOPE_AGENT`, `DESIGN_AGENT`, `CODE_REVIEW`, `PRIORITY_LABELS`, `LINT_CMD`/
18
+ `TYPE_CMD`/`TEST_CMD`, `BRANCH_FMT`, `COMMIT_CONV`, `MERGE_METHOD`, … resolve to for this repo).
19
+ 2. **`.claude/skills/release-loop/loop-engine.md`** — the generic engine: the numbered pipeline
20
+ (step 0 load/resume → 1 select → 2 route → 3 plan → 4 architect → 5 human gate → 6 implement →
21
+ 7 AC-verify → 8 commit/PR → 9 code-review → 10 security → 11 merge → 12 journal), plus the
22
+ ledger format, router, AC-verifier, initialization, resume, routing table, and
23
+ gate/convergence/park-hold/budget semantics.
24
+
25
+ Read config for **bindings**, engine for **logic**. Execute the engine's pipeline exactly, for
26
+ exactly one issue, then STOP.
27
+
28
+ ## Fail-safe invariants (hold even before the engine loads)
29
+
30
+ These are restated here so a partial load **over-escalates** (safe) rather than under-gates. The
31
+ engine is authoritative; on any conflict, follow the engine — but never do less than this:
32
+
33
+ - **One issue per invocation, then STOP and journal.** Never batch. The driver re-invokes with
34
+ fresh context for the next issue.
35
+ - **Resume before selecting.** If a ledger row is mid-pipeline (interrupted), finish it against
36
+ live git/PR state before starting anything new. One PR at a time; no stacked PRs.
37
+ - **Never auto-merge under uncertainty.** Default-deny: if you are unsure whether a row is
38
+ auto-merge-eligible, STOP and ask the human. Never merge red CI, never force-push, never
39
+ admin-merge, only `--delete-branch` the PR's own branch.
40
+ - **Escalate rather than guess.** Scope/value → `SCOPE_AGENT`; design → `DESIGN_AGENT`; unresolved,
41
+ contested, or irreversible → the human.
42
+ - **Never edit the user-global `SCOPE_AGENT`/`DESIGN_AGENT` definitions**, and never `git add`
43
+ unrelated pre-existing working-tree changes.
44
+ - **The ledger is gitignored** — do NOT commit it.