mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-16 16:36:53 +08:00
fix: address code-review findings in github-coordination actions
- Remove circular validation-status check in applyValidate that prevented fresh claims (validation='pending') from ever reaching 'passed' - Add staleCoordinationLabels helper to compute coordination:* labels to remove on state transitions; replaces hardcoded removeLabels:[] across all six editIssue call sites - Fix duplicate label writes in applySync: syncIssueLabels already calls editIssue for labels, so the follow-up editIssue now only updates body - Skip acquireLock finding: store.acquireLock does not exist; comment updated to explain why the fix was not applied Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33f2219307
commit
273b82c8ba
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const { loadPolicy } = require('./policy');
|
const { loadPolicy } = require('./policy');
|
||||||
const { mergeIssueBody, normalizeBodyForComparison } = require('./parsing');
|
const { mergeIssueBody, normalizeBodyForComparison } = require('./parsing');
|
||||||
const { getIssue, listIssues, editIssue, commentIssue } = require('./gh-api');
|
const { getIssue, listIssues, editIssue, commentIssue, normalizeLabels } = require('./gh-api');
|
||||||
const {
|
const {
|
||||||
assertIssueClaimable,
|
assertIssueClaimable,
|
||||||
buildIssueComment,
|
buildIssueComment,
|
||||||
@ -28,13 +28,21 @@ function assertValidIssueNumber(issueNumber) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function staleCoordinationLabels(issue, nextLabels, policy) {
|
||||||
|
const epicLabel = policy.labels && policy.labels.epic;
|
||||||
|
return normalizeLabels(issue.labels).filter(l =>
|
||||||
|
(l.startsWith('coordination:') || l === epicLabel) && !nextLabels.includes(l)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// applyClaim performs a read (getIssue) → check (assertIssueClaimable) → write
|
// applyClaim performs a read (getIssue) → check (assertIssueClaimable) → write
|
||||||
// (editIssue) sequence that is NOT atomic. Two concurrent callers can both read
|
// (editIssue) sequence that is NOT atomic. Two concurrent callers can both read
|
||||||
// an unclaimed issue, pass the check, and both succeed — resulting in a
|
// an unclaimed issue, pass the check, and both succeed — resulting in a
|
||||||
// double-claim. Until a store.acquireLock(repo, issueNumber) API is available,
|
// double-claim. A code-review finding suggested fixing this via
|
||||||
// callers should prevent races via external serialization (e.g. a GitHub
|
// context.store.acquireLock(repo, issueNumber), but that API does not exist in
|
||||||
// branch-protection rule that allows only one actor at a time, or a
|
// store.js; adding a call to it would throw at runtime. Left as-is until a
|
||||||
// serialized job queue).
|
// locking primitive is available — callers should prevent races via external
|
||||||
|
// serialization (e.g. a serialized job queue or GitHub branch-protection rule).
|
||||||
function applyClaim(repo, issueNumber, options = {}, context = {}) {
|
function applyClaim(repo, issueNumber, options = {}, context = {}) {
|
||||||
assertValidRepo(repo);
|
assertValidRepo(repo);
|
||||||
assertValidIssueNumber(issueNumber);
|
assertValidIssueNumber(issueNumber);
|
||||||
@ -63,7 +71,7 @@ function applyClaim(repo, issueNumber, options = {}, context = {}) {
|
|||||||
editIssue(repo, issueNumber, {
|
editIssue(repo, issueNumber, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
commentIssue(repo, issueNumber, buildIssueComment('claimed', repo, issueNumber, nextState), options);
|
commentIssue(repo, issueNumber, buildIssueComment('claimed', repo, issueNumber, nextState), options);
|
||||||
upsertCoordinationWorkItem(store, repo, trackedIssue, nextState, 'claim', { ...context, policy });
|
upsertCoordinationWorkItem(store, repo, trackedIssue, nextState, 'claim', { ...context, policy });
|
||||||
@ -96,12 +104,8 @@ function applySync(repo, options = {}, context = {}) {
|
|||||||
const body = mergeIssueBody(issue, nextState, policy);
|
const body = mergeIssueBody(issue, nextState, policy);
|
||||||
const labelPlan = syncIssueLabels(repo, issue, nextState, policy, options);
|
const labelPlan = syncIssueLabels(repo, issue, nextState, policy, options);
|
||||||
|
|
||||||
if (!options.dryRun && (normalizeBodyForComparison(body) !== normalizeBodyForComparison(issue.body) || labelPlan.addLabels.length > 0 || labelPlan.removeLabels.length > 0)) {
|
if (!options.dryRun && normalizeBodyForComparison(body) !== normalizeBodyForComparison(issue.body)) {
|
||||||
editIssue(repo, issue.number, {
|
editIssue(repo, issue.number, { body }, options);
|
||||||
body,
|
|
||||||
addLabels: labelPlan.addLabels,
|
|
||||||
removeLabels: labelPlan.removeLabels,
|
|
||||||
}, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshot = upsertCoordinationWorkItem(store, repo, trackedIssue, nextState, 'sync', { ...context, policy });
|
const snapshot = upsertCoordinationWorkItem(store, repo, trackedIssue, nextState, 'sync', { ...context, policy });
|
||||||
@ -132,12 +136,6 @@ function applyValidate(repo, issueNumber, options = {}, context = {}, existingIs
|
|||||||
const missingDependencies = dependencyNumbers.filter(number => !closedDependencies.includes(number));
|
const missingDependencies = dependencyNumbers.filter(number => !closedDependencies.includes(number));
|
||||||
const validations = [];
|
const validations = [];
|
||||||
|
|
||||||
if (policy.validation.required && state.validation !== 'passed') {
|
|
||||||
validations.push({ check: 'validation-status', ok: false, detail: `validation=${state.validation}` });
|
|
||||||
} else {
|
|
||||||
validations.push({ check: 'validation-status', ok: true, detail: state.validation });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (missingDependencies.length > 0) {
|
if (missingDependencies.length > 0) {
|
||||||
validations.push({ check: 'dependencies', ok: false, detail: missingDependencies.join(',') });
|
validations.push({ check: 'dependencies', ok: false, detail: missingDependencies.join(',') });
|
||||||
} else {
|
} else {
|
||||||
@ -160,7 +158,7 @@ function applyValidate(repo, issueNumber, options = {}, context = {}, existingIs
|
|||||||
editIssue(repo, issueNumber, {
|
editIssue(repo, issueNumber, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
upsertCoordinationWorkItem(context.store || null, repo, trackedIssue, nextState, 'validate', { ...context, policy });
|
upsertCoordinationWorkItem(context.store || null, repo, trackedIssue, nextState, 'validate', { ...context, policy });
|
||||||
}
|
}
|
||||||
@ -201,7 +199,7 @@ function applyPublish(repo, issueNumber, options = {}, context = {}) {
|
|||||||
editIssue(repo, issueNumber, {
|
editIssue(repo, issueNumber, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
commentIssue(repo, issueNumber, buildIssueComment('published', repo, issueNumber, nextState, {
|
commentIssue(repo, issueNumber, buildIssueComment('published', repo, issueNumber, nextState, {
|
||||||
validation: 'passed',
|
validation: 'passed',
|
||||||
@ -234,7 +232,7 @@ function applyReview(repo, issueNumber, options = {}, context = {}) {
|
|||||||
editIssue(repo, issueNumber, {
|
editIssue(repo, issueNumber, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
commentIssue(repo, issueNumber, buildIssueComment('reviewed', repo, issueNumber, nextState, {
|
commentIssue(repo, issueNumber, buildIssueComment('reviewed', repo, issueNumber, nextState, {
|
||||||
review: reviewState,
|
review: reviewState,
|
||||||
@ -270,7 +268,7 @@ function applyDecompose(repo, issueNumber, options = {}, context = {}) {
|
|||||||
editIssue(repo, issueNumber, {
|
editIssue(repo, issueNumber, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
commentIssue(repo, issueNumber, buildIssueComment('decomposed', repo, issueNumber, nextState, {
|
commentIssue(repo, issueNumber, buildIssueComment('decomposed', repo, issueNumber, nextState, {
|
||||||
taskCount: String(tasks.length),
|
taskCount: String(tasks.length),
|
||||||
@ -320,7 +318,7 @@ function applyUnblock(repo, options = {}, context = {}) {
|
|||||||
editIssue(repo, issue.number, {
|
editIssue(repo, issue.number, {
|
||||||
body,
|
body,
|
||||||
addLabels: trackedIssue.labels,
|
addLabels: trackedIssue.labels,
|
||||||
removeLabels: [],
|
removeLabels: staleCoordinationLabels(issue, trackedIssue.labels, policy),
|
||||||
}, options);
|
}, options);
|
||||||
commentIssue(repo, issue.number, buildIssueComment('unblocked', repo, issue.number, nextState, {
|
commentIssue(repo, issue.number, buildIssueComment('unblocked', repo, issue.number, nextState, {
|
||||||
dependencies: dependencyNumbers.length > 0 ? dependencyNumbers.join(',') : 'none',
|
dependencies: dependencyNumbers.length > 0 ? dependencyNumbers.join(',') : 'none',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user