claude-dev-env 4.3.0 → 7.0.0
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.
- package/.agents/agents/pr-description-writer.md +2 -2
- package/.agents/agents/test_agent_frontmatter.py +2 -2
- package/.agents/agents/test_pr_description_writer_style.py +1 -3
- package/.agents/skills/e-code-review/SKILL.md +12 -12
- package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
- package/.agents/skills/e-code-review/reference/fix.md +15 -17
- package/.agents/skills/e-code-review/reference/loop.md +35 -39
- package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
- package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
- package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
- package/.agents/skills/issue-tracker/SKILL.md +5 -4
- package/.agents/skills/pull-request/SKILL.md +11 -13
- package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
- package/.agents/skills/pull-request/scripts/pull_request.py +33 -44
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +1 -3
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
- package/bin/install.agents-home.test.mjs +15 -13
- package/bin/install.mjs +28 -0
- package/bin/install.policy-lint.test.mjs +78 -0
- package/bin/install.prune.test.mjs +59 -20
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +37 -0
- package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
- package/hooks/advisory/pr_done_reminder.py +342 -0
- package/hooks/advisory/test_pr_done_reminder.py +249 -0
- package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
- package/hooks/blocking/code_rules_paired_test.py +22 -15
- package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
- package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
- package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
- package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
- package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
- package/hooks/git-hooks/pre_commit.py +52 -11
- package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
- package/hooks/git-hooks/test_native_policy_commit.py +87 -0
- package/hooks/git-hooks/test_pre_commit.py +246 -0
- package/hooks/hooks.json +1 -1
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
- package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
- package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
- package/package.json +2 -2
- package/rules/durable-post-artifacts.md +2 -1
- package/rules/git-workflow.md +6 -6
- package/scripts/check.ps1 +0 -1
- package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
- package/scripts/ci/installer-lifecycle.test.mjs +26 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
- package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
- package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +5 -41
- package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +0 -10
- package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +1 -1
- package/scripts/policy_lint/adapter_detectors.py +13 -6
- package/scripts/policy_lint/adapters.py +7 -4
- package/scripts/policy_lint/config/constants.py +5 -0
- package/scripts/policy_lint/engine.py +48 -4
- package/scripts/policy_lint/model.py +1 -0
- package/scripts/policy_lint/registry.py +1 -1
- package/scripts/policy_lint/selection.py +2 -1
- package/scripts/policy_lint/selection_git.py +2 -0
- package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
- package/scripts/shared_tree_paths.py +32 -3
- package/scripts/test_shared_tree_paths.py +33 -0
- package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
- package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
- package/scripts/tests/test_policy_lint_selection.py +25 -2
- package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
- package/scripts/tests/test_rules.py +3 -7
- package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
- package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
- package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
- package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
- package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
- package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
- package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
- package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
- package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
- package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
- package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
- package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
- package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
- package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
- package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
- package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
- package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
- package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
- package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
- package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
- package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
- package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
- package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
- package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
- package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
- package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
- package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
- package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
- package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
- package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
- package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
- package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
- package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
- package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
- package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
- package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
- package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
- package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
- package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/SKILL.md +0 -431
- package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
- package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
- package/.agents/skills/autoconverge/reference/convergence.md +0 -219
- package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
- package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
- package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
- package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
- package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
- package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
- package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
- package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
- package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
- package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
- package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
- package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
- package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
- package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
- package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
- package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
- package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
- package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
- package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
- package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
- package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
- package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
- package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
- package/.agents/skills/hitl/SKILL.md +0 -45
- package/.agents/skills/pr-cleanup/SKILL.md +0 -140
- package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
- package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
- package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
- package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
- package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
- package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
- package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
- package/.agents/skills/pr-refinement/SKILL.md +0 -31
- package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
- package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
- package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
- package/.agents/skills/pr-small-cl/SKILL.md +0 -52
- package/.agents/skills/pr-title-description/SKILL.md +0 -96
- package/.agents/skills/prototype/SKILL.md +0 -85
- package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
- package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
- package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
- package/.agents/skills/prototype/scripts/conftest.py +0 -15
- package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
- package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
- package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
- package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
- package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
- package/.agents/skills/prototype/workflows/promotion.md +0 -25
- package/.agents/skills/prototype/workflows/sandbox.md +0 -35
- package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
- package/.agents/skills/rebase/AGENTS.md +0 -1
- package/.agents/skills/rebase/SKILL.md +0 -178
- package/.agents/skills/review-router/SKILL.md +0 -51
- package/.agents/skills/review-router/reference/route-policy.json +0 -7
- package/.agents/skills/review-router/reference/task-seeds.md +0 -1
- package/.agents/skills/review-router/scripts/conftest.py +0 -4
- package/.agents/skills/review-router/scripts/review_router.py +0 -50
- package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
- package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
- package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
- package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
- package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
- package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
- package/.agents/skills/review-tier/SKILL.md +0 -56
- package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
- package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
- package/.agents/skills/review-tier/scripts/conftest.py +0 -4
- package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
- package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
- package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
- package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
- package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
- package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-log/AGENTS.md +0 -1
- package/.agents/skills/session-log/SKILL.md +0 -170
- package/.agents/skills/session-log/templates/frontmatter.md +0 -40
- package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-tidy/AGENTS.md +0 -1
- package/.agents/skills/session-tidy/SKILL.md +0 -191
- package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
- package/.agents/skills/skill-builder/references/description-field.md +0 -111
- package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
- package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
- package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
- package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
- package/.agents/skills/skill-builder/references/skill-types.md +0 -232
- package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
- package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
- package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
- package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
- package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
- package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
- package/.agents/skills/update/.claude/CLAUDE.md +0 -1
- package/.agents/skills/update/AGENTS.md +0 -1
- package/.agents/skills/update/SKILL.md +0 -190
- package/_shared/pr-loop/AGENTS.md +0 -1
- package/_shared/pr-loop/CLAUDE.md +0 -1
- package/_shared/pr-loop/audit-contract.md +0 -183
- package/_shared/pr-loop/audit-reply-template.md +0 -151
- package/_shared/pr-loop/code-rules-gate.md +0 -65
- package/_shared/pr-loop/fix-protocol.md +0 -60
- package/_shared/pr-loop/gh-payloads.md +0 -75
- package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
- package/_shared/pr-loop/precatch-rubric.md +0 -66
- package/_shared/pr-loop/preflight-proposal.md +0 -56
- package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
- package/_shared/pr-loop/state-schema.md +0 -45
- package/_shared/pr-loop/worker-spawn.md +0 -189
- /package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +0 -0
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
"""Tests pinning build_audit_prompt's emitted A-Q category taxonomy."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import importlib.util
|
|
6
|
-
import re
|
|
7
|
-
import sys
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from types import ModuleType
|
|
10
|
-
from xml.etree.ElementTree import Element
|
|
11
|
-
|
|
12
|
-
import pytest
|
|
13
|
-
|
|
14
|
-
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
15
|
-
_skills_home_directory = _SCRIPTS_DIR.parents[3]
|
|
16
|
-
_PACKAGE_ROOT_FOR_RUBRICS = (
|
|
17
|
-
_skills_home_directory.parent
|
|
18
|
-
if _skills_home_directory.name == ".agents"
|
|
19
|
-
else _skills_home_directory
|
|
20
|
-
)
|
|
21
|
-
if str(_SCRIPTS_DIR) not in sys.path:
|
|
22
|
-
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
23
|
-
|
|
24
|
-
from skills_pr_loop_constants.path_resolver_constants import (
|
|
25
|
-
ALL_AUDIT_CATEGORY_ENTRIES,
|
|
26
|
-
AUDIT_COMMENT_POSTING_AGENT_TEXT,
|
|
27
|
-
AUDIT_COMMENT_POSTING_HEADLESS_TEXT,
|
|
28
|
-
AUDIT_OUTPUT_FORMAT_AGENT_TEXT,
|
|
29
|
-
AUDIT_OUTPUT_FORMAT_HEADLESS_TEMPLATE,
|
|
30
|
-
AUDIT_PROMPT_FLAVOR_AGENT,
|
|
31
|
-
AUDIT_PROMPT_FLAVOR_HEADLESS,
|
|
32
|
-
OUTCOME_XML_TEMPLATE,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
_CATEGORY_RUBRICS_DIR = _PACKAGE_ROOT_FOR_RUBRICS / "audit-rubrics" / "category_rubrics"
|
|
36
|
-
_HEADING_PATTERN = re.compile(r"^# Category ([A-Q]) — (.+)$")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _load_build_audit_prompt() -> ModuleType:
|
|
40
|
-
module_path = _SCRIPTS_DIR / "build_audit_prompt.py"
|
|
41
|
-
spec = importlib.util.spec_from_file_location("build_audit_prompt", module_path)
|
|
42
|
-
assert spec is not None
|
|
43
|
-
assert spec.loader is not None
|
|
44
|
-
module = importlib.util.module_from_spec(spec)
|
|
45
|
-
sys.modules["build_audit_prompt"] = module
|
|
46
|
-
spec.loader.exec_module(module)
|
|
47
|
-
return module
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
build_audit_prompt = _load_build_audit_prompt()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _rubric_label_by_letter() -> dict[str, str]:
|
|
54
|
-
assert _CATEGORY_RUBRICS_DIR.is_dir(), f"Missing rubric directory: {_CATEGORY_RUBRICS_DIR}"
|
|
55
|
-
all_labels: dict[str, str] = {}
|
|
56
|
-
for each_rubric_file in sorted(_CATEGORY_RUBRICS_DIR.glob("category-*.md")):
|
|
57
|
-
all_rubric_lines = each_rubric_file.read_text(encoding="utf-8").splitlines()
|
|
58
|
-
assert all_rubric_lines, f"Empty rubric file: {each_rubric_file}"
|
|
59
|
-
each_match = _HEADING_PATTERN.match(all_rubric_lines[0])
|
|
60
|
-
assert each_match is not None, f"Heading pattern not matched in {each_rubric_file}"
|
|
61
|
-
all_labels[each_match.group(1)] = each_match.group(2)
|
|
62
|
-
return all_labels
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def _build_audit_root() -> Element:
|
|
66
|
-
return build_audit_prompt.build_audit_prompt_xml(
|
|
67
|
-
owner="jl-cmd",
|
|
68
|
-
repo="claude-dev-env",
|
|
69
|
-
pr_number=422,
|
|
70
|
-
loop=1,
|
|
71
|
-
head_ref="feat/branch",
|
|
72
|
-
base_ref="main",
|
|
73
|
-
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
74
|
-
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def test_context_and_scope_render_paths_with_forward_slashes() -> None:
|
|
79
|
-
root = build_audit_prompt.build_audit_prompt_xml(
|
|
80
|
-
owner="jl-cmd",
|
|
81
|
-
repo="claude-dev-env",
|
|
82
|
-
pr_number=376,
|
|
83
|
-
loop=1,
|
|
84
|
-
head_ref="feat/branch",
|
|
85
|
-
base_ref="main",
|
|
86
|
-
worktree_path=Path("C:/Users/example/AppData/Local/Temp/bugteam-pr-376/worktree"),
|
|
87
|
-
run_temp_dir=Path("C:/Users/example/AppData/Local/Temp/bugteam-pr-376"),
|
|
88
|
-
)
|
|
89
|
-
context = root.find("context")
|
|
90
|
-
assert context is not None
|
|
91
|
-
worktree_text = context.findtext("worktree_path")
|
|
92
|
-
run_temp_text = context.findtext("run_temp_dir")
|
|
93
|
-
assert worktree_text == "C:/Users/example/AppData/Local/Temp/bugteam-pr-376/worktree"
|
|
94
|
-
assert run_temp_text == "C:/Users/example/AppData/Local/Temp/bugteam-pr-376"
|
|
95
|
-
scope = root.find("scope")
|
|
96
|
-
assert scope is not None
|
|
97
|
-
assert scope.text is not None
|
|
98
|
-
assert "\\" not in scope.text
|
|
99
|
-
assert "C:/Users/example/AppData/Local/Temp/bugteam-pr-376/worktree" in scope.text
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def test_bug_categories_carry_ids_a_through_q_in_order() -> None:
|
|
103
|
-
root = _build_audit_root()
|
|
104
|
-
bug_categories = root.find("bug_categories")
|
|
105
|
-
assert bug_categories is not None
|
|
106
|
-
all_emitted_ids = [each_category.get("id") for each_category in bug_categories]
|
|
107
|
-
all_expected_ids = list("ABCDEFGHIJKLMNOPQ")
|
|
108
|
-
assert all_emitted_ids == all_expected_ids
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def test_emitted_category_labels_match_constant_entries() -> None:
|
|
112
|
-
root = _build_audit_root()
|
|
113
|
-
bug_categories = root.find("bug_categories")
|
|
114
|
-
assert bug_categories is not None
|
|
115
|
-
label_by_id = {
|
|
116
|
-
each_category.get("id"): each_category.text for each_category in bug_categories
|
|
117
|
-
}
|
|
118
|
-
for each_category_id, each_category_label in ALL_AUDIT_CATEGORY_ENTRIES:
|
|
119
|
-
assert label_by_id[each_category_id] == each_category_label
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def test_category_labels_match_rubric_file_headings() -> None:
|
|
123
|
-
assert dict(ALL_AUDIT_CATEGORY_ENTRIES) == _rubric_label_by_letter()
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def test_rubric_reference_element_names_category_rubrics_directory() -> None:
|
|
127
|
-
root = _build_audit_root()
|
|
128
|
-
rubric_reference = root.find("rubric_reference")
|
|
129
|
-
assert rubric_reference is not None
|
|
130
|
-
assert rubric_reference.text is not None
|
|
131
|
-
assert "audit-rubrics/category_rubrics" in rubric_reference.text
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def test_prompt_skeleton_sub_bucket_counts_match_rubric_rows() -> None:
|
|
135
|
-
"""Each prompt skeleton's numeric sub-bucket count equals its rubric's row count.
|
|
136
|
-
|
|
137
|
-
For every (letter, label) the prompts dir holds a category-<letter>- file.
|
|
138
|
-
The skeleton above the first standalone --- line states "decomposed into N
|
|
139
|
-
sub-buckets"; that N must equal the rubric's count of | <letter>N | rows,
|
|
140
|
-
and a numeric walk-instruction range (For each sub-bucket X1-Xn) must end
|
|
141
|
-
at that same row count. Skeletons with a [N] placeholder are skipped.
|
|
142
|
-
"""
|
|
143
|
-
prompts_directory = _CATEGORY_RUBRICS_DIR.parent / "prompts"
|
|
144
|
-
count_pattern = re.compile(r"decomposed into (\d+) sub-buckets")
|
|
145
|
-
for each_letter, _each_label in ALL_AUDIT_CATEGORY_ENTRIES:
|
|
146
|
-
all_prompt_matches = sorted(prompts_directory.glob(f"category-{each_letter.lower()}-*.md"))
|
|
147
|
-
assert all_prompt_matches, f"Missing prompt file for category {each_letter}"
|
|
148
|
-
all_skeleton_lines: list[str] = []
|
|
149
|
-
for each_line in all_prompt_matches[0].read_text(encoding="utf-8").splitlines():
|
|
150
|
-
if each_line == "---":
|
|
151
|
-
break
|
|
152
|
-
all_skeleton_lines.append(each_line)
|
|
153
|
-
skeleton_text = "\n".join(all_skeleton_lines)
|
|
154
|
-
each_count_match = count_pattern.search(skeleton_text)
|
|
155
|
-
if each_count_match is None:
|
|
156
|
-
assert "decomposed into [N] sub-buckets" in skeleton_text, (
|
|
157
|
-
f"Category {each_letter}: skeleton neither states a numeric "
|
|
158
|
-
"sub-bucket count nor carries the [N] placeholder"
|
|
159
|
-
)
|
|
160
|
-
continue
|
|
161
|
-
all_rubric_matches = sorted(_CATEGORY_RUBRICS_DIR.glob(f"category-{each_letter.lower()}-*.md"))
|
|
162
|
-
assert all_rubric_matches, f"Missing rubric file for category {each_letter}"
|
|
163
|
-
rubric_row_pattern = re.compile(r"^\| " + each_letter + r"\d+ \|", re.MULTILINE)
|
|
164
|
-
sub_bucket_row_count = len(rubric_row_pattern.findall(all_rubric_matches[0].read_text(encoding="utf-8")))
|
|
165
|
-
assert int(each_count_match.group(1)) == sub_bucket_row_count, (
|
|
166
|
-
f"Category {each_letter}: skeleton says {each_count_match.group(1)} sub-buckets "
|
|
167
|
-
f"but rubric has {sub_bucket_row_count} rows"
|
|
168
|
-
)
|
|
169
|
-
walk_range_pattern = re.compile(
|
|
170
|
-
rf"For each sub-bucket {each_letter}1[-–]{each_letter}(\d+)"
|
|
171
|
-
)
|
|
172
|
-
each_walk_match = walk_range_pattern.search(skeleton_text)
|
|
173
|
-
if each_walk_match is not None:
|
|
174
|
-
assert int(each_walk_match.group(1)) == sub_bucket_row_count, (
|
|
175
|
-
f"Category {each_letter}: walk instruction ends at "
|
|
176
|
-
f"{each_letter}{each_walk_match.group(1)} but rubric has "
|
|
177
|
-
f"{sub_bucket_row_count} rows"
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def test_bug_categories_include_category_q() -> None:
|
|
182
|
-
root = _build_audit_root()
|
|
183
|
-
bug_categories = root.find("bug_categories")
|
|
184
|
-
assert bug_categories is not None
|
|
185
|
-
label_by_id = {
|
|
186
|
-
each_category.get("id"): each_category.text for each_category in bug_categories
|
|
187
|
-
}
|
|
188
|
-
assert label_by_id["Q"] == (
|
|
189
|
-
"Cross-surface claim consistency "
|
|
190
|
-
"(terminology, PR-description claims, message-vs-guard)"
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def test_pr_description_carries_body_text_when_supplied() -> None:
|
|
195
|
-
root = build_audit_prompt.build_audit_prompt_xml(
|
|
196
|
-
owner="jl-cmd",
|
|
197
|
-
repo="claude-dev-env",
|
|
198
|
-
pr_number=422,
|
|
199
|
-
loop=1,
|
|
200
|
-
head_ref="feat/branch",
|
|
201
|
-
base_ref="main",
|
|
202
|
-
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
203
|
-
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
204
|
-
pr_body_text="## Summary\nCloses the gate.",
|
|
205
|
-
)
|
|
206
|
-
pr_description = root.find("pr_description")
|
|
207
|
-
assert pr_description is not None
|
|
208
|
-
assert pr_description.text == "## Summary\nCloses the gate."
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def test_pr_description_empty_when_body_absent() -> None:
|
|
212
|
-
root = _build_audit_root()
|
|
213
|
-
pr_description = root.find("pr_description")
|
|
214
|
-
assert pr_description is not None
|
|
215
|
-
assert pr_description.text is None
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def test_read_pr_body_text_returns_file_contents(tmp_path: Path) -> None:
|
|
219
|
-
body_file = tmp_path / "pr-body.md"
|
|
220
|
-
body_file.write_text("body from disk", encoding="utf-8")
|
|
221
|
-
assert build_audit_prompt.read_pr_body_text(body_file) == "body from disk"
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def test_read_pr_body_text_returns_none_when_path_absent() -> None:
|
|
225
|
-
assert build_audit_prompt.read_pr_body_text(None) is None
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def test_read_pr_body_text_returns_none_when_file_missing(tmp_path: Path) -> None:
|
|
229
|
-
missing_file = tmp_path / "does-not-exist.md"
|
|
230
|
-
assert build_audit_prompt.read_pr_body_text(missing_file) is None
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def test_emit_audit_prompt_embeds_pr_body_text_in_pr_description() -> None:
|
|
234
|
-
xml_text = build_audit_prompt.emit_audit_prompt(
|
|
235
|
-
owner="jl-cmd",
|
|
236
|
-
repo="claude-dev-env",
|
|
237
|
-
pr_number=422,
|
|
238
|
-
loop=1,
|
|
239
|
-
head_ref="feat/branch",
|
|
240
|
-
base_ref="main",
|
|
241
|
-
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
242
|
-
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
243
|
-
pr_body_text="body via emit",
|
|
244
|
-
)
|
|
245
|
-
assert "body via emit" in xml_text
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def test_parse_arguments_defaults_pr_body_file_to_none() -> None:
|
|
249
|
-
arguments = build_audit_prompt.parse_arguments([
|
|
250
|
-
"--owner", "jl-cmd",
|
|
251
|
-
"--repo", "claude-dev-env",
|
|
252
|
-
"--pr-number", "422",
|
|
253
|
-
"--loop", "1",
|
|
254
|
-
"--head-ref", "feat/branch",
|
|
255
|
-
"--base-ref", "main",
|
|
256
|
-
"--worktree-path", "/tmp/wt",
|
|
257
|
-
"--run-temp-dir", "/tmp/rt",
|
|
258
|
-
])
|
|
259
|
-
assert arguments.pr_body_file is None
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def test_parse_arguments_reads_pr_body_file_path() -> None:
|
|
263
|
-
arguments = build_audit_prompt.parse_arguments([
|
|
264
|
-
"--owner", "jl-cmd",
|
|
265
|
-
"--repo", "claude-dev-env",
|
|
266
|
-
"--pr-number", "422",
|
|
267
|
-
"--loop", "1",
|
|
268
|
-
"--head-ref", "feat/branch",
|
|
269
|
-
"--base-ref", "main",
|
|
270
|
-
"--worktree-path", "/tmp/wt",
|
|
271
|
-
"--run-temp-dir", "/tmp/rt",
|
|
272
|
-
"--pr-body-file", "/tmp/body.md",
|
|
273
|
-
])
|
|
274
|
-
assert arguments.pr_body_file == Path("/tmp/body.md")
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def test_parse_arguments_defaults_flavor_to_agent() -> None:
|
|
278
|
-
arguments = build_audit_prompt.parse_arguments([
|
|
279
|
-
"--owner", "jl-cmd",
|
|
280
|
-
"--repo", "claude-dev-env",
|
|
281
|
-
"--pr-number", "422",
|
|
282
|
-
"--loop", "1",
|
|
283
|
-
"--head-ref", "feat/branch",
|
|
284
|
-
"--base-ref", "main",
|
|
285
|
-
"--worktree-path", "/tmp/wt",
|
|
286
|
-
"--run-temp-dir", "/tmp/rt",
|
|
287
|
-
])
|
|
288
|
-
assert arguments.flavor == AUDIT_PROMPT_FLAVOR_AGENT
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def test_parse_arguments_reads_headless_flavor() -> None:
|
|
292
|
-
arguments = build_audit_prompt.parse_arguments([
|
|
293
|
-
"--owner", "jl-cmd",
|
|
294
|
-
"--repo", "claude-dev-env",
|
|
295
|
-
"--pr-number", "422",
|
|
296
|
-
"--loop", "1",
|
|
297
|
-
"--head-ref", "feat/branch",
|
|
298
|
-
"--base-ref", "main",
|
|
299
|
-
"--worktree-path", "/tmp/wt",
|
|
300
|
-
"--run-temp-dir", "/tmp/rt",
|
|
301
|
-
"--flavor", AUDIT_PROMPT_FLAVOR_HEADLESS,
|
|
302
|
-
])
|
|
303
|
-
assert arguments.flavor == AUDIT_PROMPT_FLAVOR_HEADLESS
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def test_agent_flavor_comment_posting_names_mcp_tool() -> None:
|
|
307
|
-
root = build_audit_prompt.build_audit_prompt_xml(
|
|
308
|
-
owner="jl-cmd",
|
|
309
|
-
repo="claude-dev-env",
|
|
310
|
-
pr_number=422,
|
|
311
|
-
loop=1,
|
|
312
|
-
head_ref="feat/branch",
|
|
313
|
-
base_ref="main",
|
|
314
|
-
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
315
|
-
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
316
|
-
flavor=AUDIT_PROMPT_FLAVOR_AGENT,
|
|
317
|
-
)
|
|
318
|
-
comment_posting = root.find("comment_posting")
|
|
319
|
-
assert comment_posting is not None
|
|
320
|
-
assert comment_posting.text == AUDIT_COMMENT_POSTING_AGENT_TEXT
|
|
321
|
-
output_format = root.find("output_format")
|
|
322
|
-
assert output_format is not None
|
|
323
|
-
assert output_format.text == AUDIT_OUTPUT_FORMAT_AGENT_TEXT
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def test_headless_flavor_writes_outcome_path_and_forbids_mcp() -> None:
|
|
327
|
-
worktree_path = Path("/tmp/bugteam-pr-422/worktree")
|
|
328
|
-
pr_number = 422
|
|
329
|
-
loop = 3
|
|
330
|
-
root = build_audit_prompt.build_audit_prompt_xml(
|
|
331
|
-
owner="jl-cmd",
|
|
332
|
-
repo="claude-dev-env",
|
|
333
|
-
pr_number=pr_number,
|
|
334
|
-
loop=loop,
|
|
335
|
-
head_ref="feat/branch",
|
|
336
|
-
base_ref="main",
|
|
337
|
-
worktree_path=worktree_path,
|
|
338
|
-
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
339
|
-
flavor=AUDIT_PROMPT_FLAVOR_HEADLESS,
|
|
340
|
-
)
|
|
341
|
-
comment_posting = root.find("comment_posting")
|
|
342
|
-
assert comment_posting is not None
|
|
343
|
-
assert comment_posting.text == AUDIT_COMMENT_POSTING_HEADLESS_TEXT
|
|
344
|
-
assert "MCP" not in (comment_posting.text or "")
|
|
345
|
-
assert "TaskCreate" not in (comment_posting.text or "")
|
|
346
|
-
assert "gh" in (comment_posting.text or "")
|
|
347
|
-
assert "post_audit_thread.py" in (comment_posting.text or "")
|
|
348
|
-
|
|
349
|
-
expected_outcome_path = (
|
|
350
|
-
worktree_path / OUTCOME_XML_TEMPLATE.format(number=pr_number, loop=loop)
|
|
351
|
-
).as_posix()
|
|
352
|
-
output_format = root.find("output_format")
|
|
353
|
-
assert output_format is not None
|
|
354
|
-
assert output_format.text == AUDIT_OUTPUT_FORMAT_HEADLESS_TEMPLATE.format(
|
|
355
|
-
outcome_path=expected_outcome_path,
|
|
356
|
-
)
|
|
357
|
-
assert expected_outcome_path in (output_format.text or "")
|
|
358
|
-
assert "TaskCreate" in (output_format.text or "")
|
|
359
|
-
assert "MCP" in (output_format.text or "")
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
def test_main_headless_flavor_emits_outcome_path_on_stdout(
|
|
363
|
-
capsys: pytest.CaptureFixture[str],
|
|
364
|
-
) -> None:
|
|
365
|
-
worktree_path = "/tmp/bugteam-pr-99/worktree"
|
|
366
|
-
exit_code = build_audit_prompt.main([
|
|
367
|
-
"--owner", "jl-cmd",
|
|
368
|
-
"--repo", "claude-dev-env",
|
|
369
|
-
"--pr-number", "99",
|
|
370
|
-
"--loop", "2",
|
|
371
|
-
"--head-ref", "feat/branch",
|
|
372
|
-
"--base-ref", "main",
|
|
373
|
-
"--worktree-path", worktree_path,
|
|
374
|
-
"--run-temp-dir", "/tmp/bugteam-pr-99",
|
|
375
|
-
"--flavor", AUDIT_PROMPT_FLAVOR_HEADLESS,
|
|
376
|
-
])
|
|
377
|
-
assert exit_code == 0
|
|
378
|
-
captured = capsys.readouterr()
|
|
379
|
-
expected_outcome = (
|
|
380
|
-
Path(worktree_path)
|
|
381
|
-
/ OUTCOME_XML_TEMPLATE.format(number=99, loop=2)
|
|
382
|
-
).as_posix()
|
|
383
|
-
assert expected_outcome in captured.out
|
|
384
|
-
assert "post_audit_thread.py" in captured.out
|
|
385
|
-
assert "Do not post reviews" in captured.out
|
|
386
|
-
assert "add_comment_to_pending_review" not in captured.out
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def test_code_quality_agent_requires_scoped_intake_and_evidence_gaps() -> None:
|
|
390
|
-
agent_text = _CODE_QUALITY_AGENT_PATH.read_text(encoding="utf-8")
|
|
391
|
-
|
|
392
|
-
required_phrases = (
|
|
393
|
-
"read each existing `AGENTS.md` and `CLAUDE.md`",
|
|
394
|
-
"scoped `AGENTS.md` files as the canonical",
|
|
395
|
-
"`docs/CODE_RULES.md` is a compact projection",
|
|
396
|
-
"`hooks/blocking/code_rules_enforcer.py` is hand-maintained write-time coverage",
|
|
397
|
-
"full diff",
|
|
398
|
-
"resolved base or merge-base",
|
|
399
|
-
"complete changed-file list",
|
|
400
|
-
"PR description",
|
|
401
|
-
"evidence gap",
|
|
402
|
-
"Never infer omitted content",
|
|
403
|
-
)
|
|
404
|
-
for each_phrase in required_phrases:
|
|
405
|
-
assert each_phrase in agent_text
|
|
406
|
-
|
|
407
|
-
assert "## Review intake and policy sources" in agent_text
|
|
408
|
-
assert "Open questions" in agent_text
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
_PACKAGE_ROOT = _PACKAGE_ROOT_FOR_RUBRICS
|
|
412
|
-
_CODE_QUALITY_AGENT_PATH = _PACKAGE_ROOT / ".agents" / "agents" / "code-quality-agent.md"
|
|
413
|
-
_AUDIT_CONTRACT_PATH = _PACKAGE_ROOT / "_shared" / "pr-loop" / "audit-contract.md"
|
|
414
|
-
_PRECATCH_RUBRIC_PATH = _PACKAGE_ROOT / "_shared" / "pr-loop" / "precatch-rubric.md"
|
|
415
|
-
_CATEGORY_Q_LABEL = (
|
|
416
|
-
"Cross-surface claim consistency "
|
|
417
|
-
"(terminology, PR-description claims, message-vs-guard)"
|
|
418
|
-
)
|
|
419
|
-
_CATEGORY_Q_RUBRIC_REFERENCE = (
|
|
420
|
-
"`<managed-root>/audit-rubrics/category_rubrics/"
|
|
421
|
-
"category-q-cross-surface-claims.md`"
|
|
422
|
-
" (source fallback: "
|
|
423
|
-
"`packages/claude-dev-env/audit-rubrics/category_rubrics/"
|
|
424
|
-
"category-q-cross-surface-claims.md`)"
|
|
425
|
-
)
|
|
426
|
-
_A_THROUGH_Q_PATTERN = re.compile(r"A[\u2013-]Q")
|
|
427
|
-
_SEVENTEEN_CATEGORIES_PATTERN = re.compile(r"seventeen categor", re.IGNORECASE)
|
|
428
|
-
_STALE_SIXTEEN_PATTERN = re.compile(r"sixteen categor", re.IGNORECASE)
|
|
429
|
-
_STALE_A_THROUGH_P_PATTERN = re.compile(r"A[\u2013-]P")
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def test_code_quality_agent_default_scope_is_a_through_q() -> None:
|
|
433
|
-
agent_text = _CODE_QUALITY_AGENT_PATH.read_text(encoding="utf-8")
|
|
434
|
-
assert _A_THROUGH_Q_PATTERN.search(agent_text) is not None
|
|
435
|
-
assert _SEVENTEEN_CATEGORIES_PATTERN.search(agent_text) is not None
|
|
436
|
-
assert _STALE_SIXTEEN_PATTERN.search(agent_text) is None
|
|
437
|
-
assert "A through Q" in agent_text
|
|
438
|
-
assert "A through P" not in agent_text
|
|
439
|
-
assert _STALE_A_THROUGH_P_PATTERN.search(agent_text) is None
|
|
440
|
-
assert "## Bug Categories A–Q" in agent_text
|
|
441
|
-
assert _CATEGORY_Q_LABEL in agent_text
|
|
442
|
-
assert _CATEGORY_Q_RUBRIC_REFERENCE in agent_text
|
|
443
|
-
assert "| Q |" in agent_text
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
def test_code_quality_agent_contract_is_caller_neutral() -> None:
|
|
447
|
-
agent_text = _CODE_QUALITY_AGENT_PATH.read_text(encoding="utf-8")
|
|
448
|
-
stale_terms = (
|
|
449
|
-
"PR #394",
|
|
450
|
-
"PR #397",
|
|
451
|
-
"May 2026",
|
|
452
|
-
"r3210166636",
|
|
453
|
-
"/bugteam",
|
|
454
|
-
"/pr-converge",
|
|
455
|
-
"/autoconverge",
|
|
456
|
-
"opus",
|
|
457
|
-
"sonnet",
|
|
458
|
-
"model:",
|
|
459
|
-
)
|
|
460
|
-
for stale_term in stale_terms:
|
|
461
|
-
assert stale_term not in agent_text
|
|
462
|
-
assert "The caller provides the diff, audit scope, ID prefix, and output format." in agent_text
|
|
463
|
-
assert "Do not assume a model, caller name, or persistence path." in agent_text
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
def test_audit_contract_category_schema_includes_q() -> None:
|
|
467
|
-
contract_text = _AUDIT_CONTRACT_PATH.read_text(encoding="utf-8")
|
|
468
|
-
assert '"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q"' in (
|
|
469
|
-
contract_text
|
|
470
|
-
)
|
|
471
|
-
assert _A_THROUGH_Q_PATTERN.search(contract_text) is not None
|
|
472
|
-
assert _STALE_A_THROUGH_P_PATTERN.search(contract_text) is None
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
def test_precatch_rubric_adversarial_lane_uses_a_through_q() -> None:
|
|
476
|
-
rubric_text = _PRECATCH_RUBRIC_PATH.read_text(encoding="utf-8")
|
|
477
|
-
assert _A_THROUGH_Q_PATTERN.search(rubric_text) is not None
|
|
478
|
-
assert _STALE_A_THROUGH_P_PATTERN.search(rubric_text) is None
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
def test_audit_prompt_constraints_require_collection_before_severity_filter() -> None:
|
|
482
|
-
root = _build_audit_root()
|
|
483
|
-
constraints = root.find("constraints")
|
|
484
|
-
assert constraints is not None
|
|
485
|
-
all_constraint_texts = [
|
|
486
|
-
each_constraint.text or "" for each_constraint in constraints
|
|
487
|
-
]
|
|
488
|
-
joined = "\n".join(all_constraint_texts)
|
|
489
|
-
assert "Report every real finding" in joined
|
|
490
|
-
assert "Do not drop findings by severity during collection" in joined
|
|
491
|
-
assert "separate consumer stage" in joined
|
|
492
|
-
assert "P0" in joined and "P1" in joined and "P2" in joined
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def test_audit_contract_documents_collection_before_severity_filtering() -> None:
|
|
496
|
-
contract_text = _AUDIT_CONTRACT_PATH.read_text(encoding="utf-8")
|
|
497
|
-
assert "## Collection before severity filtering" in contract_text
|
|
498
|
-
assert "separate consumer stage" in contract_text
|
|
499
|
-
assert "Do not drop a real finding because its severity is low" in contract_text
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
"""Tests for build_converge_task_list — runnable gates and final CLEAN task."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import subprocess
|
|
7
|
-
import sys
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
11
|
-
if str(_SCRIPTS_DIR) not in sys.path:
|
|
12
|
-
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
13
|
-
|
|
14
|
-
import build_converge_task_list as task_list_module # noqa: E402
|
|
15
|
-
from skills_pr_loop_constants.converge_task_list_constants import ( # noqa: E402
|
|
16
|
-
EXIT_SUCCESS,
|
|
17
|
-
RESULT_KEY_DONE_WHEN,
|
|
18
|
-
RESULT_KEY_FINAL_TASK_ID,
|
|
19
|
-
RESULT_KEY_RUNNABLE_REVIEW_IDS,
|
|
20
|
-
RESULT_KEY_SKIPPED_REVIEW_IDS,
|
|
21
|
-
RESULT_KEY_TASKS,
|
|
22
|
-
SKIP_REASON_BUGBOT_DOWN,
|
|
23
|
-
SKIP_REASON_CODEX_NOT_REQUIRED,
|
|
24
|
-
SKIP_REASON_COPILOT_DOWN,
|
|
25
|
-
TASK_FIELD_ID,
|
|
26
|
-
TASK_FIELD_IS_RUNNABLE,
|
|
27
|
-
TASK_FIELD_KIND,
|
|
28
|
-
TASK_ID_ALL_CLEAN_SAME_HEAD,
|
|
29
|
-
TASK_ID_BUGBOT,
|
|
30
|
-
TASK_ID_BUGTEAM,
|
|
31
|
-
TASK_ID_CODE_REVIEW,
|
|
32
|
-
TASK_ID_CODEX,
|
|
33
|
-
TASK_ID_COPILOT,
|
|
34
|
-
TASK_KIND_FINAL,
|
|
35
|
-
TASK_KIND_REVIEW,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
SCRIPT_PATH = _SCRIPTS_DIR / "build_converge_task_list.py"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_default_flags_code_review_and_bugteam_runnable_final_last() -> None:
|
|
42
|
-
payload = task_list_module.build_converge_task_list(
|
|
43
|
-
is_bugbot_down=False,
|
|
44
|
-
is_copilot_down=False,
|
|
45
|
-
is_codex_down=False,
|
|
46
|
-
is_codex_required=False,
|
|
47
|
-
)
|
|
48
|
-
all_tasks = payload[RESULT_KEY_TASKS]
|
|
49
|
-
assert isinstance(all_tasks, list)
|
|
50
|
-
assert all_tasks[0][TASK_FIELD_ID] == TASK_ID_CODE_REVIEW
|
|
51
|
-
assert all_tasks[1][TASK_FIELD_ID] == TASK_ID_BUGTEAM
|
|
52
|
-
assert all_tasks[-1][TASK_FIELD_ID] == TASK_ID_ALL_CLEAN_SAME_HEAD
|
|
53
|
-
assert all_tasks[-1][TASK_FIELD_KIND] == TASK_KIND_FINAL
|
|
54
|
-
assert payload[RESULT_KEY_FINAL_TASK_ID] == TASK_ID_ALL_CLEAN_SAME_HEAD
|
|
55
|
-
assert TASK_ID_CODE_REVIEW in payload[RESULT_KEY_RUNNABLE_REVIEW_IDS]
|
|
56
|
-
assert TASK_ID_BUGTEAM in payload[RESULT_KEY_RUNNABLE_REVIEW_IDS]
|
|
57
|
-
assert TASK_ID_BUGBOT in payload[RESULT_KEY_RUNNABLE_REVIEW_IDS]
|
|
58
|
-
assert TASK_ID_COPILOT in payload[RESULT_KEY_RUNNABLE_REVIEW_IDS]
|
|
59
|
-
assert TASK_ID_CODEX in payload[RESULT_KEY_SKIPPED_REVIEW_IDS]
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def test_down_flags_skip_external_reviews() -> None:
|
|
63
|
-
payload = task_list_module.build_converge_task_list(
|
|
64
|
-
is_bugbot_down=True,
|
|
65
|
-
is_copilot_down=True,
|
|
66
|
-
is_codex_down=True,
|
|
67
|
-
is_codex_required=True,
|
|
68
|
-
)
|
|
69
|
-
all_tasks = payload[RESULT_KEY_TASKS]
|
|
70
|
-
assert isinstance(all_tasks, list)
|
|
71
|
-
all_tasks_by_id = {
|
|
72
|
-
each_task[TASK_FIELD_ID]: each_task for each_task in all_tasks
|
|
73
|
-
}
|
|
74
|
-
assert all_tasks_by_id[TASK_ID_BUGBOT][TASK_FIELD_IS_RUNNABLE] is False
|
|
75
|
-
assert all_tasks_by_id[TASK_ID_COPILOT][TASK_FIELD_IS_RUNNABLE] is False
|
|
76
|
-
assert all_tasks_by_id[TASK_ID_CODEX][TASK_FIELD_IS_RUNNABLE] is False
|
|
77
|
-
assert payload[RESULT_KEY_RUNNABLE_REVIEW_IDS] == [
|
|
78
|
-
TASK_ID_CODE_REVIEW,
|
|
79
|
-
TASK_ID_BUGTEAM,
|
|
80
|
-
]
|
|
81
|
-
assert SKIP_REASON_BUGBOT_DOWN in str(all_tasks_by_id[TASK_ID_BUGBOT])
|
|
82
|
-
assert SKIP_REASON_COPILOT_DOWN in str(all_tasks_by_id[TASK_ID_COPILOT])
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def test_codex_required_is_runnable() -> None:
|
|
86
|
-
payload = task_list_module.build_converge_task_list(
|
|
87
|
-
is_bugbot_down=True,
|
|
88
|
-
is_copilot_down=True,
|
|
89
|
-
is_codex_down=False,
|
|
90
|
-
is_codex_required=True,
|
|
91
|
-
)
|
|
92
|
-
assert TASK_ID_CODEX in payload[RESULT_KEY_RUNNABLE_REVIEW_IDS]
|
|
93
|
-
assert SKIP_REASON_CODEX_NOT_REQUIRED not in str(payload)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def test_final_task_always_last_and_runnable() -> None:
|
|
97
|
-
payload = task_list_module.build_converge_task_list(
|
|
98
|
-
is_bugbot_down=True,
|
|
99
|
-
is_copilot_down=False,
|
|
100
|
-
is_codex_down=False,
|
|
101
|
-
is_codex_required=True,
|
|
102
|
-
)
|
|
103
|
-
all_tasks = payload[RESULT_KEY_TASKS]
|
|
104
|
-
assert isinstance(all_tasks, list)
|
|
105
|
-
final_task = all_tasks[-1]
|
|
106
|
-
assert final_task[TASK_FIELD_ID] == TASK_ID_ALL_CLEAN_SAME_HEAD
|
|
107
|
-
assert final_task[TASK_FIELD_IS_RUNNABLE] is True
|
|
108
|
-
assert final_task[TASK_FIELD_KIND] == TASK_KIND_FINAL
|
|
109
|
-
assert payload[RESULT_KEY_DONE_WHEN]
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def test_cli_prints_json() -> None:
|
|
113
|
-
completed = subprocess.run(
|
|
114
|
-
[
|
|
115
|
-
sys.executable,
|
|
116
|
-
str(SCRIPT_PATH),
|
|
117
|
-
"--bugbot-down",
|
|
118
|
-
"1",
|
|
119
|
-
"--copilot-down",
|
|
120
|
-
"0",
|
|
121
|
-
"--codex-required",
|
|
122
|
-
"0",
|
|
123
|
-
],
|
|
124
|
-
check=False,
|
|
125
|
-
capture_output=True,
|
|
126
|
-
text=True,
|
|
127
|
-
)
|
|
128
|
-
assert completed.returncode == EXIT_SUCCESS, completed.stderr
|
|
129
|
-
payload = json.loads(completed.stdout)
|
|
130
|
-
assert payload[RESULT_KEY_FINAL_TASK_ID] == TASK_ID_ALL_CLEAN_SAME_HEAD
|
|
131
|
-
assert TASK_ID_BUGBOT in payload[RESULT_KEY_SKIPPED_REVIEW_IDS]
|
|
132
|
-
assert any(
|
|
133
|
-
each_task[TASK_FIELD_KIND] == TASK_KIND_REVIEW
|
|
134
|
-
for each_task in payload[RESULT_KEY_TASKS]
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def test_main_returns_usage_on_bad_bool() -> None:
|
|
139
|
-
exit_code = task_list_module.main(["--bugbot-down", "maybe"])
|
|
140
|
-
assert exit_code == task_list_module.EXIT_USAGE_ERROR
|