claude-dev-env 1.80.0 → 1.81.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/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +116 -30
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +13 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +113 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +467 -0
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +339 -0
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +297 -0
- package/audit-rubrics/CLAUDE.md +3 -2
- package/audit-rubrics/category_rubrics/CLAUDE.md +2 -1
- package/audit-rubrics/category_rubrics/category-a-api-contracts.md +2 -1
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +13 -1
- package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +19 -0
- package/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md +46 -0
- package/audit-rubrics/prompts/CLAUDE.md +2 -1
- package/audit-rubrics/prompts/category-a-api-contracts.md +1 -0
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +19 -7
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +14 -0
- package/audit-rubrics/prompts/category-q-cross-surface-claims.md +51 -0
- package/docs/CODE_RULES.md +2 -2
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/code_rules_annotations_length.py +59 -11
- package/hooks/blocking/code_rules_banned_identifiers.py +48 -9
- package/hooks/blocking/code_rules_command_dispatch.py +140 -0
- package/hooks/blocking/code_rules_docstrings.py +93 -0
- package/hooks/blocking/code_rules_enforcer.py +54 -4
- package/hooks/blocking/code_rules_js_conventions.py +246 -0
- package/hooks/blocking/code_rules_naming_collection.py +5 -0
- package/hooks/blocking/code_rules_test_assertions.py +3 -0
- package/hooks/blocking/code_rules_unused_imports.py +0 -3
- package/hooks/blocking/duplicate_rmtree_helper_blocker.py +7 -0
- package/hooks/blocking/test_code_rules_command_dispatch.py +95 -0
- package/hooks/blocking/test_code_rules_enforcer_annotations.py +20 -2
- package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +10 -2
- package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +8 -4
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +9 -3
- package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +164 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +16 -3
- package/hooks/blocking/test_code_rules_js_conventions.py +167 -0
- package/hooks/hooks_constants/CLAUDE.md +2 -0
- package/hooks/hooks_constants/blocking_check_limits.py +9 -0
- package/hooks/hooks_constants/command_dispatch_constants.py +28 -0
- package/hooks/hooks_constants/js_conventions_constants.py +54 -0
- package/package.json +1 -1
- package/rules/docstring-prose-matches-implementation.md +2 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +43 -1
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +2 -2
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +1 -4
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +100 -4
- package/skills/autoconverge/SKILL.md +2 -2
- package/skills/autoconverge/reference/convergence.md +3 -3
- package/skills/autoconverge/reference/stop-conditions.md +1 -1
- package/skills/autoconverge/workflow/converge.contract.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +245 -1
- package/skills/autoconverge/workflow/converge.mjs +129 -23
|
@@ -16,14 +16,14 @@ export const meta = {
|
|
|
16
16
|
phases: [
|
|
17
17
|
{ title: 'Reuse', detail: 'Before convergence, one reuse lens scans the full diff for reuse improvements that are certain, behaviorally identical, and autonomously implementable, and applies the qualifying ones in one commit' },
|
|
18
18
|
{ title: 'Converge', detail: 'Bugbot + code-review + bug-audit in parallel each round; one clean-coder applies all fixes; loop until all three are clean on a stable HEAD' },
|
|
19
|
-
{ title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to
|
|
19
|
+
{ title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to the configured cap, route findings back into Converge, and when Copilot is down or out of quota log a notice and mark the PR ready with the gate bypassed' },
|
|
20
20
|
{ title: 'Finalize', detail: 'Run check_convergence.py; mark draft=false on a full pass' },
|
|
21
21
|
],
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const CONFIG = {
|
|
25
25
|
maxIterations: 20,
|
|
26
|
-
copilotMaxPolls:
|
|
26
|
+
copilotMaxPolls: 8,
|
|
27
27
|
sharedScripts: '$HOME/.claude/skills/pr-converge/scripts',
|
|
28
28
|
prLoopScripts: '$HOME/.claude/_shared/pr-loop/scripts',
|
|
29
29
|
bugteamRubric: '$HOME/.claude/skills/bugteam/reference/audit-contract.md',
|
|
@@ -200,11 +200,12 @@ async function fixerWithRecovery(head, findings, sourceLabel) {
|
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
202
|
* Spawn a fresh code-editor clean-coder agent for a specific edit task (fix-edit,
|
|
203
|
-
* conflict-edit, repair-edit, repair-commit, standards-edit,
|
|
204
|
-
* commit-recover, verify-recover).
|
|
203
|
+
* conflict-edit, repair-edit, repair-commit, standards-edit,
|
|
204
|
+
* standards-resolve-threads, hardening-commit, commit-recover, verify-recover).
|
|
205
|
+
* Each task carries its own edit instructions.
|
|
205
206
|
* @param {string} task the short task name
|
|
206
207
|
* @param {object} context task-specific context
|
|
207
|
-
* @returns {Promise<object|string>} the structured output, or the transcript string when
|
|
208
|
+
* @returns {Promise<object|string>} the structured output, or the transcript string when a schema-less task ('hardening-commit', 'standards-resolve-threads') runs
|
|
208
209
|
*/
|
|
209
210
|
function runCodeEditorTask(task, context) {
|
|
210
211
|
const label = `code-editor:${task}`
|
|
@@ -293,6 +294,19 @@ function runCodeEditorTask(task, context) {
|
|
|
293
294
|
{ label, phase: 'Converge', schema: STANDARDS_EDIT_SCHEMA, agentType: 'clean-coder' },
|
|
294
295
|
)
|
|
295
296
|
}
|
|
297
|
+
if (task === 'standards-resolve-threads') {
|
|
298
|
+
const findingsBlock = renderFindingsBlock(context.findings)
|
|
299
|
+
const threadIds = context.findings
|
|
300
|
+
.flatMap((each) => collectFindingThreadIds(each))
|
|
301
|
+
.filter((each) => typeof each === 'number')
|
|
302
|
+
return convergeAgent(
|
|
303
|
+
`You are the THREAD-RESOLUTION step for a code-standard-only round on ${prCoordinates}, HEAD ${context.head} (${context.sourceLabel}). This run already filed the deferred-fix follow-up issue ${context.issueUrl}, so this batch's code-standard findings defer to that same issue. Make NO code edits, NO commit, and NO push — only reply to and resolve the review threads this batch carries.\n\n` +
|
|
304
|
+
`Findings:\n${findingsBlock}\n\n` +
|
|
305
|
+
`For each finding that carries a GitHub review comment id (${threadIds.length ? threadIds.join(', ') : 'none this batch'}): post an inline reply via python "${CONFIG.sharedScripts}/post_fix_reply.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} --in-reply-to <id> --body "Code-standard-only finding — deferred to follow-up issue ${context.issueUrl}." Then resolve the thread by its PRRT_ node id (GraphQL lookup on comment databaseId, then resolveReviewThread or the github MCP pull_request_review_write method=resolve_thread — not the numeric comment id).\n\n` +
|
|
306
|
+
`Return a one-line summary naming the threads you resolved.`,
|
|
307
|
+
{ label, phase: 'Converge', agentType: 'clean-coder' },
|
|
308
|
+
)
|
|
309
|
+
}
|
|
296
310
|
if (task === 'hardening-commit') {
|
|
297
311
|
return convergeAgent(
|
|
298
312
|
`You are the COMMIT step opening the environment-hardening PR (${context.sourceLabel}) for the change staged in ${context.hardeningRepoPath} on branch ${context.hardeningBranch}. The edit step left the hooks/rules edits in the working tree and the verify step passed, so a verifier verdict already binds to this exact working tree. Do NOT touch the PR's own branch.\n\n` +
|
|
@@ -1288,7 +1302,7 @@ function runCopilotGate(head) {
|
|
|
1288
1302
|
` gh api --method POST repos/${input.owner}/${input.repo}/pulls/${input.prNumber}/requested_reviewers -f 'reviewers[]=copilot-pull-request-reviewer[bot]'\n` +
|
|
1289
1303
|
`2. Poll for Copilot's review on HEAD ${head}: up to ${CONFIG.copilotMaxPolls} attempts, 360 seconds apart (delay each attempt with "sleep 360", or the PowerShell alternative "Start-Sleep -Seconds 360"). Each attempt: python "${CONFIG.sharedScripts}/fetch_copilot_reviews.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} for the top-level review state, plus gh api "repos/${input.owner}/${input.repo}/pulls/${input.prNumber}/comments" --paginate --slurp for inline comment ids (Copilot's login contains "copilot", case-insensitive). Only count entries whose commit_id starts with ${head}.\n` +
|
|
1290
1304
|
` - Out-of-usage notice on HEAD -> return the down result above (clean:true, down:true) and stop.\n` +
|
|
1291
|
-
` - Copilot review present
|
|
1305
|
+
` - Copilot review present on HEAD whose state is APPROVED, or COMMENTED with no inline findings -> a clean pass: return {sha:${'`'}${head}${'`'}, clean:true, down:false, findings:[]}.\n` +
|
|
1292
1306
|
` - Copilot findings on HEAD -> return them (each with its inline comment id in replyToCommentId; category 'code-standard' for pure CODE_RULES/style violations with no behavioral impact, 'bug' otherwise), clean:false, down:false.\n` +
|
|
1293
1307
|
` - No review after ${CONFIG.copilotMaxPolls} attempts -> Copilot is down for this run (unreachable, or silently out of quota with no notice): return {sha:${'`'}${head}${'`'}, clean:false, down:true, findings:[]}.\n\n` +
|
|
1294
1308
|
`Return strictly the schema.`,
|
|
@@ -1391,19 +1405,35 @@ function isStandardsOnlyRound(findings) {
|
|
|
1391
1405
|
return findings.length > 0 && findings.every((each) => each.category === 'code-standard')
|
|
1392
1406
|
}
|
|
1393
1407
|
|
|
1408
|
+
/**
|
|
1409
|
+
* Decide whether a standards-only round should file the follow-up fix issue and
|
|
1410
|
+
* open the environment-hardening PR. Standards findings are deferred rather than
|
|
1411
|
+
* fixed on this PR, so the same code-standard findings re-surface on every
|
|
1412
|
+
* converge round and on the Copilot gate; without this guard each re-entry files
|
|
1413
|
+
* a fresh duplicate follow-up issue and hardening PR for the one deferred finding
|
|
1414
|
+
* class. The follow-up issue is filed once per convergence run — a round whose
|
|
1415
|
+
* filing succeeds latches the guard, while a round whose filing failed leaves it
|
|
1416
|
+
* clear so a later round retries the filing.
|
|
1417
|
+
* @param {boolean} hasAlreadyFiled true when an earlier standards-only round in this run already filed the follow-up issue
|
|
1418
|
+
* @returns {boolean} true when this round should attempt the follow-up filing
|
|
1419
|
+
*/
|
|
1420
|
+
function shouldOpenStandardsFollowUp(hasAlreadyFiled) {
|
|
1421
|
+
return hasAlreadyFiled !== true
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1394
1424
|
/**
|
|
1395
1425
|
* Build the standards-deferral note for the closing report, naming the
|
|
1396
|
-
* environment-hardening PR only when one was opened this
|
|
1426
|
+
* environment-hardening PR only when one was opened for this run so the note
|
|
1397
1427
|
* never claims a PR the skip paths did not produce.
|
|
1398
1428
|
* @param {number} findingsCount count of deferred code-standard findings
|
|
1399
|
-
* @param {boolean}
|
|
1429
|
+
* @param {boolean} wasHardeningPrOpened true when the hardening PR was opened for this run
|
|
1400
1430
|
* @returns {string} the human-facing deferral note
|
|
1401
1431
|
*/
|
|
1402
|
-
function standardsDeferralNote(findingsCount,
|
|
1432
|
+
function standardsDeferralNote(findingsCount, wasHardeningPrOpened) {
|
|
1403
1433
|
const base = `${findingsCount} code-standard finding(s) deferred to a follow-up fix issue`
|
|
1404
|
-
return
|
|
1434
|
+
return wasHardeningPrOpened
|
|
1405
1435
|
? `${base} plus an environment-hardening PR — verify both land`
|
|
1406
|
-
: `${base} — verify it lands (no environment-hardening PR was opened this
|
|
1436
|
+
: `${base} — verify it lands (no environment-hardening PR was opened for this run)`
|
|
1407
1437
|
}
|
|
1408
1438
|
|
|
1409
1439
|
/**
|
|
@@ -1414,29 +1444,102 @@ function standardsDeferralNote(findingsCount, hardeningPrOpened) {
|
|
|
1414
1444
|
* commit (clean-coder makes one commit, pushes, and opens the DRAFT hardening
|
|
1415
1445
|
* PR). Splitting the edit from the push lets a workflow code-verifier produce the
|
|
1416
1446
|
* verdict the verified-commit gate requires for the cross-repo hardening commit.
|
|
1417
|
-
* This PR's own branch is never touched. When
|
|
1418
|
-
*
|
|
1419
|
-
* skipped.
|
|
1447
|
+
* This PR's own branch is never touched. When a hardening PR already opened for
|
|
1448
|
+
* this run, the edit staged no hardening, or the verify step fails, the follow-up
|
|
1449
|
+
* issue still stands and the commit step is skipped.
|
|
1420
1450
|
* @param {string} head PR HEAD SHA the findings were raised against
|
|
1421
1451
|
* @param {Array<object>} findings deduped code-standard-only findings
|
|
1422
1452
|
* @param {string} sourceLabel short description of where the findings came from
|
|
1423
|
-
* @
|
|
1453
|
+
* @param {boolean} hasHardeningPrAlreadyOpened true when an earlier round already opened the environment-hardening PR for this run, so the verify and commit steps are skipped and no second PR opens while the edit retries the issue filing
|
|
1454
|
+
* @returns {Promise<object>} `{ followUpIssueFiled, issueUrl, hardeningPrOpened }` — followUpIssueFiled true when the standards-edit step returned a non-empty issue URL, issueUrl that filed URL (empty string when the filing failed) so a later reuse-path round can reference it when resolving its own threads, hardeningPrOpened true only when the hardening PR was opened on this call
|
|
1424
1455
|
*/
|
|
1425
|
-
async function spawnStandardsFollowUp(head, findings, sourceLabel) {
|
|
1456
|
+
async function spawnStandardsFollowUp(head, findings, sourceLabel, hasHardeningPrAlreadyOpened) {
|
|
1426
1457
|
const editResult = await runCodeEditorTask('standards-edit', { head, findings, sourceLabel })
|
|
1458
|
+
const followUpIssueFiled = typeof editResult?.issueUrl === 'string' && editResult.issueUrl.length > 0
|
|
1459
|
+
const followUpIssueUrl = followUpIssueFiled ? editResult.issueUrl : ''
|
|
1460
|
+
if (hasHardeningPrAlreadyOpened === true) {
|
|
1461
|
+
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false }
|
|
1462
|
+
}
|
|
1427
1463
|
if (editResult?.hardeningEdited !== true || !editResult?.hardeningRepoPath) {
|
|
1428
|
-
return { hardeningPrOpened: false }
|
|
1464
|
+
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false }
|
|
1429
1465
|
}
|
|
1430
1466
|
const verifyTranscript = await runVerifierTask('hardening-verify', {
|
|
1431
1467
|
head, sourceLabel, hardeningRepoPath: editResult.hardeningRepoPath, hardeningBranch: editResult.hardeningBranch,
|
|
1432
1468
|
})
|
|
1433
1469
|
if (!verdictPassed(verifyTranscript)) {
|
|
1434
|
-
return { hardeningPrOpened: false }
|
|
1470
|
+
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false }
|
|
1435
1471
|
}
|
|
1436
1472
|
await runCodeEditorTask('hardening-commit', {
|
|
1437
1473
|
head, sourceLabel, hardeningRepoPath: editResult.hardeningRepoPath, hardeningBranch: editResult.hardeningBranch, issueUrl: editResult.issueUrl,
|
|
1438
1474
|
})
|
|
1439
|
-
return { hardeningPrOpened: true }
|
|
1475
|
+
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: true }
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
* Report whether any finding in a batch carries a GitHub review thread that needs
|
|
1480
|
+
* an inline reply and resolution.
|
|
1481
|
+
* @param {Array<object>} findings the batch of findings
|
|
1482
|
+
* @returns {boolean} true when at least one finding carries a review comment id
|
|
1483
|
+
*/
|
|
1484
|
+
function findingsCarryThreads(findings) {
|
|
1485
|
+
return findings.some((each) => collectFindingThreadIds(each).length > 0)
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* On the reuse path — after this run already filed the deferred-fix follow-up
|
|
1490
|
+
* issue — reply to and resolve this batch's code-standard review threads against
|
|
1491
|
+
* that same issue. The issue filing and hardening PR stay gated behind the
|
|
1492
|
+
* run-once flags; only the per-batch thread resolution runs here, so a later
|
|
1493
|
+
* standards-only round's bot threads are still marked resolved and the FINALIZE
|
|
1494
|
+
* zero-unresolved-bot-threads gate passes. A batch of in-memory audit findings
|
|
1495
|
+
* that carries no review thread needs no agent, so the resolve step is skipped.
|
|
1496
|
+
* @param {string} head PR HEAD SHA the findings were raised against
|
|
1497
|
+
* @param {Array<object>} findings this batch's deduped code-standard-only findings
|
|
1498
|
+
* @param {string} sourceLabel short description of where the findings came from
|
|
1499
|
+
* @returns {Promise<void>}
|
|
1500
|
+
*/
|
|
1501
|
+
async function resolveStandardsThreadsForBatch(head, findings, sourceLabel) {
|
|
1502
|
+
if (!findingsCarryThreads(findings)) {
|
|
1503
|
+
return
|
|
1504
|
+
}
|
|
1505
|
+
await runCodeEditorTask('standards-resolve-threads', {
|
|
1506
|
+
head, findings, sourceLabel, issueUrl: standardsFollowUpIssueUrl,
|
|
1507
|
+
})
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* File the deferred follow-up issue and open the environment-hardening PR at most
|
|
1512
|
+
* once per convergence run, then reuse them. The two guards latch independently:
|
|
1513
|
+
* the follow-up issue latches only when its filing succeeds, and the hardening PR
|
|
1514
|
+
* latches the moment one opens and never re-opens. A standards-only round whose
|
|
1515
|
+
* issue filing already succeeded — in the converge phase or at the Copilot gate —
|
|
1516
|
+
* skips spawnStandardsFollowUp, resolves this batch's own code-standard review
|
|
1517
|
+
* threads against the already-filed issue via resolveStandardsThreadsForBatch, and
|
|
1518
|
+
* returns the remembered hardening outcome. A round whose issue filing failed
|
|
1519
|
+
* re-runs spawnStandardsFollowUp to retry the filing, passing the remembered
|
|
1520
|
+
* hardening state so an already-opened hardening PR is never re-opened. The run
|
|
1521
|
+
* therefore files one follow-up issue and opens one hardening PR rather than a
|
|
1522
|
+
* fresh duplicate per re-entry — while every round still resolves the review
|
|
1523
|
+
* threads its own findings carry, even when the filing must retry after the
|
|
1524
|
+
* hardening PR has opened.
|
|
1525
|
+
* @param {string} head PR HEAD SHA the findings were raised against
|
|
1526
|
+
* @param {Array<object>} findings deduped code-standard-only findings
|
|
1527
|
+
* @param {string} sourceLabel short description of where the findings came from
|
|
1528
|
+
* @returns {Promise<boolean>} true when a hardening PR was opened for this run
|
|
1529
|
+
*/
|
|
1530
|
+
async function openStandardsFollowUpOnce(head, findings, sourceLabel) {
|
|
1531
|
+
if (!shouldOpenStandardsFollowUp(hasStandardsFollowUpFiled)) {
|
|
1532
|
+
log(`Standards deferral (${sourceLabel}): reusing the follow-up fix issue already filed for this run rather than filing a duplicate; environment-hardening PR ${wasStandardsHardeningPrOpened ? 'was opened for this run' : 'was not opened for this run'}`)
|
|
1533
|
+
await resolveStandardsThreadsForBatch(head, findings, sourceLabel)
|
|
1534
|
+
return wasStandardsHardeningPrOpened
|
|
1535
|
+
}
|
|
1536
|
+
const standardsOutcome = await spawnStandardsFollowUp(head, findings, sourceLabel, wasStandardsHardeningPrOpened)
|
|
1537
|
+
hasStandardsFollowUpFiled = standardsOutcome?.followUpIssueFiled === true
|
|
1538
|
+
if (hasStandardsFollowUpFiled) {
|
|
1539
|
+
standardsFollowUpIssueUrl = standardsOutcome.issueUrl
|
|
1540
|
+
}
|
|
1541
|
+
wasStandardsHardeningPrOpened = wasStandardsHardeningPrOpened || standardsOutcome?.hardeningPrOpened === true
|
|
1542
|
+
return wasStandardsHardeningPrOpened
|
|
1440
1543
|
}
|
|
1441
1544
|
|
|
1442
1545
|
let phase = 'CONVERGE'
|
|
@@ -1448,6 +1551,9 @@ let bugbotDown = input.bugbotDisabled || false
|
|
|
1448
1551
|
let copilotDown = input.copilotDisabled || false
|
|
1449
1552
|
let copilotNote = null
|
|
1450
1553
|
let standardsNote = null
|
|
1554
|
+
let hasStandardsFollowUpFiled = false
|
|
1555
|
+
let wasStandardsHardeningPrOpened = false
|
|
1556
|
+
let standardsFollowUpIssueUrl = ''
|
|
1451
1557
|
let reuseNote = null
|
|
1452
1558
|
|
|
1453
1559
|
const preflightHead = await runGitTask('resolve-head')
|
|
@@ -1501,8 +1607,8 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
1501
1607
|
const findings = roundOutcome.findings
|
|
1502
1608
|
if (isStandardsOnlyRound(findings)) {
|
|
1503
1609
|
log(`Round ${rounds}: ${findings.length} code-standard-only finding(s) — deferring to follow-up PRs and treating the round as passed`)
|
|
1504
|
-
const
|
|
1505
|
-
standardsNote = standardsDeferralNote(findings.length,
|
|
1610
|
+
const wasHardeningPrOpened = await openStandardsFollowUpOnce(head, findings, 'converge-round')
|
|
1611
|
+
standardsNote = standardsDeferralNote(findings.length, wasHardeningPrOpened)
|
|
1506
1612
|
const auditResult = await runGeneralUtilityTask('post-clean-audit', { head })
|
|
1507
1613
|
if (!auditResult?.posted) {
|
|
1508
1614
|
blocker = cleanAuditBlocker(head, auditResult)
|
|
@@ -1564,8 +1670,8 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
1564
1670
|
if (copilotOutcome.kind === 'fix') {
|
|
1565
1671
|
if (isStandardsOnlyRound(copilotOutcome.findings)) {
|
|
1566
1672
|
log(`Copilot raised ${copilotOutcome.findings.length} code-standard-only finding(s) — deferring to follow-up PRs and treating the gate as passed`)
|
|
1567
|
-
const
|
|
1568
|
-
standardsNote = standardsDeferralNote(copilotOutcome.findings.length,
|
|
1673
|
+
const wasHardeningPrOpened = await openStandardsFollowUpOnce(head, copilotOutcome.findings, 'copilot')
|
|
1674
|
+
standardsNote = standardsDeferralNote(copilotOutcome.findings.length, wasHardeningPrOpened)
|
|
1569
1675
|
copilotDown = false
|
|
1570
1676
|
copilotNote = null
|
|
1571
1677
|
phase = 'FINALIZE'
|