claude-dev-env 1.94.0 → 2.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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +41 -33
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +17 -13
- package/_shared/advisor/scripts/model_tier_run_validator.py +27 -18
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +95 -25
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
- package/_shared/advisor/scripts/tier_model_ids.py +18 -18
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
- package/_shared/pr-loop/worker-spawn.md +186 -0
- package/agents/code-verifier.md +1 -1
- package/bin/CLAUDE.md +10 -1
- package/bin/ever-shipped-skills.mjs +73 -0
- package/bin/expand_home_directory_tokens.mjs +1 -1
- package/bin/install.mjs +140 -7
- package/bin/install.prune.test.mjs +457 -0
- package/docs/CODE_RULES.md +1 -1
- package/hooks/advisory/refactor_guard.py +3 -4
- package/hooks/blocking/CLAUDE.md +7 -1
- package/hooks/blocking/block_main_commit.py +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_mock_completeness.py +1 -1
- package/hooks/blocking/code_rules_optional_params.py +2 -2
- package/hooks/blocking/code_rules_shared.py +83 -1
- package/hooks/blocking/code_rules_test_assertions.py +1 -1
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
- package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
- package/hooks/blocking/inventory_intent_records/records.py +271 -0
- package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
- package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
- package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
- package/hooks/blocking/pii_payload_scan.py +138 -42
- package/hooks/blocking/pii_prevention_blocker.py +185 -291
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/pr_description_command_parser.py +8 -4
- package/hooks/blocking/precommit_code_rules_gate.py +3 -3
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +97 -601
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
- package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
- package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
- package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
- package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_code_rules_shared.py +181 -0
- package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
- package/hooks/blocking/test_pii_payload_scan.py +168 -0
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
- package/hooks/blocking/test_sensitive_file_protector.py +185 -0
- package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/tests/conftest.py +10 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
- package/hooks/blocking/tests/test_repository_exemption.py +105 -0
- package/hooks/blocking/tests/test_repository_resolution.py +108 -0
- package/hooks/diagnostic/hook_log_extractor.py +12 -10
- package/hooks/git-hooks/post_commit.py +3 -4
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +59 -8
- package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +105 -3
- package/hooks/pyproject.toml +52 -4
- package/hooks/session/plugin_data_dir_cleanup.py +0 -1
- package/hooks/validation/mypy_validator.py +2 -2
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/health_check.py +1 -0
- package/hooks/validators/mypy_integration.py +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/ruff_integration.py +3 -0
- package/hooks/validators/test_python_style_checks.py +0 -164
- package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
- package/hooks/validators/test_python_style_fixes.py +251 -0
- package/hooks/validators/test_python_style_helpers.py +125 -0
- package/hooks/workflow/auto_formatter.py +5 -4
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/anti-corollary-tests.md +69 -0
- package/rules/bdd.md +1 -3
- package/rules/code-reviews.md +1 -1
- package/rules/gh-paginate.md +1 -1
- package/rules/plain-language.md +2 -0
- package/scripts/CLAUDE.md +4 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
- package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
- package/scripts/dev_env_scripts_constants/timing.py +7 -1
- package/scripts/grok_headless_runner.py +294 -0
- package/scripts/grok_worker_preflight.py +410 -0
- package/scripts/invoke_code_review.py +463 -0
- package/scripts/resolve_worker_spawn.py +619 -0
- package/scripts/spawn_grok_batch.py +672 -0
- package/scripts/test_grok_headless_runner.py +626 -0
- package/scripts/test_grok_worker_preflight.py +1054 -0
- package/scripts/test_invoke_code_review.py +672 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -0
- package/scripts/test_spawn_grok_batch.py +1017 -0
- package/skills/CLAUDE.md +6 -3
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +9 -3
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +33 -11
- package/skills/autoconverge/reference/stop-conditions.md +16 -5
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +202 -18
- package/skills/bugteam/CLAUDE.md +2 -2
- package/skills/bugteam/CONSTRAINTS.md +3 -2
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +18 -13
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +215 -35
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -2
- package/skills/closeout/SKILL.md +153 -0
- package/skills/closeout/reference/handoff-prompt-template.md +72 -0
- package/skills/closeout/reference/issue-body-templates.md +108 -0
- package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
- package/skills/codex-review/CLAUDE.md +46 -0
- package/skills/codex-review/SKILL.md +181 -0
- package/skills/codex-review/reference/CLAUDE.md +15 -0
- package/skills/codex-review/reference/cli-contract.md +253 -0
- package/skills/codex-review/reference/loop-integration.md +118 -0
- package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
- package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
- package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
- package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
- package/skills/codex-review/scripts/run_codex_review.py +415 -0
- package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
- package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
- package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
- package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
- package/skills/codex-review/test_skill_scaffold.py +192 -0
- package/skills/grok-spawn/CLAUDE.md +28 -0
- package/skills/grok-spawn/SKILL.md +226 -0
- package/skills/grok-spawn/reference/flag-profiles.md +132 -0
- package/skills/grok-spawn/reference/worker-briefs.md +152 -0
- package/skills/grokify/SKILL.md +9 -1
- package/skills/grokify/capability-claims.test.mjs +28 -0
- package/skills/grokify/evals/README.md +72 -0
- package/skills/grokify/evals/parse-payload.test.mjs +171 -0
- package/skills/grokify/evals/run-capability-evals.mjs +545 -0
- package/skills/orchestrator/SKILL.md +32 -22
- package/skills/orchestrator-refresh/SKILL.md +12 -8
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +34 -13
- package/skills/pr-converge/reference/convergence-gates.md +42 -15
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +130 -42
- package/skills/pr-converge/reference/state-schema.md +10 -0
- package/skills/pr-converge/scripts/CLAUDE.md +2 -0
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
- package/skills/pr-converge/scripts/check_convergence.py +605 -29
- package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
- package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
- package/skills/pr-converge/test_step5_host_branch.py +106 -0
- package/skills/pr-fix-protocol/SKILL.md +65 -0
- package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
- package/skills/reviewer-gates/SKILL.md +7 -5
- package/skills/skill-builder/references/skill-modularity.md +1 -1
- package/skills/team-advisor/SKILL.md +16 -12
- package/system-prompts/software-engineer.xml +7 -6
- package/hooks/validators/test_verify_paths.py +0 -32
- package/hooks/validators/verify_paths.py +0 -57
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import { strict as assert } from 'node:assert';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const workflowDirectory = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const convergeSource = readFileSync(join(workflowDirectory, 'converge.mjs'), 'utf8');
|
|
9
|
+
const skillSource = readFileSync(join(workflowDirectory, '..', 'SKILL.md'), 'utf8');
|
|
10
|
+
const stopConditionsSource = readFileSync(
|
|
11
|
+
join(workflowDirectory, '..', 'reference', 'stop-conditions.md'),
|
|
12
|
+
'utf8',
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
function functionBody(functionName) {
|
|
16
|
+
const functionStart = convergeSource.indexOf(`function ${functionName}(`);
|
|
17
|
+
assert.notEqual(functionStart, -1, `expected ${functionName} to exist`);
|
|
18
|
+
const nextFunctionStart = convergeSource.indexOf('\nfunction ', functionStart + 1);
|
|
19
|
+
const functionEnd = nextFunctionStart === -1 ? convergeSource.length : nextFunctionStart;
|
|
20
|
+
return convergeSource.slice(functionStart, functionEnd);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const productionModule = new Function(
|
|
24
|
+
`${functionBody('classifyCodexGateOutcome')}\n` +
|
|
25
|
+
'return { classifyCodexGateOutcome };',
|
|
26
|
+
)();
|
|
27
|
+
const { classifyCodexGateOutcome } = productionModule;
|
|
28
|
+
|
|
29
|
+
function codexResult(overrides) {
|
|
30
|
+
return {
|
|
31
|
+
sha: 'abcdef0',
|
|
32
|
+
clean: false,
|
|
33
|
+
down: false,
|
|
34
|
+
skipped: false,
|
|
35
|
+
skipReason: '',
|
|
36
|
+
findings: [],
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function codexFinding(overrides) {
|
|
42
|
+
return {
|
|
43
|
+
file: 'a.py',
|
|
44
|
+
line: 1,
|
|
45
|
+
severity: 'P1',
|
|
46
|
+
category: 'bug',
|
|
47
|
+
title: 't',
|
|
48
|
+
detail: 'd',
|
|
49
|
+
replyToCommentId: null,
|
|
50
|
+
...overrides,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test('a required clean Codex result routes to clean so the run stamps codex-clean HEAD', () => {
|
|
55
|
+
const outcome = classifyCodexGateOutcome(codexResult({ clean: true }));
|
|
56
|
+
assert.equal(outcome.kind, 'clean');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('an opt-out token skip routes to skip-token', () => {
|
|
60
|
+
const outcome = classifyCodexGateOutcome(
|
|
61
|
+
codexResult({ clean: true, down: true, skipped: true, skipReason: 'token' }),
|
|
62
|
+
);
|
|
63
|
+
assert.equal(outcome.kind, 'skip-token');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('a usage-threshold skip routes to skip-usage', () => {
|
|
67
|
+
const outcome = classifyCodexGateOutcome(
|
|
68
|
+
codexResult({ clean: true, down: false, skipped: true, skipReason: 'usage' }),
|
|
69
|
+
);
|
|
70
|
+
assert.equal(outcome.kind, 'skip-usage');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('a codex_down result routes to down', () => {
|
|
74
|
+
const outcome = classifyCodexGateOutcome(codexResult({ clean: false, down: true }));
|
|
75
|
+
assert.equal(outcome.kind, 'down');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('Codex findings route to a fix when the review completed with findings', () => {
|
|
79
|
+
const outcome = classifyCodexGateOutcome(
|
|
80
|
+
codexResult({
|
|
81
|
+
findings: [codexFinding()],
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
assert.equal(outcome.kind, 'fix');
|
|
85
|
+
assert.equal(outcome.findings.length, 1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('a dead Codex gate agent retries rather than passing', () => {
|
|
89
|
+
assert.equal(classifyCodexGateOutcome(null).kind, 'retry');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('a reachable Codex gate with no findings and no clean verdict retries', () => {
|
|
93
|
+
const outcome = classifyCodexGateOutcome(codexResult({ clean: false, down: false }));
|
|
94
|
+
assert.equal(outcome.kind, 'retry');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('skipped with an unknown skipReason retries rather than silently advancing', () => {
|
|
98
|
+
const outcome = classifyCodexGateOutcome(
|
|
99
|
+
codexResult({ skipped: true, skipReason: 'mystery' }),
|
|
100
|
+
);
|
|
101
|
+
assert.equal(outcome.kind, 'retry');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('CODEX_SCHEMA requires skipped and skipReason drawn from the skip-reason constant', () => {
|
|
105
|
+
const schemaStart = convergeSource.indexOf('const CODEX_SCHEMA =');
|
|
106
|
+
const schemaEnd = convergeSource.indexOf('const COPILOT_VERIFY_VERDICTS =');
|
|
107
|
+
assert.notEqual(schemaStart, -1, 'expected CODEX_SCHEMA to exist');
|
|
108
|
+
const schemaSource = convergeSource.slice(schemaStart, schemaEnd);
|
|
109
|
+
assert.match(schemaSource, /skipped:\s*\{\s*type:\s*'boolean'/);
|
|
110
|
+
assert.match(schemaSource, /enum:\s*CODEX_SKIP_REASONS/);
|
|
111
|
+
assert.match(schemaSource, /required:\s*\[[^\]]*'skipped'[^\]]*\]/);
|
|
112
|
+
assert.match(schemaSource, /required:\s*\[[^\]]*'skipReason'[^\]]*\]/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('the Codex gate prompt honors reviews_disabled.py with only --reviewer codex', () => {
|
|
116
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
117
|
+
const reviewsDisabledIndex = codexPrompt.indexOf('reviews_disabled.py');
|
|
118
|
+
assert.notEqual(reviewsDisabledIndex, -1, 'expected reviews_disabled.py invocation');
|
|
119
|
+
const invocationLineEnd = codexPrompt.indexOf('\\n', reviewsDisabledIndex);
|
|
120
|
+
const invocationLine = codexPrompt.slice(reviewsDisabledIndex, invocationLineEnd);
|
|
121
|
+
assert.match(invocationLine, /--reviewer codex/);
|
|
122
|
+
assert.doesNotMatch(invocationLine, /--owner|--repo/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('the Codex gate prompt decides required vs skip only through is_codex_review_required', () => {
|
|
126
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
127
|
+
assert.match(codexPrompt, /codex_usage_probe\.py/, 'expected the usage probe CLI');
|
|
128
|
+
assert.match(
|
|
129
|
+
codexPrompt,
|
|
130
|
+
/is_codex_review_required/,
|
|
131
|
+
'expected the shared probe helper rather than an inline percent threshold',
|
|
132
|
+
);
|
|
133
|
+
assert.doesNotMatch(
|
|
134
|
+
codexPrompt,
|
|
135
|
+
/WEEKLY_USAGE_GATE_THRESHOLD_PERCENT|\b10\s*%|\bpercent_left\s*[><=]/,
|
|
136
|
+
'expected no duplicate threshold literal or inline comparison in the gate prompt',
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('the Codex gate prompt treats a non-zero usage-probe exit as codex_down not usage skip', () => {
|
|
141
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
142
|
+
assert.match(
|
|
143
|
+
codexPrompt,
|
|
144
|
+
/Non-zero exit means the Codex CLI or probe is broken/,
|
|
145
|
+
'expected an explicit non-zero probe exit branch',
|
|
146
|
+
);
|
|
147
|
+
assert.match(
|
|
148
|
+
codexPrompt,
|
|
149
|
+
/Never map a failed probe to skipReason:'usage'/,
|
|
150
|
+
'expected failed probe to be barred from the usage-skip path',
|
|
151
|
+
);
|
|
152
|
+
assert.match(
|
|
153
|
+
codexPrompt,
|
|
154
|
+
/clean:false, down:true, skipped:false, skipReason:''/,
|
|
155
|
+
'expected failed probe to return the codex_down schema shape',
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('the Codex gate prompt sys.path insert uses CONFIG.codexScripts not a literal $HOME token', () => {
|
|
160
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
161
|
+
assert.match(
|
|
162
|
+
codexPrompt,
|
|
163
|
+
/sys\.path\.insert\(0, r'\$\{CONFIG\.codexScripts\}'\)/,
|
|
164
|
+
'expected sys.path.insert to interpolate CONFIG.codexScripts (home-expanded at CONFIG build)',
|
|
165
|
+
);
|
|
166
|
+
assert.doesNotMatch(
|
|
167
|
+
codexPrompt,
|
|
168
|
+
/sys\.path\.insert\(0, r'\$HOME/,
|
|
169
|
+
'expected no unexpanded $HOME inside the Python raw-string path',
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('the Codex gate prompt runs the review wrapper against the PR base branch', () => {
|
|
174
|
+
const codexPrompt = functionBody('runCodexGate');
|
|
175
|
+
assert.match(codexPrompt, /run_codex_review/, 'expected the codex-review wrapper');
|
|
176
|
+
assert.match(codexPrompt, /base_branch/, 'expected the wrapper to receive the PR base');
|
|
177
|
+
assert.match(codexPrompt, /\.base\.ref/, 'expected the agent to resolve the PR base ref');
|
|
178
|
+
assert.match(codexPrompt, /parse_codex_findings/, 'expected findings to come from the shared parser');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('the CODEX phase stamps codexCleanAt on clean and clears it on skip/down', () => {
|
|
182
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {");
|
|
183
|
+
const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", codexPhaseStart);
|
|
184
|
+
assert.notEqual(codexPhaseStart, -1, 'expected a CODEX phase block');
|
|
185
|
+
assert.notEqual(finalizePhaseStart, -1, 'expected FINALIZE after CODEX');
|
|
186
|
+
const codexPhase = convergeSource.slice(codexPhaseStart, finalizePhaseStart);
|
|
187
|
+
assert.match(codexPhase, /await runCodexGate\(head\)/);
|
|
188
|
+
assert.match(codexPhase, /classifyCodexGateOutcome\(codex\)/);
|
|
189
|
+
assert.match(codexPhase, /codexCleanAt = head/, 'expected a clean pass to stamp the HEAD');
|
|
190
|
+
assert.match(codexPhase, /skip-token[\s\S]*codexDown = true[\s\S]*codexCleanAt = null/);
|
|
191
|
+
assert.match(codexPhase, /skip-usage[\s\S]*codexDown = false[\s\S]*codexCleanAt = null/);
|
|
192
|
+
assert.match(codexPhase, /kind === 'down'[\s\S]*codexDown = true[\s\S]*codexCleanAt = null/);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('the CODEX phase routes findings through applyFixes and re-enters CONVERGE', () => {
|
|
196
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {");
|
|
197
|
+
const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", codexPhaseStart);
|
|
198
|
+
const codexPhase = convergeSource.slice(codexPhaseStart, finalizePhaseStart);
|
|
199
|
+
assert.match(codexPhase, /applyFixes\(head, codexOutcome\.findings, 'codex'\)/);
|
|
200
|
+
assert.match(codexPhase, /phase = 'CONVERGE'/);
|
|
201
|
+
assert.match(codexPhase, /head = null/);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('the COPILOT phase advances to CODEX rather than FINALIZE on a clean or bypass path', () => {
|
|
205
|
+
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
206
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {", copilotPhaseStart);
|
|
207
|
+
assert.notEqual(copilotPhaseStart, -1, 'expected a COPILOT phase block');
|
|
208
|
+
assert.notEqual(codexPhaseStart, -1, 'expected a CODEX phase after COPILOT');
|
|
209
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
210
|
+
assert.match(copilotPhase, /phase = 'CODEX'/);
|
|
211
|
+
assert.doesNotMatch(copilotPhase, /phase = 'FINALIZE'/);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('the merged FINALIZE check receives codexDown and codexCleanAt and passes matching flags', () => {
|
|
215
|
+
const finalizeStart = convergeSource.indexOf("if (phase === 'FINALIZE') {");
|
|
216
|
+
assert.notEqual(finalizeStart, -1, 'expected a FINALIZE phase block');
|
|
217
|
+
const checkCall = convergeSource.indexOf('runConvergenceCheck(', finalizeStart);
|
|
218
|
+
assert.notEqual(checkCall, -1, 'expected the FINALIZE phase to call runConvergenceCheck');
|
|
219
|
+
const checkLine = convergeSource.slice(checkCall, convergeSource.indexOf('\n', checkCall));
|
|
220
|
+
assert.match(checkLine, /codexDown/);
|
|
221
|
+
assert.match(checkLine, /codexCleanAt/);
|
|
222
|
+
const checkBody = functionBody('runConvergenceCheck');
|
|
223
|
+
assert.match(checkBody, /context\.codexDown \? ' --codex-down' : ''/);
|
|
224
|
+
assert.match(checkBody, /context\.codexCleanAt \? ` --codex-clean-at \$\{context\.codexCleanAt\}` : ''/);
|
|
225
|
+
assert.match(checkBody, /if \(context\.codexDown\) reviewerOptOutTokens\.push\('codex'\)/);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('runConvergenceCheck tells the finalize agent to persist codex_clean_at for the flagless mark-ready re-check', () => {
|
|
229
|
+
const checkBody = functionBody('runConvergenceCheck');
|
|
230
|
+
assert.match(
|
|
231
|
+
checkBody,
|
|
232
|
+
/codexCleanAtNote/,
|
|
233
|
+
'expected a dedicated note when the Codex gate stamped clean on HEAD',
|
|
234
|
+
);
|
|
235
|
+
assert.match(
|
|
236
|
+
checkBody,
|
|
237
|
+
/pr-converge-state\.json/,
|
|
238
|
+
'expected the note to name the job-dir state file the mark-ready blocker re-reads',
|
|
239
|
+
);
|
|
240
|
+
assert.match(
|
|
241
|
+
checkBody,
|
|
242
|
+
/codex_clean_at/,
|
|
243
|
+
'expected the note to write the codex_clean_at key the flagless re-check resolves',
|
|
244
|
+
);
|
|
245
|
+
assert.match(
|
|
246
|
+
checkBody,
|
|
247
|
+
/CLAUDE_JOB_DIR/,
|
|
248
|
+
'expected the note to locate the state file under CLAUDE_JOB_DIR',
|
|
249
|
+
);
|
|
250
|
+
assert.match(
|
|
251
|
+
checkBody,
|
|
252
|
+
/needsCodexCleanExportFallback/,
|
|
253
|
+
'expected a single-export fallback path when CLAUDE_JOB_DIR is unset',
|
|
254
|
+
);
|
|
255
|
+
assert.match(
|
|
256
|
+
checkBody,
|
|
257
|
+
/Emit exactly one export/,
|
|
258
|
+
'expected the opt-out instruction to forbid a second overwriting export',
|
|
259
|
+
);
|
|
260
|
+
assert.doesNotMatch(
|
|
261
|
+
checkBody,
|
|
262
|
+
/export CLAUDE_REVIEWS_DISABLED="codex"/,
|
|
263
|
+
'expected no second solo codex export that would overwrite other tokens',
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('CONFIG expands home for the codex-review scripts directory', () => {
|
|
268
|
+
assert.match(
|
|
269
|
+
convergeSource,
|
|
270
|
+
/const homeDirectory = \(process\.env\.HOME \|\| process\.env\.USERPROFILE/,
|
|
271
|
+
'expected home to be resolved from process.env before CONFIG path use',
|
|
272
|
+
);
|
|
273
|
+
assert.match(
|
|
274
|
+
convergeSource,
|
|
275
|
+
/codexScripts:\s*`\$\{homeDirectory\}\/\.claude\/skills\/codex-review\/scripts`/,
|
|
276
|
+
'expected codexScripts to expand homeDirectory rather than embed $HOME',
|
|
277
|
+
);
|
|
278
|
+
assert.doesNotMatch(
|
|
279
|
+
convergeSource,
|
|
280
|
+
/codexScripts:\s*'\$HOME\//,
|
|
281
|
+
'expected no literal $HOME token in codexScripts (Python raw strings do not expand it)',
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test('meta lists the Codex gate phase between Copilot and Finalize', () => {
|
|
286
|
+
const copilotPhaseIndex = convergeSource.indexOf("title: 'Copilot gate'");
|
|
287
|
+
const codexPhaseIndex = convergeSource.indexOf("title: 'Codex gate'");
|
|
288
|
+
const finalizePhaseIndex = convergeSource.indexOf("title: 'Finalize'");
|
|
289
|
+
assert.notEqual(codexPhaseIndex, -1, 'expected a Codex gate phase in meta');
|
|
290
|
+
assert.ok(copilotPhaseIndex < codexPhaseIndex && codexPhaseIndex < finalizePhaseIndex);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test('SKILL.md names Codex among the terminal confirmation gates', () => {
|
|
294
|
+
assert.match(skillSource, /Codex/i);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('stop-conditions.md documents the Codex gate skip paths as non-blockers', () => {
|
|
298
|
+
assert.match(stopConditionsSource, /Codex/i);
|
|
299
|
+
assert.match(stopConditionsSource, /codex/i);
|
|
300
|
+
});
|
|
@@ -312,8 +312,8 @@ test('each fix push, each lens-retry, and the convergence repair invalidate the
|
|
|
312
312
|
const invalidationMatches = convergeSource.match(/^ +head = null$/gm) || [];
|
|
313
313
|
assert.equal(
|
|
314
314
|
invalidationMatches.length,
|
|
315
|
-
|
|
316
|
-
'expected head invalidation after the static-sweep fix push, the CONVERGE fix push, the terminal-Bugbot fix push, the COPILOT fix push, the convergence repair, the all-lenses-dead retry, the not-clean-no-findings retry, and the all-clean no-lens-ran clean-audit refusal',
|
|
315
|
+
9,
|
|
316
|
+
'expected head invalidation after the static-sweep fix push, the CONVERGE fix push, the terminal-Bugbot fix push, the COPILOT fix push, the CODEX fix push, the convergence repair, the all-lenses-dead retry, the not-clean-no-findings retry, and the all-clean no-lens-ran clean-audit refusal',
|
|
317
317
|
);
|
|
318
318
|
});
|
|
319
319
|
|
|
@@ -617,8 +617,8 @@ test('every standards-deferral call site builds standardsNote from the spawnStan
|
|
|
617
617
|
const callSiteUses = convergeSource.match(/standardsNote = standardsDeferralNote\(/g) || [];
|
|
618
618
|
assert.equal(
|
|
619
619
|
callSiteUses.length,
|
|
620
|
-
|
|
621
|
-
'expected the converge-round, terminal-Bugbot, and
|
|
620
|
+
4,
|
|
621
|
+
'expected the converge-round, terminal-Bugbot, Copilot, and Codex standards call sites to each build standardsNote via standardsDeferralNote(...)',
|
|
622
622
|
);
|
|
623
623
|
assert.doesNotMatch(
|
|
624
624
|
convergeSource,
|
|
@@ -1234,7 +1234,7 @@ test('the FINALIZE phase drives the merged check and reads ready from its result
|
|
|
1234
1234
|
const finalizeStart = convergeSource.indexOf("if (phase === 'FINALIZE') {");
|
|
1235
1235
|
assert.notEqual(finalizeStart, -1, 'expected a FINALIZE phase block');
|
|
1236
1236
|
const finalizeBody = convergeSource.slice(finalizeStart, finalizeStart + 1000);
|
|
1237
|
-
assert.match(finalizeBody, /runConvergenceCheck\(\{ head, bugbotDown, copilotDown, bugteamPostBlocked: cleanAuditNote !== null \}\)/);
|
|
1237
|
+
assert.match(finalizeBody, /runConvergenceCheck\(\{ head, bugbotDown, copilotDown, codexDown, codexCleanAt, bugteamPostBlocked: cleanAuditNote !== null \}\)/);
|
|
1238
1238
|
assert.match(finalizeBody, /classifyReadyOutcome\(finalizeResult\)/);
|
|
1239
1239
|
assert.doesNotMatch(
|
|
1240
1240
|
finalizeBody,
|
|
@@ -276,8 +276,8 @@ test('the Copilot gate prompt tiers each finding and returns the review URL', ()
|
|
|
276
276
|
|
|
277
277
|
test('the COPILOT phase returns blocker user-review only when inconclusive findings remain after verification, carrying the verifier-noted payload with no auto-fix in the branch', () => {
|
|
278
278
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
279
|
-
const
|
|
280
|
-
const copilotPhase = convergeSource.slice(copilotPhaseStart,
|
|
279
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {", copilotPhaseStart);
|
|
280
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
281
281
|
const userReviewGuardStart = copilotPhase.indexOf('inconclusive.length > 0');
|
|
282
282
|
assert.notEqual(userReviewGuardStart, -1, 'expected the user-review return to be guarded by inconclusive.length > 0');
|
|
283
283
|
const userReviewBranch = copilotPhase.slice(userReviewGuardStart, userReviewGuardStart + 600);
|
|
@@ -296,8 +296,8 @@ test('the COPILOT phase returns blocker user-review only when inconclusive findi
|
|
|
296
296
|
|
|
297
297
|
test('the COPILOT phase awaits verifyCodeConcernFindings exactly once, inside the code-concern guard', () => {
|
|
298
298
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
299
|
-
const
|
|
300
|
-
const copilotPhase = convergeSource.slice(copilotPhaseStart,
|
|
299
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {", copilotPhaseStart);
|
|
300
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
301
301
|
const guardStart = copilotPhase.indexOf('if (codeConcern.length > 0)');
|
|
302
302
|
assert.notEqual(guardStart, -1, 'expected the verification to sit behind a codeConcern.length > 0 guard');
|
|
303
303
|
const verifyCalls = convergeSource.match(/await verifyCodeConcernFindings\(/g) || [];
|
|
@@ -306,21 +306,21 @@ test('the COPILOT phase awaits verifyCodeConcernFindings exactly once, inside th
|
|
|
306
306
|
assert.ok(verifyCallIndex > guardStart, 'expected the verifyCodeConcernFindings call to sit inside the code-concern guard');
|
|
307
307
|
});
|
|
308
308
|
|
|
309
|
-
test('the all-refuted / empty-roundFindings COPILOT path advances to
|
|
309
|
+
test('the all-refuted / empty-roundFindings COPILOT path advances to CODEX with the gate passed', () => {
|
|
310
310
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
311
|
-
const
|
|
312
|
-
const copilotPhase = convergeSource.slice(copilotPhaseStart,
|
|
311
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {", copilotPhaseStart);
|
|
312
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
313
313
|
const emptyRoundStart = copilotPhase.indexOf('roundFindings.length === 0');
|
|
314
314
|
assert.notEqual(emptyRoundStart, -1, 'expected the COPILOT phase to handle an empty roundFindings set after verification');
|
|
315
315
|
const emptyRoundBranch = copilotPhase.slice(emptyRoundStart, emptyRoundStart + 300);
|
|
316
316
|
assert.match(emptyRoundBranch, /copilotDown = false/, 'expected the empty-roundFindings path to clear copilotDown');
|
|
317
|
-
assert.match(emptyRoundBranch, /phase = '
|
|
317
|
+
assert.match(emptyRoundBranch, /phase = 'CODEX'/, 'expected the empty-roundFindings path to advance to CODEX');
|
|
318
318
|
});
|
|
319
319
|
|
|
320
320
|
test('the COPILOT phase folds confirmed findings through attachVerifiedRepro into the round fix list', () => {
|
|
321
321
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
322
|
-
const
|
|
323
|
-
const copilotPhase = convergeSource.slice(copilotPhaseStart,
|
|
322
|
+
const codexPhaseStart = convergeSource.indexOf("if (phase === 'CODEX') {", copilotPhaseStart);
|
|
323
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
324
324
|
assert.match(
|
|
325
325
|
copilotPhase,
|
|
326
326
|
/roundFindings = \[\.\.\.selfHealing, \.\.\.confirmed\.map\(\(each\) => attachVerifiedRepro\(each\)\)\]/,
|
|
@@ -405,15 +405,15 @@ test('runConvergenceCheck wires the --copilot-down flag from the copilotDown con
|
|
|
405
405
|
);
|
|
406
406
|
});
|
|
407
407
|
|
|
408
|
-
test('the COPILOT phase routes a down outcome to
|
|
408
|
+
test('the COPILOT phase routes a down outcome to CODEX with the gate bypassed', () => {
|
|
409
409
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
410
410
|
assert.notEqual(copilotPhaseStart, -1, 'expected a COPILOT phase block');
|
|
411
411
|
const downBranchStart = convergeSource.indexOf("copilotOutcome.kind === 'down'", copilotPhaseStart);
|
|
412
412
|
assert.notEqual(downBranchStart, -1, 'expected the COPILOT phase to handle a down outcome');
|
|
413
|
-
const downBranch = convergeSource.slice(downBranchStart, downBranchStart +
|
|
413
|
+
const downBranch = convergeSource.slice(downBranchStart, downBranchStart + 700);
|
|
414
414
|
assert.match(downBranch, /copilotDown = true/);
|
|
415
415
|
assert.match(downBranch, /copilotNote =/);
|
|
416
|
-
assert.match(downBranch, /phase = '
|
|
416
|
+
assert.match(downBranch, /phase = 'CODEX'/);
|
|
417
417
|
});
|
|
418
418
|
|
|
419
419
|
test('resolveCopilotDown reports down only for a down outcome', () => {
|
|
@@ -448,7 +448,7 @@ test('resolveCopilotDown clears the bypass for a retry outcome', () => {
|
|
|
448
448
|
assert.equal(resolveCopilotDown({ kind: 'retry' }), false);
|
|
449
449
|
});
|
|
450
450
|
|
|
451
|
-
test('the standards-only Copilot sub-path resets copilotDown before
|
|
451
|
+
test('the standards-only Copilot sub-path resets copilotDown before CODEX', () => {
|
|
452
452
|
const standardsBranchStart = convergeSource.indexOf(
|
|
453
453
|
'isStandardsOnlyRound(roundFindings)',
|
|
454
454
|
);
|
|
@@ -459,19 +459,19 @@ test('the standards-only Copilot sub-path resets copilotDown before FINALIZE', (
|
|
|
459
459
|
);
|
|
460
460
|
const standardsBranch = convergeSource.slice(standardsBranchStart, standardsBranchStart + 800);
|
|
461
461
|
const resetIndex = standardsBranch.indexOf('copilotDown = false');
|
|
462
|
-
const
|
|
462
|
+
const codexIndex = standardsBranch.indexOf("phase = 'CODEX'");
|
|
463
463
|
assert.notEqual(
|
|
464
464
|
resetIndex,
|
|
465
465
|
-1,
|
|
466
466
|
'expected the standards-only sub-path to reset copilotDown so a recovered Copilot is not bypassed',
|
|
467
467
|
);
|
|
468
|
-
assert.notEqual(
|
|
468
|
+
assert.notEqual(codexIndex, -1, 'expected the standards-only sub-path to reach CODEX');
|
|
469
469
|
assert.ok(
|
|
470
|
-
resetIndex <
|
|
471
|
-
'expected copilotDown to be cleared before the transition to
|
|
470
|
+
resetIndex < codexIndex,
|
|
471
|
+
'expected copilotDown to be cleared before the transition to CODEX',
|
|
472
472
|
);
|
|
473
473
|
assert.match(
|
|
474
|
-
standardsBranch.slice(0,
|
|
474
|
+
standardsBranch.slice(0, codexIndex),
|
|
475
475
|
/copilotNote = null/,
|
|
476
476
|
'expected the standards-only sub-path to clear the stale copilotNote alongside copilotDown',
|
|
477
477
|
);
|
|
@@ -480,12 +480,12 @@ test('the standards-only Copilot sub-path resets copilotDown before FINALIZE', (
|
|
|
480
480
|
test('the COPILOT phase recomputes copilotDown from each gate outcome via resolveCopilotDown', () => {
|
|
481
481
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
482
482
|
assert.notEqual(copilotPhaseStart, -1, 'expected a COPILOT phase block');
|
|
483
|
-
const
|
|
484
|
-
"if (phase === '
|
|
483
|
+
const codexPhaseStart = convergeSource.indexOf(
|
|
484
|
+
"if (phase === 'CODEX') {",
|
|
485
485
|
copilotPhaseStart,
|
|
486
486
|
);
|
|
487
|
-
assert.notEqual(
|
|
488
|
-
const copilotPhase = convergeSource.slice(copilotPhaseStart,
|
|
487
|
+
assert.notEqual(codexPhaseStart, -1, 'expected a CODEX phase block after COPILOT');
|
|
488
|
+
const copilotPhase = convergeSource.slice(copilotPhaseStart, codexPhaseStart);
|
|
489
489
|
assert.match(
|
|
490
490
|
copilotPhase,
|
|
491
491
|
/copilotDown = resolveCopilotDown\(copilotOutcome\)/,
|
|
@@ -552,7 +552,7 @@ test('the COPILOT phase short-circuits on the unified reviewer-down gate before
|
|
|
552
552
|
);
|
|
553
553
|
assert.match(beforeGate, /copilotDown = true/, 'expected the bypass to mark copilotDown');
|
|
554
554
|
assert.match(beforeGate, /copilotNote =/, 'expected the bypass to set a copilotNote');
|
|
555
|
-
assert.match(beforeGate, /phase = '
|
|
555
|
+
assert.match(beforeGate, /phase = 'CODEX'/, 'expected the bypass to advance to CODEX');
|
|
556
556
|
assert.match(beforeGate, /continue/, 'expected the bypass to continue without spawning the gate agent');
|
|
557
557
|
});
|
|
558
558
|
|
|
@@ -574,7 +574,7 @@ test('copilotDown initializes from input.copilotDisabled so the pre-check decisi
|
|
|
574
574
|
);
|
|
575
575
|
});
|
|
576
576
|
|
|
577
|
-
test('a copilotDisabled run reaches
|
|
577
|
+
test('a copilotDisabled run reaches CODEX with --copilot-down', () => {
|
|
578
578
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
579
579
|
const bypassStart = convergeSource.indexOf(
|
|
580
580
|
'if (resolveReviewerDown(reviewerAvailability?.copilot, input.copilotDisabled || false))',
|
|
@@ -583,12 +583,12 @@ test('a copilotDisabled run reaches FINALIZE with --copilot-down', () => {
|
|
|
583
583
|
assert.notEqual(bypassStart, -1, 'expected the unified resolveReviewerDown bypass in the COPILOT phase');
|
|
584
584
|
const bypassBlock = convergeSource.slice(bypassStart, bypassStart + 800);
|
|
585
585
|
assert.match(bypassBlock, /copilotDown = true/, 'expected the bypass to set copilotDown');
|
|
586
|
-
assert.match(bypassBlock, /phase = '
|
|
586
|
+
assert.match(bypassBlock, /phase = 'CODEX'/, 'expected the bypass to advance to CODEX');
|
|
587
587
|
const convergenceCheckBody = functionBody('runConvergenceCheck');
|
|
588
588
|
assert.match(
|
|
589
589
|
convergenceCheckBody,
|
|
590
590
|
/context\.copilotDown \? ' --copilot-down' : ''/,
|
|
591
|
-
'expected the convergence check to pass --copilot-down when the bypassed copilotDown reaches FINALIZE',
|
|
591
|
+
'expected the convergence check to pass --copilot-down when the bypassed copilotDown reaches FINALIZE via CODEX',
|
|
592
592
|
);
|
|
593
593
|
});
|
|
594
594
|
|
|
@@ -673,8 +673,8 @@ test('every standards-deferral call site routes the create through openStandards
|
|
|
673
673
|
const onceCalls = convergeSource.match(/await openStandardsFollowUpOnce\(/g) || [];
|
|
674
674
|
assert.equal(
|
|
675
675
|
onceCalls.length,
|
|
676
|
-
|
|
677
|
-
'expected the converge-round, terminal-Bugbot, and
|
|
676
|
+
4,
|
|
677
|
+
'expected the converge-round, terminal-Bugbot, Copilot, and Codex standards call sites to all defer to openStandardsFollowUpOnce',
|
|
678
678
|
);
|
|
679
679
|
const directCreates = convergeSource.match(/await spawnStandardsFollowUp\(/g) || [];
|
|
680
680
|
assert.equal(
|
|
@@ -100,7 +100,7 @@ test('the converge call site sets a fix-stalled blocker when an all-null-thread
|
|
|
100
100
|
test('the copilot call site sets a fix-stalled blocker when an all-null-thread resolvedWithoutCommit round cannot progress', () => {
|
|
101
101
|
const copilotBranch = convergeSource.slice(
|
|
102
102
|
convergeSource.indexOf("const fixResult = await applyFixes(head, roundFindings, 'copilot')"),
|
|
103
|
-
convergeSource.indexOf("phase = 'CONVERGE'\n continue\n }\n phase = '
|
|
103
|
+
convergeSource.indexOf("phase = 'CONVERGE'\n continue\n }\n phase = 'CODEX'"),
|
|
104
104
|
);
|
|
105
105
|
assert.match(copilotBranch, /collectFindingThreadIds/);
|
|
106
106
|
assert.match(copilotBranch, /fix stalled/i);
|