From 6505194469ee91bcff60a5b2007afbc72cb7ef4d Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Mon, 14 Sep 2026 19:28:20 +0200 Subject: [PATCH] codex-repository-review-context: Exclude generated catalog noise --- .github/workflows/copilot_issue.yml | 2 +- .github/workflows/copilot_issue_comment.yml | 2 +- .github/workflows/copilot_pull_request.yml | 2 +- .../copilot_pull_request_comment.yml | 2 +- scripts/validate-workflow-contract.cjs | 23 +++++++++++++++ .../validate_workflow_contract.test.ts | 28 +++++++++++++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/.github/workflows/copilot_issue.yml b/.github/workflows/copilot_issue.yml index 463154e22..fb5053491 100644 --- a/.github/workflows/copilot_issue.yml +++ b/.github/workflows/copilot_issue.yml @@ -28,7 +28,7 @@ jobs: repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }} issues-locale: ${{ vars.ISSUES_LOCALE || '' }} pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }} - ai-ignore-files: build/* + ai-ignore-files: build/*,specs/CATALOG.md bugbot-dry-run: ${{ vars.BUGBOT_DRY_RUN || 'false' }} bugbot-effort: ${{ vars.BUGBOT_EFFORT || 'smart' }} bugbot-review-drafts: ${{ vars.BUGBOT_REVIEW_DRAFTS || 'false' }} diff --git a/.github/workflows/copilot_issue_comment.yml b/.github/workflows/copilot_issue_comment.yml index a3d8787f3..95ceb59cd 100644 --- a/.github/workflows/copilot_issue_comment.yml +++ b/.github/workflows/copilot_issue_comment.yml @@ -29,7 +29,7 @@ jobs: repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }} issues-locale: ${{ vars.ISSUES_LOCALE || '' }} pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }} - ai-ignore-files: build/* + ai-ignore-files: build/*,specs/CATALOG.md debug: ${{ vars.DEBUG }} agent-provider: ${{ vars.AGENT_PROVIDER || 'codex' }} agent-model-provider: ${{ vars.AGENT_MODEL_PROVIDER || 'openai' }} diff --git a/.github/workflows/copilot_pull_request.yml b/.github/workflows/copilot_pull_request.yml index 4ffbc4a10..f4b08879b 100644 --- a/.github/workflows/copilot_pull_request.yml +++ b/.github/workflows/copilot_pull_request.yml @@ -61,7 +61,7 @@ jobs: repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }} issues-locale: ${{ vars.ISSUES_LOCALE || '' }} pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }} - ai-ignore-files: build/* + ai-ignore-files: build/*,specs/CATALOG.md bugbot-severity: ${{ vars.BUGBOT_SEVERITY || 'low' }} bugbot-comment-limit: ${{ vars.BUGBOT_COMMENT_LIMIT || '20' }} bugbot-fix-verify-commands: ${{ vars.BUGBOT_AUTOFIX_VERIFY_COMMANDS }} diff --git a/.github/workflows/copilot_pull_request_comment.yml b/.github/workflows/copilot_pull_request_comment.yml index ad9eef3d6..fe09821e9 100644 --- a/.github/workflows/copilot_pull_request_comment.yml +++ b/.github/workflows/copilot_pull_request_comment.yml @@ -29,7 +29,7 @@ jobs: repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }} issues-locale: ${{ vars.ISSUES_LOCALE || '' }} pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }} - ai-ignore-files: build/* + ai-ignore-files: build/*,specs/CATALOG.md debug: ${{ vars.DEBUG }} agent-provider: ${{ vars.AGENT_PROVIDER || 'codex' }} agent-model-provider: ${{ vars.AGENT_MODEL_PROVIDER || 'openai' }} diff --git a/scripts/validate-workflow-contract.cjs b/scripts/validate-workflow-contract.cjs index e2c8bc600..e1f9a7647 100644 --- a/scripts/validate-workflow-contract.cjs +++ b/scripts/validate-workflow-contract.cjs @@ -99,6 +99,13 @@ const REPOSITORY_LOCALE_INPUTS = Object.freeze({ 'issues-locale': "${{ vars.ISSUES_LOCALE || '' }}", 'pull-requests-locale': "${{ vars.PULL_REQUESTS_LOCALE || '' }}", }); +const REPOSITORY_BUGBOT_CONTEXT_EXCLUSIONS = 'build/*,specs/CATALOG.md'; +const REPOSITORY_BUGBOT_WORKFLOW_FILES = new Set([ + 'copilot_issue.yml', + 'copilot_issue_comment.yml', + 'copilot_pull_request.yml', + 'copilot_pull_request_comment.yml', +]); function workflowFiles(directory) { return readdirSync(directory) @@ -228,6 +235,20 @@ function assertRepositoryLocaleInputs(file, workflow) { } } +function assertRepositoryBugbotContextExclusions(file, workflow) { + const relativeFile = relativeWorkflow(file); + if (!relativeFile.startsWith('.github/workflows/') + || !REPOSITORY_BUGBOT_WORKFLOW_FILES.has(path.basename(file))) return; + for (const [jobId, job] of Object.entries(workflow.jobs ?? {})) { + for (const [stepIndex, step] of (job.steps ?? []).entries()) { + if (!isCopilotAction(step)) continue; + if (step.with?.['ai-ignore-files'] !== REPOSITORY_BUGBOT_CONTEXT_EXCLUSIONS) { + throw new Error(`${relativeFile} job ${jobId} step ${stepIndex + 1} must exclude generated build and specification catalog artifacts from Bugbot context exactly.`); + } + } + } +} + function assertAgentInstallationPrerequisites(file, workflow) { const manifestFile = path.basename(file); if (!WORKFLOW_AGENT_ROLES[manifestFile] && manifestFile !== 'agent-cli-provisioning.yml') return; @@ -859,6 +880,7 @@ function validateWorkflow(file, workflow) { assertSequentialMutationWorkflow(file, workflow); assertAgentInputs(file, workflow); assertRepositoryLocaleInputs(file, workflow); + assertRepositoryBugbotContextExclusions(file, workflow); assertAgentInstallationPrerequisites(file, workflow); assertNoJobLevelSecrets(file, workflow); assertAgentWorkflowPermissions(file, workflow); @@ -910,6 +932,7 @@ module.exports = { assertCopilotActionInputs, assertAgentInputs, assertRepositoryLocaleInputs, + assertRepositoryBugbotContextExclusions, assertAgentInstallationPrerequisites, assertNoJobLevelSecrets, assertAgentWorkflowPermissions, diff --git a/src/tooling/__tests__/validate_workflow_contract.test.ts b/src/tooling/__tests__/validate_workflow_contract.test.ts index 1b19ec026..7ccbc5f02 100644 --- a/src/tooling/__tests__/validate_workflow_contract.test.ts +++ b/src/tooling/__tests__/validate_workflow_contract.test.ts @@ -13,6 +13,7 @@ interface ContractModule { assertAgentWorkflowPermissions(file: string, workflow: Record): void; assertAgentInstallationPrerequisites(file: string, workflow: Record): void; assertRepositoryLocaleInputs(file: string, workflow: Record): void; + assertRepositoryBugbotContextExclusions(file: string, workflow: Record): void; assertLightweightBranchSyncWorkflow(file: string, workflow: Record): void; MIN_QUEUE_JOB_TIMEOUT_MINUTES: number; DEPLOYMENT_VALIDATION_TIMEOUT_MINUTES: number; @@ -37,6 +38,7 @@ const { assertAgentWorkflowPermissions, assertAgentInstallationPrerequisites, assertRepositoryLocaleInputs, + assertRepositoryBugbotContextExclusions, assertLightweightBranchSyncWorkflow, MIN_QUEUE_JOB_TIMEOUT_MINUTES, DEPLOYMENT_VALIDATION_TIMEOUT_MINUTES, @@ -671,6 +673,32 @@ describe('workflow contract validator', () => { ); }); + it('keeps generated artifacts out of this repository Bugbot context', () => { + for (const fileName of [ + 'copilot_issue.yml', + 'copilot_issue_comment.yml', + 'copilot_pull_request.yml', + 'copilot_pull_request_comment.yml', + ]) { + const file = path.join(process.cwd(), '.github/workflows', fileName); + const workflow = yaml.load(readFileSync(file, 'utf8')) as MutationWorkflow; + expect(() => assertRepositoryBugbotContextExclusions(file, workflow)).not.toThrow(); + } + }); + + it('rejects repository workflows that restore generated catalog noise', () => { + const file = path.join(process.cwd(), '.github/workflows', 'copilot_pull_request.yml'); + const workflow = yaml.load(readFileSync(file, 'utf8')) as MutationWorkflow; + const action = workflow.jobs['copilot-pull-requests'].steps.find( + (step: { uses?: string }) => step.uses === './', + ); + action.with['ai-ignore-files'] = 'build/*'; + + expect(() => validateWorkflow(file, workflow)).toThrow( + 'must exclude generated build and specification catalog artifacts', + ); + }); + it('requires Node.js 24 before every workflow path that may install a pinned agent CLI', () => { for (const directory of ['.github/workflows', 'setup/workflows']) { for (const fileName of [