claude-dev-env 1.92.0 → 1.93.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 +5 -39
- package/_shared/CLAUDE.md +2 -1
- package/_shared/advisor/CLAUDE.md +21 -0
- package/_shared/advisor/advisor-protocol.md +142 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/__init__.py +1 -0
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +86 -0
- package/_shared/advisor/scripts/model_tier_run_validator.py +246 -0
- package/_shared/advisor/scripts/pyproject.toml +3 -0
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +302 -0
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +180 -0
- package/_shared/advisor/scripts/tier_model_ids.py +164 -0
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/precatch-rubric.md +65 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +137 -5
- package/_shared/pr-loop/scripts/post_audit_thread.py +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +16 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +2 -1
- package/_shared/pr-loop/scripts/reviewer_availability.py +42 -12
- package/_shared/pr-loop/scripts/reviews_disabled.py +65 -21
- package/_shared/pr-loop/scripts/terminology_sweep.py +69 -21
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/tests/fixtures/{copilot_internal_user_jonecho.json → copilot_internal_user_example.json} +1 -1
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +1 -1
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -2
- package/_shared/pr-loop/scripts/tests/test_copilot_quota.py +8 -8
- package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +18 -5
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +56 -5
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +104 -17
- package/agents/CLAUDE.md +3 -2
- package/agents/clasp-deployment-orchestrator.md +2 -2
- package/agents/code-advisor.md +7 -5
- package/agents/code-verifier.md +6 -2
- package/agents/session-advisor.md +27 -0
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +1 -1
- package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +1 -1
- package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
- package/audit-rubrics/prompts/category-e-dead-code.md +2 -2
- package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
- package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
- package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
- package/audit-rubrics/prompts/category-l-behavior-equivalence.md +2 -2
- package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +2 -2
- package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +2 -2
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +2 -2
- package/bin/CLAUDE.md +1 -0
- package/bin/expand_home_directory_tokens.mjs +75 -0
- package/bin/install.mjs +21 -7
- package/bin/install.test.mjs +220 -10
- package/hooks/CLAUDE.md +1 -1
- package/hooks/blocking/CLAUDE.md +4 -1
- package/hooks/blocking/_path_setup.py +13 -0
- package/hooks/blocking/bash_pre_tool_use_dispatcher.py +281 -0
- package/hooks/blocking/code_rules_dead_module_constant.py +88 -16
- package/hooks/blocking/code_rules_enforcer.py +6 -0
- package/hooks/blocking/code_rules_imports_logging.py +34 -1
- package/hooks/blocking/code_rules_paths_syspath.py +1 -1
- package/hooks/blocking/code_rules_test_layout.py +272 -0
- package/hooks/blocking/nas_ssh_binary_enforcer.py +22 -12
- package/hooks/blocking/pii_prevention_blocker.py +781 -0
- package/hooks/blocking/pii_scanner.py +340 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
- package/hooks/blocking/stop_dispatcher.py +84 -0
- package/hooks/blocking/test__path_setup.py +39 -0
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +61 -0
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +294 -0
- package/hooks/blocking/test_code_rules_enforcer_cross_skill_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +51 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +103 -0
- package/hooks/blocking/test_code_rules_enforcer_dot_test_pattern.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +11 -11
- package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_same_file_inline_duplicate.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_test_layout.py +111 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +76 -0
- package/hooks/blocking/test_destructive_command_blocker.py +29 -9
- package/hooks/blocking/test_gh_body_arg_blocker.py +3 -3
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +96 -39
- package/hooks/blocking/test_pii_prevention_blocker.py +905 -0
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +41 -0
- package/hooks/blocking/test_pii_scanner.py +165 -0
- package/hooks/blocking/test_pr_description_enforcer_pr_number.py +1 -1
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +14 -13
- package/hooks/blocking/test_stop_dispatcher.py +187 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +1 -1
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +3 -3
- package/hooks/blocking/volatile_path_in_post_blocker.py +54 -5
- package/hooks/diagnostic/migrations/README.md +25 -26
- package/hooks/diagnostic/test_hook_log_extractor.py +10 -10
- package/hooks/hooks.json +10 -120
- package/hooks/hooks_constants/CLAUDE.md +12 -3
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +78 -0
- package/hooks/hooks_constants/dead_module_constant_constants.py +1 -0
- package/hooks/hooks_constants/hosted_hook_runner.py +73 -0
- package/hooks/hooks_constants/local_identity.py +182 -0
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +4 -37
- package/hooks/hooks_constants/pii_prevention_constants.py +299 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/python_style_checks_constants.py +15 -0
- package/hooks/hooks_constants/stop_dispatcher_constants.py +26 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +87 -0
- package/hooks/hooks_constants/test_hardcoded_user_path_constants.py +2 -2
- package/hooks/hooks_constants/test_hosted_hook_runner.py +101 -0
- package/hooks/hooks_constants/test_layout_constants.py +28 -0
- package/hooks/hooks_constants/test_local_identity.py +88 -0
- package/hooks/hooks_constants/test_session_env_cleanup_constants.py +1 -1
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +32 -0
- package/hooks/validators/README.md +5 -6
- package/hooks/validators/__init__.py +2 -2
- package/hooks/validators/python_style_checks.py +298 -243
- package/hooks/validators/run_all_validators.py +6 -0
- package/hooks/validators/test_python_style_checks.py +278 -163
- package/package.json +2 -2
- package/rules/CLAUDE.md +7 -1
- package/rules/bdd.md +1 -1
- package/rules/nas-ssh-invocation.md +6 -4
- package/rules/no-justification-noise.md +61 -0
- package/rules/testing.md +0 -2
- package/scripts/test_setup_project_paths.py +1 -1
- package/skills/CLAUDE.md +6 -3
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +2 -2
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/test__path_resolver.py +42 -3
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +11 -11
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +26 -3
- package/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +49 -25
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +113 -35
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +44 -22
- package/skills/autoconverge/reference/gotchas.md +11 -0
- package/skills/autoconverge/reference/stop-conditions.md +23 -13
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +15 -19
- package/skills/autoconverge/workflow/converge.contract.test.mjs +43 -42
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +141 -16
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.mjs +329 -68
- package/skills/autoconverge/workflow/converge.path-aware.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.precatch.test.mjs +152 -0
- package/skills/autoconverge/workflow/converge.run-input.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +15 -15
- package/skills/autoconverge/workflow/test_convergence_summary.py +6 -6
- package/skills/bdd-protocol/SKILL.md +4 -5
- package/skills/bdd-protocol/references/anti-patterns.md +1 -1
- package/skills/bugteam/SKILL.md +8 -0
- package/skills/bugteam/reference/copilot-gap-analysis.md +20 -20
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +1 -1
- package/skills/bugteam/reference/team-setup.md +7 -5
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/copilot-finding-triage/SKILL.md +124 -0
- package/skills/copilot-finding-triage/reference/tier-rubric.md +49 -0
- package/skills/copilot-finding-triage/scripts/conftest.py +8 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
- package/skills/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +32 -0
- package/skills/copilot-finding-triage/scripts/notify_ntfy.py +192 -0
- package/skills/copilot-finding-triage/scripts/test_notify_ntfy.py +141 -0
- package/skills/copilot-finding-triage/templates/notification.md +34 -0
- package/skills/copilot-review/SKILL.md +8 -6
- package/skills/findbugs/SKILL.md +4 -0
- package/skills/fixbugs/SKILL.md +8 -7
- package/skills/gotcha/CLAUDE.md +2 -2
- package/skills/gotcha/SKILL.md +4 -4
- package/skills/log-audit/SKILL.md +4 -6
- package/skills/monitor-open-prs/CLAUDE.md +1 -1
- package/skills/monitor-open-prs/SKILL.md +6 -1
- package/skills/orchestrator/SKILL.md +184 -0
- package/skills/orchestrator-refresh/SKILL.md +37 -0
- package/skills/post-audit-findings/SKILL.md +5 -9
- package/skills/pr-consistency-audit/SKILL.md +5 -1
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +86 -47
- package/skills/pr-converge/reference/CLAUDE.md +1 -1
- package/skills/pr-converge/reference/convergence-gates.md +20 -17
- package/skills/pr-converge/reference/examples.md +63 -47
- package/skills/pr-converge/reference/fix-protocol.md +15 -15
- package/skills/pr-converge/reference/ground-rules.md +11 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +6 -5
- package/skills/pr-converge/reference/per-tick.md +129 -107
- package/skills/pr-converge/reference/state-schema.md +15 -10
- package/skills/pr-converge/scripts/check_convergence.py +5 -4
- package/skills/pr-converge/scripts/test_check_convergence.py +6 -4
- package/skills/pr-fix-protocol/SKILL.md +3 -8
- package/skills/pr-loop-cloud-transport/SKILL.md +102 -0
- package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +40 -0
- package/skills/pr-loop-cloud-transport/reference/substitution-matrix.md +48 -0
- package/skills/pr-loop-lifecycle/SKILL.md +5 -9
- package/skills/pr-scope-resolve/SKILL.md +3 -6
- package/skills/privacy-hygiene/SKILL.md +114 -0
- package/skills/qbug/SKILL.md +8 -8
- package/skills/rebase/SKILL.md +5 -1
- package/skills/refine/SKILL.md +4 -5
- package/skills/reviewer-gates/SKILL.md +7 -11
- package/skills/session-log/SKILL.md +4 -1
- package/skills/skill-builder/SKILL.md +3 -6
- package/skills/structure-prompt/SKILL.md +4 -5
- package/skills/team-advisor/SKILL.md +32 -164
- package/skills/test_markdown_link_integrity.py +10 -6
- package/skills/update/SKILL.md +5 -1
- package/skills/usage-pause/SKILL.md +14 -5
- package/skills/usage-pause/scripts/resolve_usage_window.py +83 -5
- package/skills/usage-pause/scripts/test_resolve_usage_window.py +185 -17
- package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +4 -3
- package/skills/verified-build/SKILL.md +4 -9
- package/skills/team-advisor-refresh/SKILL.md +0 -25
|
@@ -39,7 +39,7 @@ test('a path-scoped run defers to a step that names a different repository direc
|
|
|
39
39
|
test('convergeAgent prepends the worktree directive for the active repo path', () => {
|
|
40
40
|
const agentDefinition = sliceBetween('const convergeAgent =', '\nconst PRE_COMMIT_GATE_STEP');
|
|
41
41
|
assert.match(agentDefinition, /worktreeDirective\(activeRepoPath\)/);
|
|
42
|
-
assert.match(agentDefinition, /
|
|
42
|
+
assert.match(agentDefinition, /HEADLESS_EDIT_PREAMBLE/);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
test('the run binds activeRepoPath from input.repoPath after the input is parsed', () => {
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
|
|
10
|
+
function functionSource(functionName) {
|
|
11
|
+
const functionStart = convergeSource.indexOf(`function ${functionName}(`);
|
|
12
|
+
assert.notEqual(functionStart, -1, `expected ${functionName} to exist`);
|
|
13
|
+
const nextMatch = /\n(?:async )?function /.exec(convergeSource.slice(functionStart + 1));
|
|
14
|
+
const functionEnd =
|
|
15
|
+
nextMatch === null ? convergeSource.length : functionStart + 1 + nextMatch.index;
|
|
16
|
+
return convergeSource.slice(functionStart, functionEnd);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sliceBetween(startNeedle, endNeedle) {
|
|
20
|
+
const sliceStart = convergeSource.indexOf(startNeedle);
|
|
21
|
+
assert.notEqual(sliceStart, -1, `expected ${startNeedle} to exist`);
|
|
22
|
+
const sliceEnd = convergeSource.indexOf(endNeedle, sliceStart + startNeedle.length);
|
|
23
|
+
assert.notEqual(sliceEnd, -1, `expected ${endNeedle} to exist after ${startNeedle}`);
|
|
24
|
+
return convergeSource.slice(sliceStart, sliceEnd);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('runStaticSweep exists and runs after the preflight-git no-SHA guard and before the parallel lenses', () => {
|
|
28
|
+
assert.match(convergeSource, /function runStaticSweep\(/, 'expected the static-sweep lens builder to exist');
|
|
29
|
+
const sweepCallIndex = convergeSource.indexOf('await runStaticSweep(');
|
|
30
|
+
assert.notEqual(sweepCallIndex, -1, 'expected the CONVERGE round to invoke runStaticSweep');
|
|
31
|
+
const noShaGuardIndex = convergeSource.indexOf('preflight-git agent returned no SHA');
|
|
32
|
+
const parallelIndex = convergeSource.indexOf('const lenses = await parallel(');
|
|
33
|
+
assert.ok(
|
|
34
|
+
noShaGuardIndex !== -1 && noShaGuardIndex < sweepCallIndex,
|
|
35
|
+
'expected the static sweep to run after the no-SHA guard',
|
|
36
|
+
);
|
|
37
|
+
assert.ok(
|
|
38
|
+
sweepCallIndex < parallelIndex,
|
|
39
|
+
'expected the static sweep to run before the opus reading lenses so they review sweep-clean code',
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('the static-sweep prompt names the deterministic gates and never invokes the gh CLI', () => {
|
|
44
|
+
const body = functionSource('runStaticSweep');
|
|
45
|
+
assert.match(body, /code_rules_gate\.py/, 'expected the CODE_RULES gate to be named');
|
|
46
|
+
assert.match(body, /--base/, 'expected the gate to run against the origin/main base');
|
|
47
|
+
assert.match(body, /\bruff\b/, 'expected ruff to be named');
|
|
48
|
+
assert.match(body, /\bmypy\b/, 'expected mypy to be named');
|
|
49
|
+
assert.match(body, /\bpytest\b/, 'expected stem-matched pytest to be named');
|
|
50
|
+
assert.doesNotMatch(body, /\bgh\b/, 'expected the sweep to use only local commands so it runs in a cloud session');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('the static-sweep runs on the sonnetMedium tier via convergeReadOnlyAgent with the Explore agent type', () => {
|
|
54
|
+
const body = functionSource('runStaticSweep');
|
|
55
|
+
assert.match(body, /convergeReadOnlyAgent\(/, 'expected the sweep to spawn through the read-only preamble');
|
|
56
|
+
assert.match(body, /renderLensDiffContext\(preflightResult\)/, 'expected the sweep to inject the preflight changed-file context');
|
|
57
|
+
assert.match(body, /label: 'lens:static-sweep'/);
|
|
58
|
+
assert.match(body, /agentType: 'Explore'/);
|
|
59
|
+
assert.match(body, /\.\.\.TIERS\.sonnetMedium/, 'expected the deterministic sweep to run on the cheaper sonnet tier');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('a static-sweep finding routes through applyFixes with the static-sweep label and nulls head before continue', () => {
|
|
63
|
+
const sweepBranch = sliceBetween('await runStaticSweep(', "const lenses = await parallel(");
|
|
64
|
+
assert.match(sweepBranch, /applyFixes\(head, sweep\.findings, 'static-sweep'\)/, 'expected sweep findings to route through applyFixes');
|
|
65
|
+
assert.match(sweepBranch, /detectFixProgress\(sweepFix, head, false\)/, 'expected the sweep fix to check fix progress with no thread-bearing finding');
|
|
66
|
+
const headNullIndex = sweepBranch.indexOf('head = null');
|
|
67
|
+
const continueIndex = sweepBranch.indexOf('continue');
|
|
68
|
+
assert.notEqual(headNullIndex, -1, 'expected the sweep-findings branch to null head so the next round reviews the fixed code');
|
|
69
|
+
assert.ok(headNullIndex < continueIndex, 'expected head to be invalidated before the sweep branch continues');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('runAuditLens carries the adversarial second pass and points at the pre-catch rubric', () => {
|
|
73
|
+
const body = functionSource('runAuditLens');
|
|
74
|
+
assert.match(body, /missed at least 3 P1/, 'expected the mandatory adversarial second pass');
|
|
75
|
+
assert.match(body, /CONFIG\.precatchRubric/, 'expected the audit lens to point at the pre-catch rubric for the doc-parity/test/PR-description lanes');
|
|
76
|
+
assert.doesNotMatch(body, /nothing new is an acceptable/i, 'expected a bare "nothing new" to be rejected');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('runSelfReviewLens exists on opusMedium via convergeReadOnlyAgent and covers the three semantic lanes', () => {
|
|
80
|
+
const body = functionSource('runSelfReviewLens');
|
|
81
|
+
assert.match(body, /convergeReadOnlyAgent\(/, 'expected the self-review lens to spawn through the read-only preamble');
|
|
82
|
+
assert.match(body, /renderLensDiffContext\(preflightResult\)/, 'expected the self-review lens to inject the preflight changed-file context');
|
|
83
|
+
assert.match(body, /CONFIG\.precatchRubric/, 'expected the self-review lens to read the pre-catch rubric');
|
|
84
|
+
assert.match(body, /pr-consistency-audit/, 'expected the self-review lens to reuse the pr-consistency-audit method');
|
|
85
|
+
assert.match(body, /category-o-docstring-vs-impl-drift/, 'expected the self-review lens to cite the category-o drift rubric');
|
|
86
|
+
assert.match(body, /parity/i, 'expected the doc-vs-code parity lane');
|
|
87
|
+
assert.match(body, /Test-assertion completeness/i, 'expected the test-assertion completeness lane');
|
|
88
|
+
assert.match(body, /PR-description-vs-diff/i, 'expected the PR-description parity lane');
|
|
89
|
+
assert.match(body, /label: 'lens:self-review'/);
|
|
90
|
+
assert.match(body, /\.\.\.TIERS\.opusMedium/, 'expected the semantic lens to stay on opus');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('the round parallel array holds exactly the three internal lenses and no Bugbot slot', () => {
|
|
94
|
+
const parallelArray = sliceBetween('const lenses = await parallel([', '])');
|
|
95
|
+
assert.match(parallelArray, /runCodeReviewLens\(head, reviewerAvailability\)/);
|
|
96
|
+
assert.match(parallelArray, /runAuditLens\(head, reviewerAvailability\)/);
|
|
97
|
+
assert.match(parallelArray, /runSelfReviewLens\(head, reviewerAvailability\)/);
|
|
98
|
+
assert.doesNotMatch(parallelArray, /runBugbotLens/, 'expected Bugbot to leave the per-round parallel and become a terminal gate');
|
|
99
|
+
assert.doesNotMatch(parallelArray, /isBugbotDownPreSpawn/, 'expected the pre-spawn Bugbot-down decision to be gone from the round');
|
|
100
|
+
assert.doesNotMatch(convergeSource, /const isBugbotDownPreSpawn/, 'expected no pre-spawn Bugbot-down decision anywhere in the round');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('both the CONVERGE clean branch and the standards-only branch route to the terminal BUGBOT phase', () => {
|
|
104
|
+
const convergeBranch = sliceBetween("if (phase === 'CONVERGE') {", "if (phase === 'BUGBOT') {");
|
|
105
|
+
const toBugbot = convergeBranch.match(/phase = 'BUGBOT'/g) || [];
|
|
106
|
+
assert.equal(toBugbot.length, 2, 'expected the standards-only and all-clean branches to both set phase = BUGBOT');
|
|
107
|
+
assert.doesNotMatch(convergeBranch, /phase = 'COPILOT'/, 'expected the CONVERGE block to route to BUGBOT, not straight to COPILOT');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('the terminal BUGBOT phase skips runBugbotLens when the reviewer-availability gate reports Bugbot down', () => {
|
|
111
|
+
const bugbotPhase = sliceBetween("if (phase === 'BUGBOT') {", "if (phase === 'COPILOT') {");
|
|
112
|
+
const availabilityGateIndex = bugbotPhase.indexOf('resolveReviewerDown(reviewerAvailability?.bugbot, input.bugbotDisabled || false)');
|
|
113
|
+
const gateCallIndex = bugbotPhase.indexOf('await runBugbotLens(head, reviewerAvailability)');
|
|
114
|
+
assert.notEqual(availabilityGateIndex, -1, 'expected the terminal gate to consult resolveReviewerDown before spawning the Bugbot lens');
|
|
115
|
+
assert.notEqual(gateCallIndex, -1, 'expected the terminal gate to call runBugbotLens when Bugbot is enabled');
|
|
116
|
+
assert.ok(availabilityGateIndex < gateCallIndex, 'expected the availability gate to short-circuit before any Bugbot agent spawns');
|
|
117
|
+
const beforeGate = bugbotPhase.slice(availabilityGateIndex, gateCallIndex);
|
|
118
|
+
assert.match(beforeGate, /bugbotDown = true/, 'expected the availability-down path to mark bugbotDown');
|
|
119
|
+
assert.match(beforeGate, /phase = 'COPILOT'/, 'expected the availability-down path to advance to the Copilot gate with no agent');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('the terminal BUGBOT phase classifies via the shared reviewer-gate classifier and routes each outcome', () => {
|
|
123
|
+
const bugbotPhase = sliceBetween("if (phase === 'BUGBOT') {", "if (phase === 'COPILOT') {");
|
|
124
|
+
assert.match(bugbotPhase, /classifyReviewerGateOutcome\(bugbot\)/, 'expected the terminal gate to reuse the shared reviewer-gate classifier');
|
|
125
|
+
assert.match(bugbotPhase, /applyFixes\(head, bugbotOutcome\.findings, 'bugbot'\)/, 'expected a Bugbot fix outcome to route through applyFixes');
|
|
126
|
+
assert.match(bugbotPhase, /head = null\n\s*phase = 'CONVERGE'/, 'expected a Bugbot fix to invalidate head and re-enter CONVERGE');
|
|
127
|
+
assert.match(bugbotPhase, /phase = 'COPILOT'/, 'expected a clean or down Bugbot verdict to advance to the Copilot gate');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('convergeReadOnlyAgent exists and the read-only preamble omits the rm-shape paragraph the edit preamble keeps', () => {
|
|
131
|
+
assert.match(convergeSource, /const convergeReadOnlyAgent = \(prompt, options\) =>/, 'expected the read-only agent spawner to exist');
|
|
132
|
+
const preambleRegion = convergeSource.slice(
|
|
133
|
+
convergeSource.indexOf('const HEADLESS_EDIT_PREAMBLE ='),
|
|
134
|
+
convergeSource.indexOf('\nlet activeRepoPath'),
|
|
135
|
+
);
|
|
136
|
+
const preambleModule = new Function(
|
|
137
|
+
`${preambleRegion}\nreturn { HEADLESS_EDIT_PREAMBLE, HEADLESS_READONLY_PREAMBLE };`,
|
|
138
|
+
)();
|
|
139
|
+
assert.match(preambleModule.HEADLESS_EDIT_PREAMBLE, /rm shape rules/, 'expected the edit preamble to retain the rm-shape rules');
|
|
140
|
+
assert.doesNotMatch(preambleModule.HEADLESS_READONLY_PREAMBLE, /rm shape rules/, 'expected the read-only preamble to drop the rm-shape paragraph');
|
|
141
|
+
assert.ok(
|
|
142
|
+
preambleModule.HEADLESS_READONLY_PREAMBLE.length < preambleModule.HEADLESS_EDIT_PREAMBLE.length,
|
|
143
|
+
'expected the read-only preamble to be shorter than the edit preamble',
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('COMMIT_RECOVERY_MAX_ATTEMPTS is one and bounds the commitWithRecovery loop', () => {
|
|
148
|
+
assert.match(convergeSource, /const COMMIT_RECOVERY_MAX_ATTEMPTS = 1/, 'expected a dedicated commit-recovery cap of one');
|
|
149
|
+
assert.match(convergeSource, /const FIX_RECOVERY_MAX_ATTEMPTS = 2/, 'expected the wider verify-recovery cap to stay at two');
|
|
150
|
+
const commitBody = functionSource('commitWithRecovery');
|
|
151
|
+
assert.match(commitBody, /attempt < COMMIT_RECOVERY_MAX_ATTEMPTS/, 'expected commitWithRecovery to bound its loop by the dedicated cap');
|
|
152
|
+
});
|
|
@@ -22,7 +22,7 @@ const productionModule = new Function(
|
|
|
22
22
|
|
|
23
23
|
const { normalizeRunInput, classifyRunInput } = productionModule;
|
|
24
24
|
|
|
25
|
-
const VALID_COORDINATES = { owner: 'jl-cmd', repo: 'claude-
|
|
25
|
+
const VALID_COORDINATES = { owner: 'jl-cmd', repo: 'claude-dev-env', prNumber: 543 };
|
|
26
26
|
|
|
27
27
|
test('an object payload passes through unchanged', () => {
|
|
28
28
|
assert.deepEqual(normalizeRunInput(VALID_COORDINATES), VALID_COORDINATES);
|
|
@@ -59,13 +59,13 @@ test('classifyRunInput blocks a null payload with a structured blocker', () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
test('classifyRunInput blocks a payload missing owner', () => {
|
|
62
|
-
const classified = classifyRunInput({ repo: 'claude-
|
|
62
|
+
const classified = classifyRunInput({ repo: 'claude-dev-env', prNumber: 543 });
|
|
63
63
|
assert.equal(classified.input, null);
|
|
64
64
|
assert.match(classified.blocker, /coordinates/i);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
test('classifyRunInput blocks a payload missing prNumber', () => {
|
|
68
|
-
const classified = classifyRunInput({ owner: 'jl-cmd', repo: 'claude-
|
|
68
|
+
const classified = classifyRunInput({ owner: 'jl-cmd', repo: 'claude-dev-env' });
|
|
69
69
|
assert.equal(classified.input, null);
|
|
70
70
|
assert.match(classified.blocker, /coordinates/i);
|
|
71
71
|
});
|
|
@@ -38,15 +38,15 @@ const SCRIPT_PATH = '/abs/skills/autoconverge/workflow/converge.mjs';
|
|
|
38
38
|
|
|
39
39
|
function validEntry(prNumber) {
|
|
40
40
|
return {
|
|
41
|
-
owner: '
|
|
42
|
-
repo: '
|
|
41
|
+
owner: 'example-org',
|
|
42
|
+
repo: 'example-repo',
|
|
43
43
|
prNumber,
|
|
44
44
|
repoPath: `/worktrees/pr-${prNumber}`,
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function validArgs() {
|
|
49
|
-
return { convergeScriptPath: SCRIPT_PATH, prs: [validEntry(
|
|
49
|
+
return { convergeScriptPath: SCRIPT_PATH, prs: [validEntry(101), validEntry(102)] };
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
test('an object payload passes through unchanged', () => {
|
|
@@ -69,7 +69,7 @@ test('valid coordinates classify with no blocker and keep every PR entry', () =>
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
test('a missing convergeScriptPath is blocked', () => {
|
|
72
|
-
const classified = classifyMultiInput({ prs: [validEntry(
|
|
72
|
+
const classified = classifyMultiInput({ prs: [validEntry(101)] });
|
|
73
73
|
assert.equal(classified.input, null);
|
|
74
74
|
assert.match(classified.blocker, /convergeScriptPath/);
|
|
75
75
|
});
|
|
@@ -93,14 +93,14 @@ test('a non-array prs value is blocked', () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
test('an entry missing prNumber is blocked', () => {
|
|
96
|
-
const badEntry = { owner: '
|
|
96
|
+
const badEntry = { owner: 'example-org', repo: 'example-repo', repoPath: '/w/x' };
|
|
97
97
|
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: [badEntry] });
|
|
98
98
|
assert.equal(classified.input, null);
|
|
99
99
|
assert.match(classified.blocker, /missing/);
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
test('an entry missing repoPath is blocked', () => {
|
|
103
|
-
const badEntry = { owner: '
|
|
103
|
+
const badEntry = { owner: 'example-org', repo: 'example-repo', prNumber: 101 };
|
|
104
104
|
const classified = classifyMultiInput({ convergeScriptPath: SCRIPT_PATH, prs: [badEntry] });
|
|
105
105
|
assert.equal(classified.input, null);
|
|
106
106
|
assert.match(classified.blocker, /missing/);
|
|
@@ -113,29 +113,29 @@ test('a null payload is blocked', () => {
|
|
|
113
113
|
});
|
|
114
114
|
|
|
115
115
|
test('childRunInput forwards the PR coordinates to the child run', () => {
|
|
116
|
-
const childArgs = childRunInput(validEntry(
|
|
117
|
-
assert.equal(childArgs.owner, '
|
|
118
|
-
assert.equal(childArgs.repo, '
|
|
119
|
-
assert.equal(childArgs.prNumber,
|
|
120
|
-
assert.equal(childArgs.repoPath, '/worktrees/pr-
|
|
116
|
+
const childArgs = childRunInput(validEntry(101));
|
|
117
|
+
assert.equal(childArgs.owner, 'example-org');
|
|
118
|
+
assert.equal(childArgs.repo, 'example-repo');
|
|
119
|
+
assert.equal(childArgs.prNumber, 101);
|
|
120
|
+
assert.equal(childArgs.repoPath, '/worktrees/pr-101');
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
test('childRunInput forwards copilotDisabled true when the entry opts out', () => {
|
|
124
|
-
const optedOutEntry = { ...validEntry(
|
|
124
|
+
const optedOutEntry = { ...validEntry(101), copilotDisabled: true };
|
|
125
125
|
assert.equal(childRunInput(optedOutEntry).copilotDisabled, true);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
test('childRunInput defaults copilotDisabled to false when the entry omits it', () => {
|
|
129
|
-
assert.equal(childRunInput(validEntry(
|
|
129
|
+
assert.equal(childRunInput(validEntry(101)).copilotDisabled, false);
|
|
130
130
|
});
|
|
131
131
|
|
|
132
132
|
test('childRunInput forwards bugbotDisabled true when the entry opts out', () => {
|
|
133
|
-
const optedOutEntry = { ...validEntry(
|
|
133
|
+
const optedOutEntry = { ...validEntry(101), bugbotDisabled: true };
|
|
134
134
|
assert.equal(childRunInput(optedOutEntry).bugbotDisabled, true);
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
test('childRunInput defaults bugbotDisabled to false when the entry omits it', () => {
|
|
138
|
-
assert.equal(childRunInput(validEntry(
|
|
138
|
+
assert.equal(childRunInput(validEntry(101)).bugbotDisabled, false);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
test('the malformed-input blocker return carries an empty allDeferredPrs list', async () => {
|
|
@@ -32,7 +32,7 @@ def test_prompt_carries_pr_coordinates_and_round_count() -> None:
|
|
|
32
32
|
"""Should name the PR coordinates and the aggregated round count."""
|
|
33
33
|
prompt = convergence_summary.build_summary_prompt(
|
|
34
34
|
owner="jl-cmd",
|
|
35
|
-
repo="claude-
|
|
35
|
+
repo="claude-dev-env",
|
|
36
36
|
pr_number=581,
|
|
37
37
|
round_count=39,
|
|
38
38
|
findings=SAMPLE_FINDINGS,
|
|
@@ -41,17 +41,17 @@ def test_prompt_carries_pr_coordinates_and_round_count() -> None:
|
|
|
41
41
|
copilot_note=None,
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
assert "owner=jl-cmd repo=claude-
|
|
45
|
-
assert "https://github.com/jl-cmd/claude-
|
|
44
|
+
assert "owner=jl-cmd repo=claude-dev-env PR #581" in prompt
|
|
45
|
+
assert "https://github.com/jl-cmd/claude-dev-env/pull/581" in prompt
|
|
46
46
|
assert "convergence in 39 round(s)" in prompt
|
|
47
|
-
assert "gh api repos/jl-cmd/claude-
|
|
47
|
+
assert "gh api repos/jl-cmd/claude-dev-env/pulls/581" in prompt
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def test_prompt_instructs_plain_json_object_not_structured_output() -> None:
|
|
51
51
|
"""Should instruct a plain JSON object answer and never name the StructuredOutput tool."""
|
|
52
52
|
prompt = convergence_summary.build_summary_prompt(
|
|
53
53
|
owner="jl-cmd",
|
|
54
|
-
repo="claude-
|
|
54
|
+
repo="claude-dev-env",
|
|
55
55
|
pr_number=581,
|
|
56
56
|
round_count=39,
|
|
57
57
|
findings=SAMPLE_FINDINGS,
|
|
@@ -68,7 +68,7 @@ def test_prompt_numbers_every_finding_with_severity_and_location() -> None:
|
|
|
68
68
|
"""Should list each aggregated finding numbered with severity, file, and line."""
|
|
69
69
|
prompt = convergence_summary.build_summary_prompt(
|
|
70
70
|
owner="jl-cmd",
|
|
71
|
-
repo="claude-
|
|
71
|
+
repo="claude-dev-env",
|
|
72
72
|
pr_number=581,
|
|
73
73
|
round_count=39,
|
|
74
74
|
findings=SAMPLE_FINDINGS,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bdd-protocol
|
|
3
3
|
description: >-
|
|
4
|
-
On-demand BDD depth
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
scenario quality. Triggers: bdd-protocol, Example Mapping, BDD anti-patterns, §7.6.
|
|
4
|
+
On-demand BDD depth: Example Mapping, scenario quality and anti-patterns, solo
|
|
5
|
+
minimal BDD, and outside-in test layout. Use when expanding `<behavior_protocol>`,
|
|
6
|
+
writing executable specifications, or giving 'the one where' examples.
|
|
7
|
+
Triggers: bdd-protocol, Example Mapping, BDD anti-patterns, §7.6.
|
|
9
8
|
---
|
|
10
9
|
@~/.claude/skills/bdd-protocol/references/example-mapping.md
|
|
11
10
|
@~/.claude/skills/bdd-protocol/references/anti-patterns.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Scenario quality and anti-patterns (§7.6 and related)
|
|
2
2
|
|
|
3
|
-
Use this as a **checklist** when writing or reviewing scenarios and developer-facing specifications. Phrasing follows *BDD in Action* 2e and the catalog summarized in [claude-
|
|
3
|
+
Use this as a **checklist** when writing or reviewing scenarios and developer-facing specifications. Phrasing follows *BDD in Action* 2e and the catalog summarized in [claude-dev-env#82](https://github.com/jl-cmd/claude-dev-env/issues/82).
|
|
4
4
|
|
|
5
5
|
## Positive targets (what "good" looks like)
|
|
6
6
|
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -18,6 +18,14 @@ The audit agent loads the A–P category rubrics from
|
|
|
18
18
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` alongside
|
|
19
19
|
[`PROMPTS.md`](PROMPTS.md) and produces a single outcome XML per loop.
|
|
20
20
|
|
|
21
|
+
## Transport check (before any GitHub step)
|
|
22
|
+
|
|
23
|
+
Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
|
|
24
|
+
scope is resolved, run `gh api repos/<owner>/<repo> --jq .permissions.push`
|
|
25
|
+
and take `true` as the pass. When any check fails, run the
|
|
26
|
+
`pr-loop-cloud-transport` skill first and route every `gh` operation in this
|
|
27
|
+
skill through its substitution matrix.
|
|
28
|
+
|
|
21
29
|
## Pre-flight
|
|
22
30
|
|
|
23
31
|
```bash
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Status: HISTORICAL — patch plan partially superseded.**
|
|
4
4
|
>
|
|
5
|
-
> This file documents the read-only investigation that motivated the K–N rubric addendum and the Step 2.6 / Step 3.5 standards-review phases. **The audit-rubric portion of the patch plan was reverted in [PR #292](https://github.com/jl-cmd/claude-
|
|
5
|
+
> This file documents the read-only investigation that motivated the K–N rubric addendum and the Step 2.6 / Step 3.5 standards-review phases. **The audit-rubric portion of the patch plan was reverted in [PR #292](https://github.com/jl-cmd/claude-dev-env/pull/292):** PROMPTS.md no longer carries the K–N addendum or the `<copilot_derived_addendum_source>` block, and SKILL.md no longer runs the Step 2.6 / Step 3.5 standards-review phases.
|
|
6
6
|
>
|
|
7
7
|
> **What replaced K–N (partial coverage):** several judgment-heavy categories now have deterministic validators in `code_rules_enforcer.py` — `check_collection_prefix` (category K), `check_library_print` (category L), `check_inline_literal_collections`, `check_loop_variable_naming`, `check_parameter_annotations`, `check_return_annotations`. These validators are exercised by `bugteam_code_rules_gate.py` during the bugteam pre-audit / loop, and by the shipped repo `pre-commit` / `pre-push` git hooks that invoke that gate. They are **not** wired into the repo's default `PreToolUse` Write|Edit hook list in `packages/claude-dev-env/hooks/hooks.json`, so they do not block at Write/Edit time by default — enforcement comes from the gate (audit-time) and the git hooks (commit/push-time).
|
|
8
8
|
>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
This file is the reference record produced by the read-only investigation of why the `/bugteam` audit/fix loop and `bugteam_code_rules_gate.py` repeatedly miss the classes of code-quality violations that the GitHub Copilot reviewer raises on follow-up review rounds. It is written so future bugteam runs can skim the inventory, the rubric/validator coverage diffs, and the patch plan without re-deriving them.
|
|
14
14
|
|
|
15
|
-
Sources of truth cited below: `~/.claude/docs/CODE_RULES.md`, `~/.claude/CLAUDE.md`, `~/.claude/rules/file-global-constants.md`, `~/.claude/skills/bugteam/SKILL.md`, `~/.claude/skills/bugteam/PROMPTS.md`, `~/.claude/skills/bugteam/CONSTRAINTS.md`, `~/.claude/skills/bugteam/scripts/bugteam_code_rules_gate.py`, `~/.claude/skills/bugteam/scripts/bugteam_preflight.py`, `~/.claude/hooks/blocking/code_rules_enforcer.py`, plus `gh api repos/
|
|
15
|
+
Sources of truth cited below: `~/.claude/docs/CODE_RULES.md`, `~/.claude/CLAUDE.md`, `~/.claude/rules/file-global-constants.md`, `~/.claude/skills/bugteam/SKILL.md`, `~/.claude/skills/bugteam/PROMPTS.md`, `~/.claude/skills/bugteam/CONSTRAINTS.md`, `~/.claude/skills/bugteam/scripts/bugteam_code_rules_gate.py`, `~/.claude/skills/bugteam/scripts/bugteam_preflight.py`, `~/.claude/hooks/blocking/code_rules_enforcer.py`, plus `gh api repos/example-org/example-repo/pulls/{101,102}/comments` filtered to author `Copilot`.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -23,22 +23,22 @@ Sources of truth cited below: `~/.claude/docs/CODE_RULES.md`, `~/.claude/CLAUDE.
|
|
|
23
23
|
Copilot review comments fetched with:
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
gh api repos/
|
|
27
|
-
gh api repos/
|
|
26
|
+
gh api repos/example-org/example-repo/pulls/101/comments --paginate --jq '.[] | select(.user.login == "Copilot")'
|
|
27
|
+
gh api repos/example-org/example-repo/pulls/102/comments --paginate --jq '.[] | select(.user.login == "Copilot")'
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
PR #
|
|
30
|
+
PR #101 head SHA `29117309cf4ec1e83883160d8c819e0843f9c3ac` (merged). PR #102 head SHA `c9c935a96cc59d39d623dc7eddda3d341007607c` (merged); Copilot reviewed at original commit `e4abf52c3a6c724b4e64bfed0d979cd60a2c8bf0`.
|
|
31
31
|
|
|
32
32
|
| finding_id | pr_number | file:line | rule_cited | severity | mapped_bugteam_category_letter | layer_that_should_have_caught_it |
|
|
33
33
|
|---|---|---|---|---|---|---|
|
|
34
|
-
| 3153098661 |
|
|
35
|
-
| 3153098689 |
|
|
36
|
-
| 3153098727 |
|
|
37
|
-
| 3153098762 |
|
|
38
|
-
| 3153098782 |
|
|
39
|
-
| 3153475246 |
|
|
40
|
-
| 3153475297 |
|
|
41
|
-
| 3153475331 |
|
|
34
|
+
| 3153098661 | 101 | `shared_utils/theme_db/writer.py:158` | Magic values — column-name string literals (`"theme_name"`, `"content_id"`, …) hardcoded inside SQL builder bodies (CODE_RULES.md §⚡ Magic values; J) | P1 | J | bugteam pre-flight gate (`bugteam_code_rules_gate.py`) — string literals are masked by `_mask_string_literals_preserving_length`, so the number-only magic-value detector never sees them; bugteam audit rubric (J) names the rule but the regex passes |
|
|
35
|
+
| 3153098689 | 101 | `shared_utils/theme_db/writer.py:361` | File-length / function-length / SRP smell (`write_outcome` >30 lines, module 446 lines exceeds the 400-line advisory) | P2 | none | initial+final standards-review phases bracketing the loop — no rubric letter exists; the existing 400/1000-line advisory in `code_rules_enforcer.advise_file_line_count` is stderr-only and never blocks |
|
|
36
|
+
| 3153098727 | 101 | `shared_utils/theme_db/summary.py:267` | Library `print()` calls in non-CLI library code (CODE_RULES.md §Self-Documenting Code; "Make output stream explicit" practice) | P1 | none | harness PreToolUse hook (`code_rules_enforcer.py`) — no detector exists for `print(`/`sys.stdout.write` inside library modules |
|
|
37
|
+
| 3153098762 | 101 | `shared_utils/theme_db/summary.py:263` | PR-description spec drift — banner missing column-header rows promised in PR body | P2 | A (loosely) | initial+final standards-review phases — A is signature/async-shaped, not "promised behavior vs implementation" shaped |
|
|
38
|
+
| 3153098782 | 101 | `shared_utils/theme_db/writer.py:125` | Naming clarity — `_is_set_column_value` reads like it excludes `None` but does not | P2 | none | bugteam audit rubric addendum — no naming-clarity category in A–J |
|
|
39
|
+
| 3153475246 | 102 | `shared_utils/theme_db/config/constants.py:91` | Collection naming — `THEMES_INSERT_REQUIRED_COLUMN_NAMES` is a tuple and must use the `ALL_*` prefix (CODE_RULES.md §5 "Extended naming rules" → Collections: `all_orders`, `all_users`) | P1 | none | harness PreToolUse hook AND bugteam pre-flight gate — no detector for the collection-prefix rule. Reproduced at `e4abf52c`; renamed to `ALL_THEMES_INSERT_REQUIRED_COLUMN_NAMES` in the merged PR head |
|
|
40
|
+
| 3153475297 | 102 | `shared_utils/theme_db/writer.py:296` | Collection naming — parameter `column_value_pairs` is a list and must use the `all_*` prefix | P1 | none | harness PreToolUse hook AND bugteam pre-flight gate — same gap as 3153475246 for parameter names |
|
|
41
|
+
| 3153475331 | 102 | `shared_utils/theme_db/summary.py:206` (referenced; underlying defect is in `shared_utils/theme_db/tracker.py` `flush()`) | Wrapper plumb-through — public `tracker.flush(*, output_folder)` silently drops `loud_banner_stream` that `ThemeDatabaseWriteSummary.flush(*, output_folder, loud_banner_stream=None)` accepts | P1 | A (loosely) | bugteam audit rubric addendum — A focuses on signatures/return types, not on whether a wrapper preserves the optional kwargs of the function it delegates to |
|
|
42
42
|
|
|
43
43
|
### Rubric coverage diff
|
|
44
44
|
|
|
@@ -77,7 +77,7 @@ Source: every detector inside `~/.claude/hooks/blocking/code_rules_enforcer.py`
|
|
|
77
77
|
- `check_skip_decorators_in_tests` / `check_existence_check_tests` / `check_constant_equality_tests` (enforcer.py:1079-1277) — test-file checks. Catches none.
|
|
78
78
|
- `check_unused_optional_parameters` (enforcer.py:1854-1933) — same-file callers must vary an optional parameter. Does not catch the inverse case where a wrapper drops an underlying function's optional kwarg from its own signature. Misses 3153475331.
|
|
79
79
|
- `check_incomplete_mocks` / `check_duplicated_format_patterns` (enforcer.py:1746-1851) — advisory-only on test files / repeated f-strings. Catches none.
|
|
80
|
-
- `advise_file_line_count` (enforcer.py:379-399) — soft advisory at 400, hard advisory at 1000; never blocking. Triggers on PR #
|
|
80
|
+
- `advise_file_line_count` (enforcer.py:379-399) — soft advisory at 400, hard advisory at 1000; never blocking. Triggers on PR #101 `writer.py` (446 lines) but is stderr-only, so the bugteam gate exit code stays 0 and the audit/fix loop never sees the signal as a finding. Misses 3153098689 in practice.
|
|
81
81
|
|
|
82
82
|
`bugteam_code_rules_gate.py` adds no detectors of its own — `run_gate` (gate.py:379-443) only filters violations to the changed-line set and routes blocking/advisory output. Extending the gate without extending `validate_content` (or adding sibling detectors invoked from `run_gate`) cannot close the gap.
|
|
83
83
|
|
|
@@ -97,7 +97,7 @@ The bugteam audit rubric (PROMPTS.md §bug_categories A–J) and the determinist
|
|
|
97
97
|
|
|
98
98
|
> **Historical — the rubric/phases portion below was reverted in PR #292.** Sections (a) PROMPTS.md K–N addendum, (b) Step 2.6 INITIAL standards review, and the FINAL standards review (Step 3.5) were reverted and replaced by deterministic validators (see status banner at the top of this file). Sections that landed deterministic validators in `code_rules_enforcer.py` (collection prefix, library print, string-literal magic, inline literal collections, loop-variable naming, parameter/return annotations) remain in force. Treat the rubric/phase sections below as record-of-what-was-tried, not a current to-do list.
|
|
99
99
|
|
|
100
|
-
Each section names exactly one target file, the literal text or regex to add, and a verification step that re-runs the new detection against the PR #
|
|
100
|
+
Each section names exactly one target file, the literal text or regex to add, and a verification step that re-runs the new detection against the PR #101 / PR #102 diffs.
|
|
101
101
|
|
|
102
102
|
### a. `~/.claude/skills/bugteam/PROMPTS.md`
|
|
103
103
|
|
|
@@ -143,7 +143,7 @@ Each section names exactly one target file, the literal text or regex to add, an
|
|
|
143
143
|
|
|
144
144
|
<copilot_derived_addendum_source>
|
|
145
145
|
The K–N categories were added after Copilot raised real findings on
|
|
146
|
-
PR #
|
|
146
|
+
PR #101 (writer.py / summary.py) and PR #102 (constants.py / writer.py /
|
|
147
147
|
tracker.py) that converged "0 P0 / 0 P1 / 0 P2" under the original
|
|
148
148
|
A–J rubric. See ~/.claude/skills/bugteam/reference/copilot-gap-analysis.md
|
|
149
149
|
for the inventory and the validators that now back categories K and L.
|
|
@@ -158,7 +158,7 @@ Each section names exactly one target file, the literal text or regex to add, an
|
|
|
158
158
|
python $HOME/.claude/skills/bugteam/scripts/bugteam_code_rules_gate.py /tmp/pr70_writer.py /tmp/pr70_summary.py /tmp/pr73_constants.py /tmp/pr73_writer.py
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
Run after the K and L deterministic detectors land in §c/d below; the categories M and N stay rubric-only and are exercised by replaying PR #
|
|
161
|
+
Run after the K and L deterministic detectors land in §c/d below; the categories M and N stay rubric-only and are exercised by replaying PR #101 / PR #102 through `/bugteam` with the new PROMPTS.md and observing that the audit posts findings keyed to lines 158 (M), 125 (rubric N — naming clarity), 263 (rubric N — PR-description drift), 361 (initial/final standards review — file length), and 206 (N — wrapper plumb-through).
|
|
162
162
|
|
|
163
163
|
### b. `~/.claude/skills/bugteam/SKILL.md`
|
|
164
164
|
|
|
@@ -228,7 +228,7 @@ Loop log:
|
|
|
228
228
|
**Verification step:**
|
|
229
229
|
|
|
230
230
|
```
|
|
231
|
-
gh pr diff
|
|
231
|
+
gh pr diff 101 -R example-org/example-repo > /tmp/pr101.diff && gh pr diff 102 -R example-org/example-repo > /tmp/pr102.diff && /bugteam --dry-run --replay 101 102
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
Followed by reading `<team_temp_dir>/pr-70/initial-review.outcomes.xml` and `final-review.outcomes.xml` to confirm Copilot finding ids 3153098661, 3153098689, 3153098727, 3153098762, 3153098782, 3153475246, 3153475297, 3153475331 are surfaced as new entries in either the INITIAL or FINAL review (or both) and that `Step 3.5` upgrades the exit reason when any P0/P1 finding remains.
|
|
@@ -505,7 +505,7 @@ Expected output after the patch lands: a list containing `Line 91: Collection co
|
|
|
505
505
|
|
|
506
506
|
## Cross-references
|
|
507
507
|
|
|
508
|
-
- Inventory data sources: live `gh api repos/
|
|
509
|
-
- Original-commit content used to confirm violations: PR #
|
|
508
|
+
- Inventory data sources: live `gh api repos/example-org/example-repo/pulls/{101,102}/comments` filtered to `Copilot`; verbatim bodies preserved in the inventory table above.
|
|
509
|
+
- Original-commit content used to confirm violations: PR #101 head `29117309cf4ec1e83883160d8c819e0843f9c3ac`; PR #102 review-time commit `e4abf52c3a6c724b4e64bfed0d979cd60a2c8bf0`; PR #102 merged head `c9c935a96cc59d39d623dc7eddda3d341007607c`.
|
|
510
510
|
- CODE_RULES.md sections invoked by the patch plan: §⚡ Magic values; §5 Extended naming rules (collections `all_orders`, `all_users`); §6.5 File length guidance; §7 Right-Sized Engineering; §10 No redundant data fetches (used as analogue for wrapper plumb-through).
|
|
511
511
|
- Constraints honored: `gh-body-file` (no `gh ... --body` calls in the new code paths), `no-shell-substitution` (no `$(...)` in the verification commands above; multi-step shell flows are written as separate Bash invocations or `&&`-chained literal strings).
|
|
@@ -6,7 +6,7 @@ Audit every category A through P. Each returns Shape A finding or Shape B verifi
|
|
|
6
6
|
|
|
7
7
|
Hit a wall on this step? Spawn a background agent to record it. Keep working.
|
|
8
8
|
|
|
9
|
-
1. Spawn `Agent(run_in_background=true)` — brief it: check `jl-cmd/claude-
|
|
9
|
+
1. Spawn `Agent(run_in_background=true)` — brief it: check `jl-cmd/claude-dev-env` for an open PR touching this file, add a commit to it, or create a fresh branch + worktree + draft PR.
|
|
10
10
|
2. Tell the agent your obstacle and what fixed it. It writes the entry below.
|
|
11
11
|
|
|
12
12
|
## Obstacles
|
|
@@ -137,7 +137,7 @@ Shape: dict keyed by `finding_id`. Each value: `{finding_comment_id, finding_com
|
|
|
137
137
|
|
|
138
138
|
When `/bugteam` runs against multiple PRs across repos, each PR operates as a
|
|
139
139
|
logical sub-team within the master `bugteam` team. The PR identity token is
|
|
140
|
-
`{owner}/{repo}#{N}` (e.g. `jl-cmd/claude-
|
|
140
|
+
`{owner}/{repo}#{N}` (e.g. `jl-cmd/claude-dev-env#422`). The slugified form
|
|
141
141
|
comes from `slugify_pr_identity(owner, repo, pr_number)` in
|
|
142
142
|
[`_path_resolver.py`](../../_shared/pr-loop/scripts/_path_resolver.py).
|
|
143
143
|
|
|
@@ -154,7 +154,9 @@ Self-claiming by task subject prefix keeps each teammate on its assigned PR.
|
|
|
154
154
|
run` issue comment via the Step 2.5 issue-comments fallback endpoint after
|
|
155
155
|
every successful FIX push, to re-trigger Cursor's bugbot on the new commit.
|
|
156
156
|
|
|
157
|
-
**
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
**Availability gate.** Cursor Bugbot is off by default. The FIX subagent
|
|
158
|
+
posts the re-trigger only when bugbot is enabled for the run — when
|
|
159
|
+
`CLAUDE_REVIEWS_ENABLED` (comma-separated, case-insensitive,
|
|
160
|
+
whitespace-tolerant) lists `bugbot` and `CLAUDE_REVIEWS_DISABLED` does not.
|
|
161
|
+
Otherwise it skips the re-trigger post even when the flag is present, and the
|
|
162
|
+
rest of the bugteam audit/fix cycle continues unchanged.
|
|
@@ -5,7 +5,7 @@ joined without a space only inside unfinished markdown link targets), then
|
|
|
5
5
|
wrap with textwrap. Preserves fenced blocks verbatim.
|
|
6
6
|
|
|
7
7
|
Same algorithm as ``packages/claude-dev-env/skills/pr-converge/scripts/reflow_skill_md.py``
|
|
8
|
-
from https://github.com/jl-cmd/claude-
|
|
8
|
+
from https://github.com/jl-cmd/claude-dev-env/pull/349 (branch
|
|
9
9
|
``cursor/pr-converge-skill-line-wrap-ecd1``); ``SKILL_PATH`` points at bugteam
|
|
10
10
|
``SKILL.md``. Link reference definitions (``[id]: url``) are treated as logical
|
|
11
11
|
line starts so they are not merged with prior paragraphs.
|