{"record":{"id":"81474c6a6205ece8","repo":"paperclipai/paperclip","slug":"cloud-readiness-workflow-identity-does-not-match","errorCode":null,"errorMessage":"Cloud readiness workflow identity does not match.","messagePattern":"Cloud readiness workflow identity does not match\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-source-verification.mjs","lineNumber":26,"sourceCode":"function assertSha(sha) {\n  if (!/^[a-f0-9]{40}$/.test(sha ?? \"\")) throw new Error(\"A full lowercase source SHA is required.\");\n}\n\nfunction trustedRun(run, sha, workflowId) {\n  return run.workflow_id === workflowId && run.path === workflowPath &&\n    run.repository?.full_name === repository && run.head_repository?.full_name === repository &&\n    run.head_sha === sha && run.head_branch === \"master\" && run.event === \"push\" &&\n    Number.isSafeInteger(run.id) && run.id > 0 &&\n    Number.isSafeInteger(run.run_attempt) && run.run_attempt > 0;\n}\n\n// Consume one versioned job, independent of image/migrator availability. A\n// failed image build must not invalidate source checks that already passed.\nexport async function readSourceVerification(sha, api) {\n  assertSha(sha);\n  const workflow = await api(`/repos/${repository}/actions/workflows/cloud-readiness.yml`);\n  if (workflow.path !== workflowPath || !Number.isSafeInteger(workflow.id) || workflow.id < 1) {\n    throw new Error(\"Cloud readiness workflow identity does not match.\");\n  }\n  const listing = await api(`/repos/${repository}/actions/workflows/${workflow.id}/runs?head_sha=${sha}&event=push&branch=master&per_page=100`);\n  if (!Array.isArray(listing.workflow_runs) || !Number.isSafeInteger(listing.total_count) ||\n      listing.total_count < 0 || listing.total_count > 100 || listing.workflow_runs.length !== listing.total_count) {\n    throw new Error(\"Cloud readiness run listing is incomplete.\");\n  }\n  const run = listing.workflow_runs.filter((candidate) => trustedRun(candidate, sha, workflow.id))\n    .sort((a, b) => b.id - a.id)[0];\n  if (!run) return undefined;\n\n  // Attempt-specific jobs prevent an earlier successful attempt from blessing\n  // a later rerun. Keep pagination even though today's matrix fits one page.\n  const jobs = [];\n  for (let page = 1; page <= 10; page += 1) {\n    const batch = await api(`/repos/${repository}/actions/runs/${run.id}/attempts/${run.run_attempt}/jobs?per_page=100&page=${page}`);\n    if (!Array.isArray(batch.jobs)) throw new Error(\"Cloud readiness job listing is malformed.\");\n    jobs.push(...batch.jobs);\n    if (batch.jobs.length < 100) break;","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-source-verification.mjs#L8-L44","documentation":"readSourceVerification looks up the cloud-readiness workflow via the GitHub API and requires the returned workflow object to have path exactly '.github/workflows/cloud-readiness.yml' and a positive safe-integer id. If GitHub resolves a workflow with a different path or an invalid id, this throws — guarding against renamed/moved workflow files or unexpected API responses.","triggerScenarios":"The workflow file was renamed/moved (workflow.path differs), GitHub returns a workflow entry with a missing/zero/negative id, or a same-named workflow at a different path is resolved.","commonSituations":"Refactoring .github/workflows/cloud-readiness.yml to another filename; having two workflows and the API resolving the wrong one by name; mocked api() fixtures missing id/path fields.","solutions":["Restore the workflow to .github/workflows/cloud-readiness.yml or update the workflowPath constant in scripts/cloud-source-verification.mjs to the new path.","Inspect `GET /repos/paperclipai/paperclip/actions/workflows/cloud-readiness.yml` output to see what path/id GitHub actually returns.","Fix test doubles so the api() stub returns { path, id } matching the expected workflow."],"exampleFix":"// before\nconst workflowPath = \".github/workflows/cloud-readiness.yml\"; // file renamed to cloud-ready.yml\n// after\nconst workflowPath = \".github/workflows/cloud-ready.yml\"; // keep constant in sync with the actual file path","handlingStrategy":"try-catch","validationCode":"const wf = await api(\"/repos/paperclipai/paperclip/actions/workflows/cloud-readiness.yml\");\nif (wf?.path !== \".github/workflows/cloud-readiness.yml\") {\n  throw new Error(`Workflow file moved: ${wf?.path}. Update workflowPath in cloud-source-verification.mjs.`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const proof = await waitForSourceVerification(sha, { api });\n} catch (error) {\n  if (/workflow identity does not match/.test(error.message)) {\n    console.error(\"cloud-readiness.yml path/id changed — sync the constant and rerun.\");\n    process.exitCode = 1;\n  } else throw error;\n}","preventionTips":["Treat .github/workflows/cloud-readiness.yml as a pinned contract; rename only with a coordinated script change","Test readSourceVerification with realistic workflow objects (path + positive id)","Check the API response after any workflow refactoring in .github/workflows/"],"tags":["github-api","workflow","validation"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}