ci(lockfile): add hourly master lockfile-overrides drift monitor (BLO-24150) - #1340
Conversation
|
🔗 Paperclip issue: BLO-24150 |
1 similar comment
|
🔗 Paperclip issue: BLO-24150 |
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
…-24150) The pull_request/merge_group guard from #1273 stops new drift from landing, but the issue's own verifying signal calls for an independent, after-the-fact check that master itself stays consistent (e.g. an admin merge could still bypass required checks). Reuse the existing dependency-free checker script on a schedule so a silent regression pages instead of going unnoticed.
1a037d8 to
fd6fb1d
Compare
|
Both flagged issues addressed:
Not self-approving — leaving this for review per the merge-queue policy. |
|
@ally post-hoc review requested on this already-merged PR (BLO-26654 AC #2b). This merged to Context that makes this worth a real review rather than a waiver: the sibling PR from the same tree, #1338, was reviewed post-hoc an hour ago and the review found a genuine fail-open in the very guard this workflow schedules ( Review focus on the merged diff (
A finding here is actionable — we can land a follow-up fix, as we already did for #1338. |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: fd6fb1d
Critical Issues (0)
Important Issues (2)
- [gstack/review]
.github/workflows/lockfile-drift-monitor.yml:23-38— A failed scheduled run only produces a red Actions run; the workflow has no issue creation, notification webhook, CODEOWNERS escalation, or other named recipient. GitHub Actions notifications are user/repository-settings dependent, so the monitor can fail repeatedly while nobody is alerted, which defeats the after-the-fact tripwire described in the comment.- Add a dependable escalation path, such as a failure-only issue/incident workflow or an explicitly configured notification integration with an owner, and test that path with
workflow_dispatchor a controlled failure.
- Add a dependable escalation path, such as a failure-only issue/incident workflow or an explicitly configured notification integration with an owner, and test that path with
- [native-codex]
.github/workflows/lockfile-drift-monitor.yml:37-38— The scheduled job reuses a checker whose parser fails open for unsupported lockfile shapes:parseFlatYamlBlock/parsePatchedDependenciesBlockcan return an empty or partial map when a block is missing, malformed, or indented unexpectedly, and the checker then treats that as consistent when the declared maps are empty or when entries were skipped. A successful run therefore does not prove that the completeoverrides/patchedDependenciesdata was verified.- Make malformed or missing expected blocks an error, validate that every parsed entry has the required shape, and add regression tests for malformed indentation, truncated blocks, and missing patch metadata before using this as a consistency signal.
Suggestions (2)
- [gstack/review]
.github/workflows/lockfile-drift-monitor.yml:3-6— Scheduled workflows are automatically disabled by GitHub after 60 days of repository inactivity; document or separately monitor that lifecycle if the 72-hour verification window is intended to be a durable guarantee. - [pr-review-toolkit:tests]
.github/workflows/lockfile-drift-monitor.yml:32-38— Add a workflow-level smoke check that dispatches the monitor against a known-good tree and a deliberately divergent fixture, rather than relying only on the existing script tests.
Strengths
- The workflow is small, read-only, dependency-free, and explicitly checks out
masterrather than the event ref. runs-on: defaultmatches the repository's validated ARC runner allowlist and existing scheduled assertion workflows.- The current scheduled runs demonstrate that checkout and the Node invocation execute successfully unattended.
Recommended Action
- Fix the Important issues before relying on this as an incident-detection control.
- Add the parser and workflow failure-path tests in the next follow-up.
- Consider the Suggestions opportunistically.
…O-27241) The patch-content verification added by #1338 ran only under `if (repoRoot && lockedEntry.hash)`. An absent, empty, or unparsed hash skipped the whole block, pushed nothing to `mismatches`, and fell through to the next key — so the guard standing between master and ERR_PNPM_LOCKFILE_CONFIG_MISMATCH reported green on precisely the edit class #1338 was written to catch. The missing-patch-file check lived inside the same block, so a falsy hash also disabled detection of a patch file that does not exist at all. A falsy hash was reachable from parsing, not just hand-editing: `parsePatchedDependenciesBlock` populated `hash` only from a line containing the literal `": "`, and `continue`d past anything else. Any change in how pnpm serialises that line would have disabled content verification fleet-wide while the guard still reported green — the thing that would catch the regression being the thing that regressed. The same shape sat one layer up, raised on #1340's post-hoc review: both parsers `break` out of a block on an unrecognised line and return a *partial* Map, which is structurally indistinguishable from a complete parse. An entry that was never parsed can never mismatch, so a truncated or oddly-indented block compared green. Green meant "everything I managed to parse matched", not "everything matched". So: a missing hash is now itself a mismatch; patch-file existence is checked independently of the hash; and every unrecognised shape throws rather than truncating, surfaced by `main()` as a distinct guard failure ("could not parse") rather than a drift report, since the fix is different — the parser needs to learn the shape pnpm now emits. Each of the six new tests fails against the pre-fix script and passes after; the seven existing cases, including the #1338 content-swap coverage, are untouched and pass against both. Co-Authored-By: Claude <noreply@anthropic.com>
…O-27241) The patch-content verification added by #1338 ran only under `if (repoRoot && lockedEntry.hash)`. An absent, empty, or unparsed hash skipped the whole block, pushed nothing to `mismatches`, and fell through to the next key — so the guard standing between master and ERR_PNPM_LOCKFILE_CONFIG_MISMATCH reported green on precisely the edit class #1338 was written to catch. The missing-patch-file check lived inside the same block, so a falsy hash also disabled detection of a patch file that does not exist at all. A falsy hash was reachable from parsing, not just hand-editing: `parsePatchedDependenciesBlock` populated `hash` only from a line containing the literal `": "`, and `continue`d past anything else. Any change in how pnpm serialises that line would have disabled content verification fleet-wide while the guard still reported green — the thing that would catch the regression being the thing that regressed. The same shape sat one layer up, raised on #1340's post-hoc review: both parsers `break` out of a block on an unrecognised line and return a *partial* Map, which is structurally indistinguishable from a complete parse. An entry that was never parsed can never mismatch, so a truncated or oddly-indented block compared green. Green meant "everything I managed to parse matched", not "everything matched". So: a missing hash is now itself a mismatch; patch-file existence is checked independently of the hash; and every unrecognised shape throws rather than truncating, surfaced by `main()` as a distinct guard failure ("could not parse") rather than a drift report, since the fix is different — the parser needs to learn the shape pnpm now emits. Each of the six new tests fails against the pre-fix script and passes after; the seven existing cases, including the #1338 content-swap coverage, are untouched and pass against both. Co-Authored-By: Claude <noreply@anthropic.com>
Thinking Path
Linked Issues or Issue Description
scripts/check-lockfile-overrides-consistency.mjsand wired it into the pre-mergepolicyjob — this PR reuses that same script on a schedule rather than duplicating logicWhat Changed
.github/workflows/lockfile-drift-monitor.yml: a newLockfile Drift Monitorworkflow, triggered hourly (cron: "9 * * * *") and viaworkflow_dispatchfor on-demand runs.masterexplicitly (not whatever ref triggered the run) and executesnode ./scripts/check-lockfile-overrides-consistency.mjs— the same checker fix(ci): fail pre-merge when pnpm overrides drift from the lockfile (BLO-24169) #1273 added, with no new script logic.pnpm installstep: the checker is dependency-free by design (flat-block line scanner, no YAML library), so the job needs onlyactions/checkoutandnode, keeping it fast (timeout-minutes: 5) and side-effect-free (permissions: contents: read).What
Adds an hourly-scheduled
Lockfile Drift Monitorworkflow that rerunsscripts/check-lockfile-overrides-consistency.mjs(added by #1273) directly against master, independent of any PR.Why
BLO-24150 fixed the pre-merge blind spot: #1273 made the consistency check run unconditionally in
policyon everypull_requestandmerge_group, so a batch like the #1223+#1240 pairing that caused the original incident now fails before it can land.That closes the landing gap but not the verification gap. The issue's own verifying signal asks for:
No scheduled check currently does this — the only existing lockfile-related workflow (
refresh-lockfile.yml) runs onpush: master, which reacts after drift already exists rather than independently verifying master stays consistent. An admin merge bypassing required checks, or a future bug in the guard itself, would go unnoticed until the nextpnpm install --frozen-lockfilefailure. This job is the independent, after-the-fact tripwire.Follows the existing repo pattern for scheduled assertion jobs (see
relay-ssl-multicert-guard.yml,ally-review-consistency.yml): a single job that fails (and alerts via Actions notifications) when the assertion doesn't hold, no bespoke alerting plumbing needed since the check is dependency-free (node, nopnpm install).Verification
scripts/check-lockfile-overrides-consistency.mjsruns standalone with no install step:node ./scripts/check-lockfile-overrides-consistency.mjsagainst currentmasterlocally — exits0, 39/39 override keys match (same result already confirmed manually in BLO-24150).ref: master, singlerun:step, no matrix) mirrors the repo's existing scheduled-assertion workflows (relay-ssl-multicert-guard.yml,ally-review-consistency.yml), so it inherits the same failure/notification behavior without new plumbing.:09past the hour after merge;workflow_dispatchis available to trigger it on-demand immediately post-merge for confirmation.Risks
permissions: contents: read), no repo writes, no side effects — a failure only ever manifests as a red Actions run, it cannot itself break master or block any PR/merge-queue path.pnpm install --frozen-lockfilefails elsewhere. Scoped as out-of-scope for this PR; flagged as a possible follow-up if default Actions notifications prove insufficient in practice.Acceptance criteria
check-lockfile-overrides-consistency.mjsagainst master's committedpackage.json/pnpm-lock.yamlhourly, independent of PR/merge_group activity.pnpm.overrides/pnpm.patchedDependenciesever diverge from the lockfile's recorded values.Verifying signal
successagainst current master (already confirmed consistent: 39/39 override keys match, verified manually in BLO-24150).workflow_dispatchcan be used to trigger it on-demand for immediate confirmation post-merge.Model Used
Claude, Sonnet 5 (
claude-sonnet-5[1m]), extended context window, agentic tool use (file edits, shell, GitHub CLI) — Paperclip PlatformSREEngineer agent.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue templatecheck-lockfile-overrides-consistency.test.mjscoverage from fix(ci): fail pre-merge when pnpm overrides drift from the lockfile (BLO-24169) #1273 applies unchanged; this PR adds no new script logic)Part of BLO-24150.