claude-dev-env 2.3.0 → 2.5.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/CLAUDE.md +53 -48
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
- package/_shared/pr-loop/worker-spawn.md +1 -1
- package/agents/CLAUDE.md +3 -1
- package/agents/caveman.md +0 -1
- package/agents/clasp-deployment-orchestrator.md +0 -1
- package/agents/clean-coder.md +0 -1
- package/agents/code-advisor.md +0 -1
- package/agents/code-quality-agent.md +1 -2
- package/agents/code-verifier.md +3 -4
- package/agents/deep-research.md +0 -1
- package/agents/docs-agent.md +0 -1
- package/agents/git-commit-crafter.md +0 -1
- package/agents/issue-tracker.md +42 -0
- package/agents/plan-packet-validator.md +0 -1
- package/agents/pr-description-writer.md +0 -1
- package/agents/skill-writer-agent.md +84 -0
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +105 -3
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +29 -13
- package/bin/CLAUDE.md +68 -5
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +88 -0
- package/bin/install.mjs +1138 -114
- package/bin/install.prune.test.mjs +869 -19
- package/bin/install.test.mjs +906 -2
- package/commands/implement.md +1 -1
- package/commands/right-size.md +1 -1
- package/docs/CLAUDE.md +2 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/references/CLAUDE.md +5 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +107 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/docs/wsl-docker-cowork-starter-matrix.md +89 -0
- package/hooks/blocking/CLAUDE.md +9 -1
- package/hooks/blocking/code_review_enforcement_config_bootstrap.py +53 -0
- package/hooks/blocking/code_review_gate_deny.py +74 -0
- package/hooks/blocking/code_review_pr_create_gate.py +198 -0
- package/hooks/blocking/code_review_push_gate.py +145 -0
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +348 -0
- package/hooks/blocking/code_review_stamp_store.py +233 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +7 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +15 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +212 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +138 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +49 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +38 -0
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +39 -27
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -155
- package/hooks/blocking/conftest.py +2 -0
- package/hooks/blocking/convergence_gate_blocker.py +112 -23
- package/hooks/blocking/destructive_command_blocker.py +19 -6
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pr_description_proof_of_work.py +52 -34
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +4 -1
- package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +62 -0
- package/hooks/blocking/test_code_review_gate_deny.py +54 -0
- package/hooks/blocking/test_code_review_pr_create_gate.py +199 -0
- package/hooks/blocking/test_code_review_push_gate.py +205 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +199 -0
- package/hooks/blocking/test_code_review_stamp_store.py +205 -0
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +124 -2
- package/hooks/blocking/test_convergence_gate_blocker.py +153 -5
- package/hooks/blocking/test_destructive_command_blocker.py +1 -1
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +45 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pr_description_proof_of_work.py +151 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -8
- package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
- package/hooks/blocking/test_verification_verdict_store.py +974 -903
- package/hooks/blocking/test_verified_commit_gate.py +581 -581
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +114 -2
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1014
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +29 -17
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +35 -0
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/blocking/volatile_path_in_post_blocker.py +69 -8
- package/hooks/git-hooks/git_hooks_constants/__init__.py +6 -0
- package/hooks/git-hooks/pre_push.py +89 -2
- package/hooks/git-hooks/test_pre_push.py +128 -0
- package/hooks/hooks.json +26 -1
- package/hooks/hooks_constants/CLAUDE.md +3 -1
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +8 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +26 -11
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +20 -3
- package/hooks/hooks_constants/destructive_command_segment_constants.py +3 -1
- package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
- package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/pyproject_config_discovery_constants.py +16 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +24 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +6 -0
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +8 -1
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
- package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
- package/hooks/pyproject.toml +1 -0
- package/hooks/validators/CLAUDE.md +2 -0
- package/hooks/validators/config/__init__.py +0 -0
- package/hooks/validators/config/directory_exemption_constants.py +183 -0
- package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
- package/hooks/validators/conftest.py +4 -0
- package/hooks/validators/mypy_integration.py +63 -50
- package/hooks/validators/pyproject_config_discovery.py +101 -0
- package/hooks/validators/ruff_integration.py +257 -25
- package/hooks/validators/run_all_validators.py +223 -19
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_mypy_integration.py +32 -0
- package/hooks/validators/test_pyproject_config_discovery.py +94 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +160 -2
- package/hooks/validators/test_run_all_validators.py +140 -68
- package/hooks/validators/test_run_all_validators_config_discovery.py +123 -0
- package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
- package/package.json +10 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/docstring-prose-matches-implementation.md +45 -67
- package/rules/durable-post-artifacts.md +7 -0
- package/rules/state-what-is.md +25 -0
- package/rules/verified-commit-gate-skip.md +1 -1
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Capture-PoolHealth.ps1 +410 -0
- package/scripts/_code_review_test_support.py +404 -0
- package/scripts/claude_chain_runner.py +141 -1
- package/scripts/codec_forwarding_test_support.py +83 -0
- package/scripts/conftest.py +23 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -2
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +53 -1
- package/scripts/dev_env_scripts_constants/code_review_constants.py +129 -12
- package/scripts/dev_env_scripts_constants/test_code_review_constants.py +55 -0
- package/scripts/invoke_code_review.py +550 -38
- package/scripts/resolve_worker_spawn.py +626 -619
- package/scripts/spawn_grok_batch.py +672 -672
- package/scripts/test_claude_chain_runner.py +131 -0
- package/scripts/test_invoke_code_review_chain.py +70 -0
- package/scripts/test_invoke_code_review_cli.py +192 -0
- package/scripts/test_invoke_code_review_codec.py +77 -0
- package/scripts/test_invoke_code_review_contract.py +256 -0
- package/scripts/test_invoke_code_review_git.py +123 -0
- package/scripts/test_invoke_code_review_mode.py +99 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -1014
- package/scripts/test_resolve_worker_spawn_codec.py +101 -0
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -421
- package/skills/autoconverge/reference/convergence.md +26 -4
- package/skills/autoconverge/reference/multi-pr.md +6 -1
- package/skills/autoconverge/reference/stop-conditions.md +16 -10
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +4 -4
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +175 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1244
- package/skills/autoconverge/workflow/converge.mjs +191 -8
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/converge_multi.mjs +7 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +5 -0
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
- package/skills/closeout/SKILL.md +33 -50
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
- package/skills/codex-review/scripts/run_codex_review.py +233 -1
- package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
- package/skills/condensing-instructions/SKILL.md +81 -0
- package/skills/copilot-review/SKILL.md +119 -119
- package/skills/e-code-review/SKILL.md +52 -0
- package/skills/e-code-review/reference/fix.md +54 -0
- package/skills/e-code-review/reference/loop.md +43 -0
- package/skills/e-code-review/reference/low.md +57 -0
- package/skills/e-code-review/reference/medium.md +153 -0
- package/skills/e-code-review/reference/xhigh.md +182 -0
- package/skills/e-simplify/SKILL.md +97 -0
- package/skills/fresh-branch/CLAUDE.md +2 -0
- package/skills/fresh-branch/SKILL.md +2 -0
- package/skills/fresh-branch/scripts/create_fresh_branch.py +78 -180
- package/skills/fresh-branch/scripts/fresh_branch_git_commands.py +285 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -0
- package/skills/fresh-branch/scripts/pytest.ini +4 -0
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +98 -0
- package/skills/fresh-branch/scripts/test_fresh_branch_git_commands.py +310 -0
- package/skills/issue-tracker/SKILL.md +92 -0
- package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
- package/skills/issue-tracker/reference/handoff-schema.md +64 -0
- package/skills/issue-tracker/reference/operation-matrix.md +41 -0
- package/skills/orchestrator/SKILL.md +162 -21
- package/skills/orchestrator/scripts/status_gate.py +625 -0
- package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
- package/skills/orchestrator/scripts/test_status_gate.py +439 -0
- package/skills/orchestrator-refresh/SKILL.md +110 -35
- package/skills/plan-to-pr/SKILL.md +155 -0
- package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
- package/skills/plan-to-pr/reference/model-routing.md +36 -0
- package/skills/plan-to-pr/reference/packet-contract.md +43 -0
- package/skills/plan-to-pr/reference/packet-schema.json +57 -0
- package/skills/plan-to-pr/reference/process-inventory.md +22 -0
- package/skills/plan-to-pr/reference/review-loop.md +33 -0
- package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
- package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
- package/skills/plan-to-pr/reference/task-seeds.md +14 -0
- package/skills/plan-to-pr/reference/task-ticket.md +38 -0
- package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
- package/skills/plan-to-pr/scripts/config/constants.py +193 -0
- package/skills/plan-to-pr/scripts/create_packet.py +173 -0
- package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
- package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
- package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
- package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
- package/skills/plan-to-pr/scripts/validate_run.py +173 -0
- package/skills/plan-to-pr/test_skill_contract.py +207 -0
- package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
- package/skills/pr-converge/SKILL.md +472 -469
- package/skills/pr-converge/reference/examples.md +3 -3
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +7 -4
- package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
- package/skills/pr-converge/reference/per-tick.md +5 -5
- package/skills/pr-converge/reference/progress-checklist.md +1 -1
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
- package/skills/pr-converge/test_step5_host_branch.py +1 -1
- package/skills/pr-fix-protocol/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -68
- package/skills/prototype/SKILL.md +86 -0
- package/skills/prototype/reference/honest-limitations.md +23 -0
- package/skills/prototype/reference/promotion-tasks.md +23 -0
- package/skills/prototype/scripts/build_sandbox_settings.py +249 -0
- package/skills/prototype/scripts/conftest.py +15 -0
- package/skills/prototype/scripts/launch_sandbox.py +205 -0
- package/skills/prototype/scripts/probe_sandbox_safety.py +311 -0
- package/skills/prototype/scripts/prototype_scripts_constants/__init__.py +1 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +41 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +23 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +45 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +10 -0
- package/skills/prototype/scripts/test_build_sandbox_settings.py +275 -0
- package/skills/prototype/scripts/test_launch_sandbox.py +303 -0
- package/skills/prototype/scripts/test_probe_sandbox_safety.py +284 -0
- package/skills/prototype/workflows/promotion.md +27 -0
- package/skills/prototype/workflows/sandbox.md +35 -0
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/skill-builder/CLAUDE.md +3 -3
- package/skills/skill-builder/SKILL.md +5 -5
- package/skills/skill-builder/references/CLAUDE.md +1 -1
- package/skills/skill-builder/references/delegation-map.md +3 -3
- package/skills/skill-builder/references/description-field.md +1 -1
- package/skills/skill-builder/references/skill-modularity.md +2 -3
- package/skills/skill-builder/workflows/CLAUDE.md +1 -1
- package/skills/skill-builder/workflows/improve-skill.md +1 -1
- package/skills/skill-builder/workflows/new-skill.md +2 -2
- package/skills/task-build/CLAUDE.md +8 -7
- package/skills/task-build/SKILL.md +16 -8
- package/skills/task-build/reference/tool-routing.md +19 -0
- package/skills/team-advisor/SKILL.md +2 -2
- package/scripts/test_invoke_code_review.py +0 -672
- package/skills/closeout/reference/issue-body-templates.md +0 -108
|
@@ -106,6 +106,18 @@ follow-up issue listing the findings, opens a draft environment-hardening PR
|
|
|
106
106
|
(hooks/rules that block those violation classes at Write/Edit time), resolves
|
|
107
107
|
any bot threads with a deferral note, and reports the deferral in
|
|
108
108
|
`standardsNote`. The hardening PRs land in the run result's `deferredPrs` list.
|
|
109
|
+
Standards-only deferral still takes precedence when every finding is category
|
|
110
|
+
`code-standard`.
|
|
111
|
+
|
|
112
|
+
When the already-deduped findings for a phase are all severity P2 (and not
|
|
113
|
+
standards-only), the phase still applies the fixes once. If the fix does **not**
|
|
114
|
+
move HEAD (for example `resolvedWithoutCommit`), the phase treats that HEAD as
|
|
115
|
+
clean and advances forward. If the fix **does** move HEAD, the run re-enters
|
|
116
|
+
CONVERGE on the fixed SHA so HEAD-bound CLEAN/review stamps rebuild before
|
|
117
|
+
FINALIZE — those stamps are SHA-specific. The three CONVERGE reading lenses stay
|
|
118
|
+
parallel; the P2-only decision is a routing branch on the deduped set after
|
|
119
|
+
`resolveRoundOutcome` / gate classification. Mixed P0/P1 findings keep the
|
|
120
|
+
existing fix + re-converge path.
|
|
109
121
|
|
|
110
122
|
**BUGBOT** gate (terminal external confirmation):
|
|
111
123
|
|
|
@@ -115,7 +127,10 @@ any bot threads with a deferral note, and reports the deferral in
|
|
|
115
127
|
to the Copilot gate with `bugbotDown` set.
|
|
116
128
|
- Otherwise drive Cursor Bugbot to a verdict on HEAD (trigger and poll its CI
|
|
117
129
|
check run when needed).
|
|
118
|
-
- Bugbot findings
|
|
130
|
+
- Bugbot findings that are not standards-only and not P2-only → fix them and
|
|
131
|
+
return to CONVERGE on the new HEAD.
|
|
132
|
+
- Bugbot P2-only findings → fix once; advance to Copilot only when HEAD is
|
|
133
|
+
unchanged, otherwise re-converge on the fixed HEAD.
|
|
119
134
|
- Bugbot clean or approved → move to the Copilot gate.
|
|
120
135
|
- Bugbot down after the poll cap → move to the Copilot gate with `bugbotDown` set.
|
|
121
136
|
|
|
@@ -123,7 +138,10 @@ any bot threads with a deferral note, and reports the deferral in
|
|
|
123
138
|
|
|
124
139
|
- Request a Copilot review on HEAD (skipping a duplicate request), then poll up
|
|
125
140
|
to the configured cap, 360 seconds apart.
|
|
126
|
-
- Copilot findings
|
|
141
|
+
- Copilot findings that are not standards-only and not P2-only → fix them and
|
|
142
|
+
return to CONVERGE on the new HEAD.
|
|
143
|
+
- Copilot P2-only findings → fix once; advance to Codex only when HEAD is
|
|
144
|
+
unchanged, otherwise re-converge on the fixed HEAD.
|
|
127
145
|
- Copilot clean or approved → move to the Codex gate.
|
|
128
146
|
- Copilot down or out of quota (an out-of-usage notice, or no review after the
|
|
129
147
|
configured cap) → log a notice and move to the Codex gate with the Copilot gate
|
|
@@ -135,11 +153,15 @@ any bot threads with a deferral note, and reports the deferral in
|
|
|
135
153
|
the weekly usage probe via `is_codex_review_required` (shared threshold
|
|
136
154
|
constant — no inline percent), and the wrapper's `codex_down` class.
|
|
137
155
|
- Opt-out token or `codex_down` → set `codexDown`, no stamp, move to the
|
|
138
|
-
convergence check with `--codex-down`.
|
|
156
|
+
convergence check with `--codex-down`. A `codex_down` classification also
|
|
157
|
+
records a `codexNote` so the skip stays visible in the final report.
|
|
139
158
|
- Usage at/below threshold or null → skip with no stamp; the machine checklist
|
|
140
159
|
applies the same rule.
|
|
141
160
|
- Above threshold → run the codex-review wrapper against the PR base branch.
|
|
142
|
-
Non–code-standard findings → fix and return to CONVERGE.
|
|
161
|
+
Non–code-standard, non–P2-only findings → fix and return to CONVERGE.
|
|
162
|
+
P2-only findings → fix once; when HEAD is unchanged, stamp `codexCleanAt` and
|
|
163
|
+
move to the convergence check (no re-converge), otherwise re-converge on the
|
|
164
|
+
fixed HEAD.
|
|
143
165
|
Standards-only findings → defer a follow-up, stamp `codexCleanAt`, and move to the
|
|
144
166
|
convergence check (no fix push).
|
|
145
167
|
Clean → stamp `codexCleanAt` and pass `--codex-clean-at` into the convergence check.
|
|
@@ -72,6 +72,7 @@ Workflow({
|
|
|
72
72
|
scriptPath: "<this skill dir>/workflow/converge_multi.mjs",
|
|
73
73
|
args: {
|
|
74
74
|
convergeScriptPath: "<this skill dir>/workflow/converge.mjs",
|
|
75
|
+
homeDirectory: "<HOME>",
|
|
75
76
|
prs: [
|
|
76
77
|
{ owner: "<O>", repo: "<R>", prNumber: <N1>, repoPath: "<abs worktree 1>", bugbotDisabled: false, copilotDisabled: false },
|
|
77
78
|
{ owner: "<O>", repo: "<R>", prNumber: <N2>, repoPath: "<abs worktree 2>", bugbotDisabled: false, copilotDisabled: false }
|
|
@@ -82,7 +83,11 @@ Workflow({
|
|
|
82
83
|
|
|
83
84
|
`convergeScriptPath` is the absolute path to `workflow/converge.mjs` in this same
|
|
84
85
|
skill directory; each `repoPath` is the absolute path of the worktree that PR is
|
|
85
|
-
checked out in.
|
|
86
|
+
checked out in. `homeDirectory` is the absolute path to the directory that holds
|
|
87
|
+
`.claude` (resolve it once from `$HOME`, or `$env:USERPROFILE` on Windows, with
|
|
88
|
+
forward slashes); the fan-out forwards it to every child run, which needs it to
|
|
89
|
+
build the codex-review scripts path because the workflow sandbox has no access to
|
|
90
|
+
environment variables. The workflow runs in the background and notifies the session on
|
|
86
91
|
completion; watch live progress with `/workflows`, where each PR's child run
|
|
87
92
|
appears under its own group.
|
|
88
93
|
|
|
@@ -88,16 +88,22 @@ Each reviewer-down condition below skips its own convergence-check gate. The fla
|
|
|
88
88
|
`--copilot-down` (the Copilot review gate and the pending-requested-reviews
|
|
89
89
|
gate bypassed), then mark-ready. `copilotNote` records the bypass for the
|
|
90
90
|
final report.
|
|
91
|
-
- **Terminal Codex gate skipped or
|
|
92
|
-
runs after Bugbot and Copilot. When
|
|
93
|
-
(`reviews_disabled.py --reviewer codex`
|
|
94
|
-
advances with no review. When the weekly
|
|
95
|
-
null or at/below the shared threshold
|
|
96
|
-
gate skips without a stamp; the
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
- **Terminal Codex gate skipped (opt-out, usage, or `codex_down`)** — the
|
|
92
|
+
conditional-required Codex gate runs after Bugbot and Copilot. When
|
|
93
|
+
`CLAUDE_REVIEWS_DISABLED` lists `codex` (`reviews_disabled.py --reviewer codex`
|
|
94
|
+
exit 0), the gate sets `codexDown` and advances with no review. When the weekly
|
|
95
|
+
usage probe reports `percent_left` null or at/below the shared threshold
|
|
96
|
+
(`is_codex_review_required` false), the gate skips without a stamp; the
|
|
97
|
+
convergence check applies the same rule. When the codex-review wrapper
|
|
98
|
+
classifies `codex_down` — a Codex CLI error, or a review that ran past its
|
|
99
|
+
`CONFIG.codexReviewTimeoutSeconds` budget — the gate skips the same way: it sets
|
|
100
|
+
`codexDown`, records a `codexNote` so the skip stays visible in the final
|
|
101
|
+
report, clears `codexCleanAt`, and advances to `FINALIZE` (mark-ready with the
|
|
102
|
+
gate bypassed) so a Codex outage does not block convergence; Codex runs again on
|
|
103
|
+
the next convergence. A clean required run stamps `codexCleanAt` for
|
|
104
|
+
`--codex-clean-at`. Non-code-standard findings re-enter `CONVERGE` through the
|
|
105
|
+
existing fix path. Standards-only findings defer a follow-up, stamp
|
|
106
|
+
`codexCleanAt`, and advance to `FINALIZE` (no fix push).
|
|
101
107
|
- **A lens agent dies** — when one parallel reading lens returns null (a terminal
|
|
102
108
|
agent failure), the round proceeds on the surviving lenses. A real defect it
|
|
103
109
|
would have caught surfaces in a later round or at the convergence check. A dead
|
|
@@ -18,6 +18,7 @@ Workflow scripts and report utilities for the `autoconverge` skill.
|
|
|
18
18
|
| `converge.precatch.test.mjs` | Tests the pre-catch stage: static-sweep ordering and gate coverage, the adversarial second pass, the self-review lens, the terminal Bugbot gate, the read-only/edit preamble split, and the commit-recovery cap. |
|
|
19
19
|
| `converge.fix-progress.test.mjs` | Tests fix-progress tracking across rounds. |
|
|
20
20
|
| `converge.fix-recovery.test.mjs` | Tests recovery when a fix commit fails. |
|
|
21
|
+
| `converge.p2-advance.test.mjs` | Tests P2-only fix-then-advance helpers and phase routing. |
|
|
21
22
|
| `converge.run-input.test.mjs` | Tests workflow input validation. |
|
|
22
23
|
| `test_aggregate_runs.py` | Tests for `aggregate_runs.py`. |
|
|
23
24
|
| `test_convergence_summary.py` | Tests for `convergence_summary.py`. |
|
|
@@ -215,7 +215,7 @@ test('every no-lens-reviewed round registers one shared counter and every lens-r
|
|
|
215
215
|
const registerSites = loopBody.match(/registerNoLensRound\(/g) || [];
|
|
216
216
|
assert.equal(registerSites.length, 3, 'expected the preflight-no-SHA, all-lenses-dead, and no-lens-ran rounds to register');
|
|
217
217
|
const resetSites = loopBody.match(/resetNoLensRounds\(\)/g) || [];
|
|
218
|
-
assert.equal(resetSites.length,
|
|
218
|
+
assert.equal(resetSites.length, 6, 'expected a reset on the static-sweep, standards, P2-only, findings, not-clean, and posted lens-ran rounds');
|
|
219
219
|
const deadBranch = convergeSource.slice(
|
|
220
220
|
convergeSource.indexOf('if (roundOutcome.allLensesDead) {'),
|
|
221
221
|
convergeSource.indexOf('const findings = roundOutcome.findings'),
|
|
@@ -533,7 +533,7 @@ test('CLEAN_AUDIT_SCHEMA requires posted, reviewUrl, and reason', () => {
|
|
|
533
533
|
test('the standards-only call site records a clean-audit bypass and continues to BUGBOT when the post does not land', () => {
|
|
534
534
|
const branch = convergeSource.slice(
|
|
535
535
|
convergeSource.indexOf('if (isStandardsOnlyRound(findings)) {'),
|
|
536
|
-
convergeSource.indexOf('if (findings
|
|
536
|
+
convergeSource.indexOf('if (isP2OnlyFindings(findings)) {'),
|
|
537
537
|
);
|
|
538
538
|
assert.match(branch, /runGeneralUtilityTask\(.*'post-clean-audit'/);
|
|
539
539
|
assert.match(branch, /cleanAuditNote = resolveCleanAuditNote\(auditResult, head, rounds\)/);
|
|
@@ -545,7 +545,7 @@ test('the standards-only call site records a clean-audit bypass and continues to
|
|
|
545
545
|
test('the standards-only call site relays lens provenance, the deferred standards findings, and the deferral filing state', () => {
|
|
546
546
|
const branch = convergeSource.slice(
|
|
547
547
|
convergeSource.indexOf('if (isStandardsOnlyRound(findings)) {'),
|
|
548
|
-
convergeSource.indexOf('if (findings
|
|
548
|
+
convergeSource.indexOf('if (isP2OnlyFindings(findings)) {'),
|
|
549
549
|
);
|
|
550
550
|
assert.match(branch, /const namedLenses = nameLensResults\(lenses\)/);
|
|
551
551
|
assert.match(branch, /lensResults: namedLenses/);
|
|
@@ -655,7 +655,7 @@ test('the workflow declares cleanAuditNote and assembles it into every result vi
|
|
|
655
655
|
assert.match(convergeSource, /let cleanAuditNote = null/);
|
|
656
656
|
assert.match(
|
|
657
657
|
convergeSource,
|
|
658
|
-
/const assembleResult = \(outcomeFields\) => \(\{\n\s*\.\.\.outcomeFields,\n\s*standardsNote,\n\s*copilotNote,\n\s*cleanAuditNote,\n\s*reuseNote,\n\s*deferredPrs,\n\s*\}\)/,
|
|
658
|
+
/const assembleResult = \(outcomeFields\) => \(\{\n\s*\.\.\.outcomeFields,\n\s*standardsNote,\n\s*copilotNote,\n\s*codexNote,\n\s*cleanAuditNote,\n\s*reuseNote,\n\s*deferredPrs,\n\s*\}\)/,
|
|
659
659
|
);
|
|
660
660
|
const assembleCalls = convergeSource.match(/return assembleResult\(/g) || [];
|
|
661
661
|
assert.ok(
|
|
@@ -178,7 +178,118 @@ test('the Codex gate prompt runs the review wrapper against the PR base branch',
|
|
|
178
178
|
assert.match(codexPrompt, /parse_codex_findings/, 'expected findings to come from the shared parser');
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
test('the
|
|
181
|
+
test('the Codex gate prompt passes an explicit generous timeout so a slow-but-healthy review is not cut short', () => {
|
|
182
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
183
|
+
assert.match(
|
|
184
|
+
codexPrompt,
|
|
185
|
+
/timeout_seconds=\$\{CONFIG\.codexReviewTimeoutSeconds\}/,
|
|
186
|
+
'expected the driver to pass CONFIG.codexReviewTimeoutSeconds, overriding the too-small wrapper default',
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('CONFIG.codexReviewTimeoutSeconds exceeds the observed real Codex runtime', () => {
|
|
191
|
+
const match = convergeSource.match(/codexReviewTimeoutSeconds:\s*(\d+)/);
|
|
192
|
+
assert.notEqual(match, null, 'expected CONFIG.codexReviewTimeoutSeconds to be defined');
|
|
193
|
+
const budgetSeconds = Number(match[1]);
|
|
194
|
+
assert.ok(
|
|
195
|
+
budgetSeconds >= 900,
|
|
196
|
+
`expected a budget comfortably above the ~683s measured Codex runtime, got ${budgetSeconds}`,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('the Codex gate prompt runs the review in the background rather than the default foreground timeout', () => {
|
|
201
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
202
|
+
assert.match(
|
|
203
|
+
codexPrompt,
|
|
204
|
+
/run_in_background/i,
|
|
205
|
+
'expected the review driver to run in the background — the foreground tool timeout caps below the review runtime and would kill a healthy review',
|
|
206
|
+
);
|
|
207
|
+
assert.match(
|
|
208
|
+
codexPrompt,
|
|
209
|
+
/result file/i,
|
|
210
|
+
'expected the driver to persist its outcome to a result file the agent reads once the background run finishes',
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('the Codex gate prompt waits for the background review inside the same turn with a bounded Monitor loop', () => {
|
|
215
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
216
|
+
assert.match(codexPrompt, /Monitor tool/, 'expected a same-turn Monitor wait for the background review');
|
|
217
|
+
assert.match(codexPrompt, /until-loop/, 'expected a bounded until-loop that re-checks the result file');
|
|
218
|
+
assert.match(codexPrompt, /codexReviewPollIntervalSeconds/, 'expected a named poll interval in the wait protocol');
|
|
219
|
+
assert.match(codexPrompt, /codexReviewMaxPolls/, 'expected a named attempt budget in the wait protocol');
|
|
220
|
+
assert.match(
|
|
221
|
+
codexPrompt,
|
|
222
|
+
/Do NOT end this turn/i,
|
|
223
|
+
'expected an explicit instruction against ending the schema-bearing turn to await the background run',
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('the Codex background-review poll budget covers the review timeout', () => {
|
|
228
|
+
const intervalMatch = convergeSource.match(/codexReviewPollIntervalSeconds:\s*(\d+)/);
|
|
229
|
+
const pollsMatch = convergeSource.match(/codexReviewMaxPolls:\s*(\d+)/);
|
|
230
|
+
const timeoutMatch = convergeSource.match(/codexReviewTimeoutSeconds:\s*(\d+)/);
|
|
231
|
+
assert.notEqual(intervalMatch, null, 'expected codexReviewPollIntervalSeconds in CONFIG');
|
|
232
|
+
assert.notEqual(pollsMatch, null, 'expected codexReviewMaxPolls in CONFIG');
|
|
233
|
+
assert.notEqual(timeoutMatch, null, 'expected codexReviewTimeoutSeconds in CONFIG');
|
|
234
|
+
const intervalSeconds = Number(intervalMatch[1]);
|
|
235
|
+
const maxPolls = Number(pollsMatch[1]);
|
|
236
|
+
const timeoutSeconds = Number(timeoutMatch[1]);
|
|
237
|
+
assert.ok(
|
|
238
|
+
intervalSeconds * maxPolls >= timeoutSeconds,
|
|
239
|
+
`expected the poll budget ${intervalSeconds * maxPolls}s to cover the ${timeoutSeconds}s review timeout`,
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('the Codex gate prompt returns codex_down when the background poll budget is spent', () => {
|
|
244
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
245
|
+
assert.match(
|
|
246
|
+
codexPrompt,
|
|
247
|
+
/full poll budget is spent with no result file[\s\S]*?down:true/,
|
|
248
|
+
'expected a spent-budget branch that returns the codex_down schema shape',
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('meta Codex gate phase describes codex_down as a non-blocking skip that logs a note', () => {
|
|
253
|
+
const phaseStart = convergeSource.indexOf("title: 'Codex gate'");
|
|
254
|
+
const phaseEnd = convergeSource.indexOf("title: 'Finalize'");
|
|
255
|
+
assert.notEqual(phaseStart, -1, 'expected a Codex gate phase in meta');
|
|
256
|
+
const codexPhaseDetail = convergeSource.slice(phaseStart, phaseEnd);
|
|
257
|
+
assert.match(codexPhaseDetail, /codex_down/, 'expected the phase detail to name codex_down');
|
|
258
|
+
assert.match(
|
|
259
|
+
codexPhaseDetail,
|
|
260
|
+
/codex_down, the gate skips without blocking/,
|
|
261
|
+
'expected codex_down to be described as a non-blocking skip',
|
|
262
|
+
);
|
|
263
|
+
assert.match(
|
|
264
|
+
codexPhaseDetail,
|
|
265
|
+
/logged as a note/,
|
|
266
|
+
'expected the codex_down skip to be logged as a note so the bypass stays visible',
|
|
267
|
+
);
|
|
268
|
+
assert.doesNotMatch(
|
|
269
|
+
codexPhaseDetail,
|
|
270
|
+
/holds the PR as a draft with a codex-down blocker/,
|
|
271
|
+
'codex_down must not be described as holding the PR draft with a blocker',
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('CODEX_SCHEMA down.description describes codex_down as a skipped, bypassed gate recorded as a note', () => {
|
|
276
|
+
const schemaStart = convergeSource.indexOf('const CODEX_SCHEMA =');
|
|
277
|
+
const schemaEnd = convergeSource.indexOf('const COPILOT_VERIFY_VERDICTS =');
|
|
278
|
+
assert.notEqual(schemaStart, -1, 'expected CODEX_SCHEMA to exist');
|
|
279
|
+
const schemaSource = convergeSource.slice(schemaStart, schemaEnd);
|
|
280
|
+
const downLineStart = schemaSource.indexOf('down: {');
|
|
281
|
+
const downLine = schemaSource.slice(downLineStart, schemaSource.indexOf('\n', downLineStart));
|
|
282
|
+
assert.match(downLine, /codex_down/, 'expected the down description to name codex_down');
|
|
283
|
+
assert.match(downLine, /skipped and bypassed/, 'expected codex_down to be described as a skipped, bypassed gate');
|
|
284
|
+
assert.match(downLine, /recorded as a note/, 'expected codex_down to be recorded as a note');
|
|
285
|
+
assert.doesNotMatch(
|
|
286
|
+
downLine,
|
|
287
|
+
/holds the PR as a draft with a codex-down blocker/,
|
|
288
|
+
'the down description must not claim codex_down holds the PR draft as a blocker',
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('the CODEX phase stamps codexCleanAt on clean and clears it on an opt-out, usage, or down skip', () => {
|
|
182
293
|
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {");
|
|
183
294
|
const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", codexPhaseStart);
|
|
184
295
|
assert.notEqual(codexPhaseStart, -1, 'expected a CODEX phase block');
|
|
@@ -192,6 +303,36 @@ test('the CODEX phase stamps codexCleanAt on clean and clears it on skip/down',
|
|
|
192
303
|
assert.match(codexPhase, /kind === 'down'[\s\S]*codexDown = true[\s\S]*codexCleanAt = null/);
|
|
193
304
|
});
|
|
194
305
|
|
|
306
|
+
test('the CODEX phase skips a genuine codex_down gracefully, recording a note and advancing to mark-ready', () => {
|
|
307
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {");
|
|
308
|
+
const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", codexPhaseStart);
|
|
309
|
+
const codexPhase = convergeSource.slice(codexPhaseStart, finalizePhaseStart);
|
|
310
|
+
const downBranchStart = codexPhase.indexOf("codexOutcome.kind === 'down'");
|
|
311
|
+
const downBranchEnd = codexPhase.indexOf("codexOutcome.kind === 'fix'", downBranchStart);
|
|
312
|
+
assert.notEqual(downBranchStart, -1, 'expected a codex_down branch in the CODEX phase');
|
|
313
|
+
assert.notEqual(downBranchEnd, -1, 'expected a fix branch after the down branch');
|
|
314
|
+
const downBranch = codexPhase.slice(downBranchStart, downBranchEnd);
|
|
315
|
+
assert.match(downBranch, /codexNote =/, 'a genuine codex_down records a transparent note');
|
|
316
|
+
assert.match(downBranch, /codexDown = true/, 'the down branch sets the bypass flag so mark-ready skips the required Codex gate');
|
|
317
|
+
assert.match(downBranch, /codexCleanAt = null/, 'the down branch clears any codex-clean stamp');
|
|
318
|
+
assert.match(
|
|
319
|
+
downBranch,
|
|
320
|
+
/phase = 'FINALIZE'/,
|
|
321
|
+
'a genuine codex_down advances to mark-ready with the gate bypassed rather than blocking the run',
|
|
322
|
+
);
|
|
323
|
+
assert.doesNotMatch(downBranch, /blocker =/, 'a genuine codex_down must not record a blocker');
|
|
324
|
+
assert.doesNotMatch(downBranch, /\bbreak\b/, 'the down branch continues rather than breaking the loop');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('the workflow return object includes codexNote so the final report surfaces the Codex skip', () => {
|
|
328
|
+
const assembleStart = convergeSource.indexOf('const assembleResult =');
|
|
329
|
+
assert.notEqual(assembleStart, -1, 'expected assembleResult to exist');
|
|
330
|
+
const assembleEnd = convergeSource.indexOf('})', assembleStart);
|
|
331
|
+
assert.notEqual(assembleEnd, -1, 'expected the assembleResult object to close');
|
|
332
|
+
const assembleSource = convergeSource.slice(assembleStart, assembleEnd);
|
|
333
|
+
assert.match(assembleSource, /codexNote,/, 'expected codexNote among the returned notes');
|
|
334
|
+
});
|
|
335
|
+
|
|
195
336
|
test('the CODEX phase routes findings through applyFixes and re-enters CONVERGE', () => {
|
|
196
337
|
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {");
|
|
197
338
|
const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", codexPhaseStart);
|
|
@@ -267,8 +408,8 @@ test('runConvergenceCheck tells the finalize agent to persist codex_clean_at for
|
|
|
267
408
|
test('CONFIG expands home for the codex-review scripts directory', () => {
|
|
268
409
|
assert.match(
|
|
269
410
|
convergeSource,
|
|
270
|
-
/const homeDirectory = \(
|
|
271
|
-
'expected home to be
|
|
411
|
+
/const homeDirectory = \([\s\S]*?homeDirectoryPayload\.homeDirectory/,
|
|
412
|
+
'expected home to be sourced from the workflow args payload (the sandbox exposes no process)',
|
|
272
413
|
);
|
|
273
414
|
assert.match(
|
|
274
415
|
convergeSource,
|
|
@@ -282,6 +423,25 @@ test('CONFIG expands home for the codex-review scripts directory', () => {
|
|
|
282
423
|
);
|
|
283
424
|
});
|
|
284
425
|
|
|
426
|
+
test('no process global is read unguarded (the workflow sandbox exposes no process)', () => {
|
|
427
|
+
const unguardedProcessLines = convergeSource
|
|
428
|
+
.split('\n')
|
|
429
|
+
.filter((sourceLine) => sourceLine.includes('process.') && !sourceLine.includes('typeof process'));
|
|
430
|
+
assert.deepEqual(
|
|
431
|
+
unguardedProcessLines,
|
|
432
|
+
[],
|
|
433
|
+
'every process.* read must sit on a line guarded by typeof process; the Bun workflow sandbox has no process global',
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test('the home directory is sourced from the workflow args payload', () => {
|
|
438
|
+
assert.match(
|
|
439
|
+
convergeSource,
|
|
440
|
+
/const homeDirectoryPayload = normalizeRunInput\(args\)/,
|
|
441
|
+
'expected homeDirectory to read args (the only channel into the process-less sandbox), parsed via normalizeRunInput',
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
|
|
285
445
|
test('meta lists the Codex gate phase between Copilot and Finalize', () => {
|
|
286
446
|
const copilotPhaseIndex = convergeSource.indexOf("title: 'Copilot gate'");
|
|
287
447
|
const codexPhaseIndex = convergeSource.indexOf("title: 'Codex gate'");
|
|
@@ -294,6 +454,18 @@ test('SKILL.md names Codex among the terminal confirmation gates', () => {
|
|
|
294
454
|
assert.match(skillSource, /Codex/i);
|
|
295
455
|
});
|
|
296
456
|
|
|
457
|
+
test('SKILL.md documents codexNote in the workflow return shape', () => {
|
|
458
|
+
const returnShapeMatch = skillSource.match(/`\{ converged, rounds, finalSha, blocker,[^`]*\}`/);
|
|
459
|
+
assert.ok(returnShapeMatch, 'expected the documented return shape literal in SKILL.md');
|
|
460
|
+
assert.match(returnShapeMatch[0], /codexNote/);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test('SKILL.md final-report template surfaces the codexNote so a bypassed Codex gate stays visible', () => {
|
|
464
|
+
const reportBlockMatch = skillSource.match(/\/autoconverge exit:[\s\S]*?```/);
|
|
465
|
+
assert.ok(reportBlockMatch, 'expected the final-report template block in SKILL.md');
|
|
466
|
+
assert.match(reportBlockMatch[0], /Codex: <codexNote>/);
|
|
467
|
+
});
|
|
468
|
+
|
|
297
469
|
test('stop-conditions.md documents the Codex gate skip paths as non-blockers', () => {
|
|
298
470
|
assert.match(stopConditionsSource, /Codex/i);
|
|
299
471
|
assert.match(stopConditionsSource, /codex/i);
|