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.
- agentfluent-0.11.0/.claude/claude-security-guidance.md +69 -0
- agentfluent-0.11.0/.claude/hooks/guard_append_only.py +173 -0
- agentfluent-0.11.0/.claude/loop.config.md +119 -0
- agentfluent-0.11.0/.claude/security-patterns.json +40 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/settings.json +17 -0
- agentfluent-0.11.0/.claude/skills/release-loop/SKILL.md +44 -0
- agentfluent-0.11.0/.claude/skills/release-loop/loop-engine.md +592 -0
- agentfluent-0.11.0/.claude/specs/agent-sdk-session-format-findings.md +312 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analysis.md +164 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analyze.json +127576 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-06-05-v09-dogfood/analyze.table.txt +2823 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analysis.md +165 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analyze.json +155136 -0
- agentfluent-0.11.0/.claude/specs/analysis/2026-07-03-v10-dogfood/analyze.table.txt +3022 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/decisions.md +315 -0
- agentfluent-0.11.0/.claude/specs/prd-agent-sdk-discovery.md +135 -0
- agentfluent-0.11.0/.claude/specs/prd-continuous-eval.md +174 -0
- agentfluent-0.11.0/.claude/specs/prd-loop-engineering.md +295 -0
- agentfluent-0.11.0/.claude/specs/prd-loop-plugin.md +153 -0
- agentfluent-0.11.0/.claude/specs/prd-pricing-genai-migration.md +151 -0
- agentfluent-0.11.0/.claude/specs/prd-v0.10.md +227 -0
- agentfluent-0.11.0/.claude/specs/prd-v0.11.md +202 -0
- agentfluent-0.11.0/.claude/specs/value-review-437-verbosity.md +182 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/ci.yml +1 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/release-please.yml +1 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/security-review.yml +9 -2
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.gitignore +10 -0
- agentfluent-0.11.0/.release-please-manifest.json +3 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/CHANGELOG.md +54 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/CLAUDE.md +44 -7
- {agentfluent-0.9.0 → agentfluent-0.11.0}/CONTRIBUTING.md +18 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/PKG-INFO +38 -30
- {agentfluent-0.9.0 → agentfluent-0.11.0}/README.md +36 -29
- agentfluent-0.11.0/docs/COST_MODEL.md +74 -0
- agentfluent-0.11.0/docs/EFFICIENCY_DIAGNOSTICS_RESEARCH.md +130 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/GLOSSARY.md +170 -3
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/ROADMAP.md +42 -7
- agentfluent-0.11.0/docs/TOKEN_ACCOUNTING.md +725 -0
- agentfluent-0.11.0/images/demo-analyze.svg +462 -0
- agentfluent-0.11.0/images/demo-diagnostics.svg +428 -0
- agentfluent-0.11.0/images/demo-diff.svg +474 -0
- agentfluent-0.11.0/images/demo-subagents.svg +167 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/pyproject.toml +8 -1
- agentfluent-0.11.0/research/agent-sdk-probe/FINDINGS.md +607 -0
- agentfluent-0.11.0/research/agent-sdk-probe/README.md +107 -0
- agentfluent-0.11.0/research/agent-sdk-probe/agent.py +269 -0
- agentfluent-0.11.0/research/agent-sdk-probe/fixture.txt +7 -0
- agentfluent-0.11.0/research/agent-sdk-probe/probe.py +38 -0
- agentfluent-0.11.0/research/agent-sdk-probe/run_matrix.py +208 -0
- agentfluent-0.11.0/research/agent-sdk-probe/sampledata/code.py +7 -0
- agentfluent-0.11.0/research/agent-sdk-probe/sampledata/data.csv +6 -0
- agentfluent-0.11.0/research/agent-sdk-probe/sampledata/notes.txt +12 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/extractor.py +3 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/models.py +29 -0
- agentfluent-0.11.0/src/agentfluent/analytics/_genai_source.py +97 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/pipeline.py +53 -3
- agentfluent-0.11.0/src/agentfluent/analytics/pricing.py +254 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/tokens.py +72 -17
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/analyze.py +5 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/helpers.py +42 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/table.py +63 -2
- agentfluent-0.11.0/src/agentfluent/config/hook_inspector.py +142 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/models.py +21 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/parser.py +37 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/paths.py +17 -0
- agentfluent-0.11.0/src/agentfluent/core/session.py +369 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_complexity.py +73 -5
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/correlator.py +219 -33
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/delegation.py +5 -5
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/model_routing.py +200 -56
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/models.py +9 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/parent_workload.py +2 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/pipeline.py +38 -2
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/signals.py +71 -11
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/trace_signals.py +37 -39
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/loader.py +32 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/terms.yaml +159 -3
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/parser.py +9 -7
- agentfluent-0.11.0/src/agentfluent/traces/sidecar.py +125 -0
- agentfluent-0.11.0/tests/fixtures/hooks/inline_bash_timing.yaml +15 -0
- agentfluent-0.11.0/tests/fixtures/hooks/no_post_hook.yaml +9 -0
- agentfluent-0.11.0/tests/fixtures/hooks/secrets_hook.py +18 -0
- agentfluent-0.11.0/tests/fixtures/hooks/timing_hook.py +15 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/README.md +54 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-leaf0001.jsonl +4 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-leaf0001.meta.json +1 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-worker001.jsonl +6 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1/subagents/agent-worker001.meta.json +1 -0
- agentfluent-0.11.0/tests/fixtures/nested_session/nested-session-1.jsonl +4 -0
- agentfluent-0.11.0/tests/fixtures/sdk_session/README.md +70 -0
- agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1/subagents/agent-child0000001.jsonl +6 -0
- agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1/subagents/agent-child0000001.meta.json +5 -0
- agentfluent-0.11.0/tests/fixtures/sdk_session/sdk-main-1.jsonl +4 -0
- agentfluent-0.11.0/tests/fixtures/session_cli_entrypoint.jsonl +2 -0
- agentfluent-0.11.0/tests/unit/cli/test_session_kind_indicator.py +186 -0
- agentfluent-0.11.0/tests/unit/test_append_only_guard.py +267 -0
- agentfluent-0.11.0/tests/unit/test_complexity.py +51 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_correlator.py +239 -13
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_dedup.py +23 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_diagnostics_pipeline.py +143 -1
- agentfluent-0.11.0/tests/unit/test_entrypoint_classification.py +218 -0
- agentfluent-0.11.0/tests/unit/test_genai_source.py +105 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_loader.py +35 -0
- agentfluent-0.11.0/tests/unit/test_hook_inspector.py +137 -0
- agentfluent-0.11.0/tests/unit/test_loop_skill_drift.py +155 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_model_routing.py +90 -0
- agentfluent-0.11.0/tests/unit/test_model_routing_sdk_main.py +282 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_cost.py +18 -2
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parser.py +177 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_paths.py +16 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_pipeline.py +33 -0
- agentfluent-0.11.0/tests/unit/test_pricing.py +550 -0
- agentfluent-0.11.0/tests/unit/test_sdk_session_fixture.py +157 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_session_models.py +54 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_signals.py +100 -1
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tokens.py +138 -3
- agentfluent-0.11.0/tests/unit/test_totaltokens_semantics.py +191 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_trace_signals.py +111 -5
- agentfluent-0.11.0/tests/unit/test_traces_nested_fixture.py +110 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_parser.py +141 -0
- agentfluent-0.11.0/tests/unit/test_traces_sidecar.py +186 -0
- agentfluent-0.11.0/tests/unit/tools/__init__.py +0 -0
- agentfluent-0.11.0/tests/unit/tools/test_dogfood_cli_runner.py +338 -0
- agentfluent-0.11.0/tests/unit/tools/test_dogfood_model_rotation.py +95 -0
- agentfluent-0.11.0/tests/unit/tools/test_dogfood_paths.py +126 -0
- agentfluent-0.11.0/tests/unit/tools/test_dogfood_runner_main.py +56 -0
- agentfluent-0.11.0/tools/__init__.py +6 -0
- agentfluent-0.11.0/tools/dogfood_runner/README.md +175 -0
- agentfluent-0.11.0/tools/dogfood_runner/__init__.py +11 -0
- agentfluent-0.11.0/tools/dogfood_runner/cli_runner.py +426 -0
- agentfluent-0.11.0/tools/dogfood_runner/install-cron.sh +125 -0
- agentfluent-0.11.0/tools/dogfood_runner/paths.py +168 -0
- agentfluent-0.11.0/tools/dogfood_runner/runner.py +270 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/uv.lock +277 -5
- agentfluent-0.9.0/.github/workflows/claude-review.yml +0 -65
- agentfluent-0.9.0/.release-please-manifest.json +0 -3
- agentfluent-0.9.0/images/demo-analyze.svg +0 -426
- agentfluent-0.9.0/images/demo-diagnostics.svg +0 -445
- agentfluent-0.9.0/images/demo-diff.svg +0 -466
- agentfluent-0.9.0/images/demo-subagents.svg +0 -167
- agentfluent-0.9.0/src/agentfluent/analytics/pricing.py +0 -116
- agentfluent-0.9.0/src/agentfluent/core/session.py +0 -186
- agentfluent-0.9.0/tests/unit/test_pricing.py +0 -141
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/agents/anthropic-research.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/agents/candidate-verifier.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/hooks/block_secret_reads.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/hooks/detect_secrets_in_output.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/skills/promote-candidates/SKILL.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/.gitkeep +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-04-29-agent-portfolio-analysis.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-05-17-v07-dogfood-analysis.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/2026-05-30-v08-dogfood/analysis.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/findings.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/labels.tsv +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/sample.tsv +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/333-error-pattern-precision/script.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/402-calibration/calibration.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/402-calibration/pairs.tsv +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/calibration.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/detections.tsv +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/export.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/sample.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/analysis/407-calibration/tune.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-mvp.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.3.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.5.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.6.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.7.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.8.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/backlog-v0.9.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/plan-100-mcp-assessment.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/plan-116-mcp-extraction.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-advanced-tool-use-diagnostics.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-context-fork-loop-detection.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-date-range-filtering.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-glossary.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-mvp.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-quality-axis.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-research-pipeline-phase-3.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-tier3-github-enrichment.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-tool-name-normalization.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.3.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.5.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.6.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.7.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.8.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/prd-v0.9.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/research/anthropic-feature-watch.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/research-update-2026-04-15.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.claude/specs/v0.4-scope-review.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/dependabot.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/dependabot-auto-merge.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/milestone-docs-issue.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.github/workflows/pr-template-check.yml +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/.python-version +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/LICENSE +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/SECURITY.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/AGENT_ANALYTICS_RESEARCH.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/RAG_OVER_TOOLS_RESEARCH.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/SECURITY.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/docs/codefluent_cli_review_042526.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-analyze.svg +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-config-check.svg +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-diagnostics.svg +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/images/archive/v0.3/demo-subagents.svg +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/images/demo-config-check.svg +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/release-please-config.json +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/README.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/build_notebook.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/quality_labels.json +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/calibration/threshold_validation.ipynb +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/generate_glossary_md.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/scripts/generate_readme_screenshots.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/agents/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/agent_metrics.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/analytics/tools.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/_time_args.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/config_check.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/diff_cmd.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/explain.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/list_cmd.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/report.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/commands/report_renderers.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/exit_codes.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/diff_table.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/formatters/json_output.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/cli/main.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/mcp_discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/retention.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/scanner.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/config/scoring.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/filtering.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/core/timeutil.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_clustering.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/_git_helpers.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/agent_audit.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/aggregation.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/builtin_actions.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/git_signals.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/github_signals.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/mcp_assessment.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/quality_signals.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diagnostics/tool_orchestration.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/compute.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/loader.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/diff/models.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/cache.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/client.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/consent.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/detection.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/github/models.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/models.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/glossary/render.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/py.typed +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/linker.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/models.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/src/agentfluent/traces/retry.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/_builders.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/conftest.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/.gitkeep +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/empty_prompt.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/no_frontmatter.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/no_tools.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/vague_description.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/agents/well_configured.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/mcp/claude_user_only.json +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/mcp/claude_user_with_disabled.json +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/parent_workload_session.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/report/analyze_snapshot.json +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/report/expected_report.md +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_basic.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_block_per_line.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_malformed.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_skip_types.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_streaming_dupes.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_with_agent.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/session_with_tool_calls.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-basic.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-empty.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-errors.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-large.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-malformed.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-retry.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-streaming-dupes.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/fixtures/subagents/agent-stuck.jsonl +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_agent_extraction.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_analytics.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_analyze_session_scope.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_config_assessment.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_diagnostics.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_git_signals_real.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_real_sessions.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/integration/test_subagent_traces.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/_recommendation_helpers.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/conftest.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_active_duration_display.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_agent_duration_cell.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_github_flag.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_session_scope.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_analyze_since_until.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_axis_json_contract.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_axis_labels.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_claude_config_dir.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_cleanup_warning_cli.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_cost_labeling.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_deep_diagnostics_formatting.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diagnostics_default.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diagnostics_smoke.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_by_model_table.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_cmd.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_summary.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_diff_table_signed.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_environment_warning_banner.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_exit_codes.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_explain.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_glossary_footer.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_help.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_json_alias.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_json_output.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_list_since_until.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_min_severity_filter.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_offload_candidates_formatting.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_output_modes.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_parse_warnings.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_recommendations_top_n.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_cmd.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_golden.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_pipeline.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_report_renderers.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_tier3_degraded_banner.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_time_args.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/cli/test_verbose_signal_message.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/diff/__init__.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/diff/test_compute.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_audit.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_metrics.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_agent_models.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_clustering.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_core_filtering.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_delegation.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_delegation_yaml_draft.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_diagnostics.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_extractor.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_git_signals.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_cache.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_client.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_consent.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_detection.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_signals_ci_failure.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_github_signals_density.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_drift.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_glossary_render.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_mcp_assessment.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_mcp_discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_cluster.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_parent_workload_extract.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_quality_signals.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_recommendation_aggregation.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_retention.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_scanner.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_scoring.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_smoke.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_timeutil.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tool_inventory_audit.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tool_orchestration.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_tools.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_discovery.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_fixtures.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_linker.py +0 -0
- {agentfluent-0.9.0 → agentfluent-0.11.0}/tests/unit/test_traces_models.py +0 -0
- {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.
|