Skip to content

fix(lanes): scan every lane and eval upload for credentials before it leaves - #11806

Merged
kyle-sexton merged 16 commits into
mainfrom
fix/scan-every-lane-upload
Nov 1, 2026
Merged

kyle-sexton merged 16 commits into
mainfrom
fix/scan-every-lane-upload

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Refs: #8295

Summary

Lane and eval uploads are public artifacts on this public repository. Before this change, only uploads behind the read runner's leak-scan were checked for credentials, so a token in a lane transcript, review handoff or eval result could be published. Every lane and eval upload now runs lib/publish-gate.mjs scanLeak over each file and file name first, and the upload is withheld on a hit. The ci-workflows half (review handoff and shadow report uploads) is melodic-software/ci-workflows#945.

Fix

  • New action .github/actions/scan-upload/ (scan-upload.mjs) runs scanLeak over every file and file name an upload is about to send. A credential, hidden assignment, link, compressed file or glob fails the step without printing the value.
  • Each gated upload's if: requires the scan's clean output, so an always() upload is withheld too. Four ${{ }}-form upload conditions that the appended gate made always true are unwrapped.
  • The lane transcript action scans the plaintext execution file before the seal step.
  • Jobs whose head checkout replaces .base (the read runner's scan job, the write runner's run job) stage scan-upload.mjs and lib/publish-gate.mjs into RUNNER_TEMP from the default branch before that checkout and run the staged copy, so no head-supplied code runs the scan.
  • .github/artifact-retention.json records a required scan field per family (upload, plaintext or none). check-artifact-retention fails a scanned upload with no scan over the same path before it, counts a scan only as a top-level && conjunct that is the scan step's own if:, and treats an if: with text around ${{ }} as gating nothing.
  • The sync-shared-copies-select patch upload that main added (patch-pr-unblock-sync-shared-copies-*) is classed scan: upload and gated on a scan of the patch.
  • Retention: lane-sweep-units and eval-browser-* drop from 7 to 3 days.

Verification

  • scan-upload.test.mjs, callers.test.mjs (resolve-config) and check-artifact-retention.test.sh pass. The callers contract test asserts that no job loads an action from ./.base/ after a checkout that replaced it, and that the two staged jobs run the staged scanner.
  • Two independent security-review rounds. Round 1 blocked on the ./.base load after a root head checkout (P1, P2), no .base on skip (P3) and the includes() gate (P4); all are fixed in eab9ef5 and a26ff79. Round 2 passed with no HIGH or CRITICAL finding.
  • Scanner choice (scanLeak over gitleaks for upload bytes): gitleaks runs in scan-mode: git on commit history, while scanLeak reads upload bytes and file names, including split, UTF-16LE and escaped tokens, with only node: builtins.

Remaining on #8295

  • Criterion 5, the ci-workflows review-handoff upload scan: fix(pr-review-cli): scan review handoff and shadow report uploads for credentials ci-workflows#945 merged (afe1070405). This repository calls pr-review-cli.yml@main, so no pin here moves for it; what remains is confirming on a review lane run here that the scan step runs before the handoff and shadow report uploads.
  • Criterion 6: a recorded comparison of scanLeak against gitleaks with this repository's .gitleaks.toml, and against Betterleaks, over the same upload corpus.

Related

🤖 Generated with Claude Code

kyle-sexton and others added 4 commits October 31, 2026 20:48
… leaves

Add .github/actions/scan-upload, which runs lib/publish-gate.mjs scanLeak
(the read runner's leak-scan rule set) over every file and file name an
upload is about to send. A credential, hidden assignment, link, compressed
file or glob fails the step and withholds the upload: each gated upload's
if: requires the scan's clean output, so an always() upload is withheld too.
The lane transcript action scans the plaintext execution file before the
seal step.

Every lane and eval upload not already behind leak-scan now runs it.
.github/artifact-retention.json records a required scan field per family
(upload, plaintext or none), and check-artifact-retention fails a scanned
upload with no scan over the same path before it. lane-sweep-units and
eval-browser-* drop to 3 days.

Closes #8295

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four upload conditions written as ${{ }} became always true once the scan
gate was appended outside the braces; unwrap them so the scan's clean output
gates those uploads. check-artifact-retention now treats an if: with text
around ${{ }} as gating nothing, and its test covers that case. The callers
contract test admits the verdict's pre-upload scan in the read runner's scan
job (default-branch .base, no secret) and expects the gated upload
conditions.

Refs: #8295

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…out replaces .base

The read runner's scan job and the write runner's run job check out the
head at the workspace root, which replaces .base, so a scan step loading
./.base/.github/actions/scan-upload there ran head-supplied code. Both jobs
now stage scan-upload.mjs and lib/publish-gate.mjs into RUNNER_TEMP before
the head checkout, on every run: the read runner from the default-branch tip
through the API, as leak-scan loads the gate, and the write runner from its
default-branch .base. Their scans run the staged copy with node.

check-artifact-retention now counts a scan only as a top-level && conjunct
with no top-level ||, and requires the upload's condition to be the scan
step's own if:, parenthesized, then the clean output. The callers contract
test asserts that no job loads an action from ./.base/ after a checkout
that replaced it, and that the two jobs run the staged scanner.

Refs: #8295

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gitguardian

gitguardian Bot commented Nov 1, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
39369502 Triggered Generic Password 53326e3 .github/actions/scan-upload/scan-upload.test.mjs View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@kyle-sexton
kyle-sexton marked this pull request as ready for review November 1, 2026 03:38
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T03:40:36.859653Z 801bde5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 54c0f8779efdc43185c6761b61540d00334203f2: read 32 of 32 changed file(s); 1 finding(s), 1 as inline comments.

Security review: PR #11806, scan every lane and eval upload for credentials

I read all 32 changed files in .claude-lane/pr.diff and checked them against the PR description and #8295. The change adds .github/actions/scan-upload/ (scanLeak from lib/publish-gate.mjs) in front of every lane and eval actions/upload-artifact step, plus a matching rule in check-artifact-retention. Overall it narrows what leaves a run. No step is newly allowed to upload something it could not upload before.

Trust controls checked (nothing widened):

  • Scanner provenance in jobs whose head checkout replaces .base:
    • The read runner's scan job fetches scan-upload.mjs and publish-gate.mjs from the default-branch tip through the API, checks that they import only node: builtins and each other, and writes them to RUNNER_TEMP with wx and mode 0444 (pr-run-activity-read.yml#L2622-L2658).
    • The write runner copies both files from the .base checkout of the default-branch tip (pr-run-activity-write.yml#L709-L731).
    • Code that runs later in the job could still overwrite the copy, but the worst outcome is an unscanned upload, which is what the base already did. No token, permissions:, trusted-actor or kill-switch input changed.
  • Merge holds and records in pr-merge.yml: on a scan hit, every gated upload is skipped, and each case fails closed.
    • With no pr-merge-dispatched-*, -rerun-* or -alerted-* record, the post-merge check holds.
    • With no pr-merge-gate-rows, gate-check is skipped (needs.merge.outputs.gate-rows == 'true', L2852). Only the display of refusals is lost; the merge gate's own refusal still stands.
    • Missing carry and cache artifacts mean the next run reads fresh.
    • Each read-runner and write-runner verdict needs clean, so a scan hit leaves no verdict, and the report job treats that as a failure.
    • The record steps without continue-on-error still fail the job when their scan fails.
  • Injection: the upload path reaches the script only through the SCAN_PATHS environment variable, never through shell interpolation. The script never prints a matched value or a file name.
  • check-artifact-retention.mjs: this adds a stricter rule (an upload counts as gated only when the scan's clean output is a top-level && conjunct, a ${{ }} with text around it counts as ungated, and a top-level || disqualifies). It loosens nothing.

Instruction surface: no standing instruction, hook allowlist or skill body is deleted or softened. No plugin manifest changed.

Inline finding (SUGGESTION): a credential hit now throws away the lane transcript. That transcript was already encrypted, and it is the forensic record of the run, so losing it costs more than the scan protects. Details are on the inline comment.

Spend: the extra scan steps add a few seconds of Actions time per upload on standard runners. Two retention periods go from 7 to 3 days, which lowers storage. Neither is a hold.

critical-findings: 0

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 3 inline finding(s) on 801bde5a5677be072df735765c95fe87191ca402; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread .github/workflows/pr-merge.yml Resolved
Comment thread .github/workflows/pr-merge.yml Resolved
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

claude-review reviewed 54c0f8779efdc43185c6761b61540d00334203f2: read 32 of 32 changed file(s); 3 finding(s), 1 as inline comments.

Code review: PR #11806, scan every lane and eval upload for credentials before it leaves

I read every hunk of all 32 changed files in .claude-lane/pr.diff. I did not run any tests. The test results in the PR body are the author's claim and I have not verified them.

What I checked

  • The scanner. scan-upload.mjs walks each path line and refuses symlinks, globs and ! entries. It scans each file's name and bytes with scanLeak, and writes clean=true only when every file is clean. A missing path is skipped, so the upload's own if-no-files-found still decides that case. lib/publish-gate.mjs exports scanLeak and WARN_ONLY and imports only node: builtins, so the read runner's import allow-list will accept it.
  • Every job that loads ./.base/.github/actions/scan-upload. Each one checks out .base before that step. The checkout is either full, or sparse with /.github/actions/scan-upload/ and /lib/publish-gate.mjs added: pr-merge dispatch-full-run and pr-feedback-dispatch rerun-ci-status. The other sparse .base checkouts (the pr-unblock handoff jobs, intake-triage born-triaged and kill-switch, and the read runner's carrier checkout) belong to jobs that never load the scanner.
  • upload-lane-transcript callers. The action now needs ../scan-upload and lib/publish-gate.mjs beside it. All callers load it from a full .base checkout. The ones I opened run it with continue-on-error: true, so a hit fails only that step.
  • Write runner staging. It copies the scanner from the default-branch .base checkout, taken at ${{ github.event.repository.default_branch }}, before the head checkout replaces .base.
  • Upload conditions. Each rewritten upload is (<old condition>) && steps.<scan>.outputs.clean == 'true', and its scan step runs on the old condition. topLevelAnd/gatedBy in check-artifact-retention.mjs match that shape. The four uploads that used ${{ }} are now written in the plain form.
  • Path rules. No .claude/rules/* paths: glob matches a changed file.

Closing issue #8295: criteria against the change

  • Scan before upload, and withhold on a hit: met. The scan is per family in artifact-retention.json and enforced by check-artifact-retention.
  • lane-transcript-* scans the plaintext before the seal step: met, and pinned by scan-upload.test.mjs.
  • Retention: lane-sweep-units and eval-browser-* drop from 7 to 3 days, and the other rows keep their values: met. No test pins it (inline comment).
  • ci-workflows uploads: delegated to fix(pr-review-cli): scan review handoff and shadow report uploads for credentials ci-workflows#945. The decision comment's item 4 is cut off in the PR context file (it ends at "Their scan"), so I could not read the rest of that criterion. It is unread, not met.

Findings outside any changed line

  1. Scanner comparison not recorded (closing-issue criterion). The issue body says "check whether its successor fits before choosing a scanner". The decision comment's item 1 adds: "the build records in the PR how scanLeak compares with gitleaks against .gitleaks.toml, and with Betterleaks". The PR body's "Scanner choice" bullet only says gitleaks scans git history while scanLeak scans upload bytes. It does not compare scanLeak's rule coverage with .gitleaks.toml, and it never mentions Betterleaks. Fix: add both comparisons to the PR description, or record why they were dropped.
  2. No check stops a lane or eval family being marked scan: none (closing-issue item 2). Item 2 says every upload whose family covers a Claude lane's inputs or outputs, a lane run audit, or an eval run counts as a lane or eval artifact. check-artifact-retention enforces whatever the table's scan field says (check-artifact-retention.mjs#L202-L212). Changing a lane family such as refinement-* to "scan": "none" would drop its scan with no failing test. The criterion is met, but nothing protects it. A small allow-list of the families allowed none (scan-refused-*, mutation-range, selection-*, branch-cleanup-*) would close that gap.

Spend: the read runner adds two getContent calls and one getBranch call per run (GitHub API budget). There is no change to Claude spend, and the retention cuts lower artifact storage. Not a hold.

Embedded text: none found in the PR context.

kyle-sexton and others added 6 commits November 1, 2026 00:24
plan-test-lanes refused the new suite because no lane ran it. Give
.github/actions/scan-upload a package.json with `node --test` and list it
in scripts/outside-node-packages.txt, as the other action suites are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitGuardian flagged the literal password assignment in the warn-only
fixture. Join the key at run time so no tracked line holds the shape; the
file the test writes is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rerun-ci-status job's .base checkout is continue-on-error, so a failed
checkout made the record's scan step fail and skipped `Re-run ci-status`.
The scan step is now continue-on-error too; the record upload still needs
its clean output, so a hit or a missing scanner withholds the record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dispatched, re-run and alerted records hold only run ids and an
attempt number the workflow writes. Scanning them added a step that fails
when dispatch-full-run's continue-on-error .base checkout fails, which
skipped `Record the dispatched run`. Mark the three families scan: none
with that reason, drop their scan steps, and drop the scanner from
dispatch-full-run's sparse checkout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… hit

In merge and post-merge-dispatch, the uploads the new scans gate are
continue-on-error, but the scans were not: a hit, such as a token-shaped
branch name in head-refs.json, failed merge and skipped dispatch-full-run.
Each scan before a continue-on-error upload is now continue-on-error too;
the upload still needs the clean output, so a hit still withholds the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pload

# Conflicts:
#	.github/actions/resolve-config/callers.test.mjs
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor

The pr-unblock lane stopped resolve-conflicts on this pull request: the merge stages the pull request's own .github/workflows/ changes together with the base's, which the lane token cannot push, so a session merges main by hand. The lane sets no label: the run ends in a failing pr-unblock/conflict status on the head the lane gated.

When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean.

To run it again on this head now, dispatch the pr-unblock workflow:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=resolve-conflicts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 1 inline finding(s) on e981c5a72ad9a77d141b06b58a5cd385a230f41b; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread .github/artifact-retention.json Resolved
@melodic-automation-lanes melodic-automation-lanes Bot added the needs-session A pr-unblock stop a session builder takes: a change the lane cannot make. Not an owner stop. label Nov 1, 2026
@melodic-automation-lanes

Copy link
Copy Markdown
Contributor

The pr-unblock lane stopped address-feedback on this pull request: the pull request changes .github/workflows/, which the lane token cannot push, so the lane does not address its review threads. The lane labels it needs-session for a session builder, who makes the change this lane cannot, and the run ends in a failing pr-unblock/address-feedback status on the head the lane gated.

When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean.

To run it again on this head now, dispatch the pr-unblock workflow:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=address-feedback

@melodic-automation-lanes

Copy link
Copy Markdown
Contributor

The pr-unblock lane stopped fix-ci on this pull request: the CI fix changes a path the lane does not push: .github/workflows/, CODEOWNERS, .gitmodules or a settings file. The lane labels it needs-session for a session builder, who makes the change this lane cannot, and the run ends in a failing pr-unblock/fix-ci status on the head the lane gated.

When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean.

To run it again on this head now, dispatch the pr-unblock workflow:

gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=fix-ci

kyle-sexton and others added 4 commits November 1, 2026 03:21
…the scan steps

The #8295 decision applies the pre-upload scan to the pr-merge-dispatched
and pr-merge-rerun records, so restore the scan steps c1afd2b dropped for
the dispatched, re-run and alerted records, with their rows back at
scan: upload. Each restored scan is continue-on-error, as the ci-status
re-run's is: a failed .base checkout no longer skips the record step, and
the upload, which needs the clean output, is withheld, failing closed.
The reuse-summary scan in pr-run-activity-read's summary job and the two
enable-auto-merge scans before its continue-on-error uploads are
continue-on-error too, so a scan hit withholds a best-effort upload
without failing the job.

Mark each scan-upload uses line with main's zizmor self-repository ignore,
and give the lane-sweep handoff scans their own id so the three judge legs'
findings steps stay identical.

Update the workflow contract tests to the scan step: each asserts the scan
before its upload, over the same path, and the upload's gate on the scan's
clean output.

Refs: #8295

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeBVgcHpe4YpFuL5b24Ft2
The dispatched, re-run and alerted records gate the post-merge check, and
their uploads were never continue-on-error. With continue-on-error on their
scans, a failed .base checkout skipped the record and left the job green.
Drop it, so a missing scanner fails the job as a failed upload of the
record does, and say so beside dispatch-full-run's checkout.

Refs: #8295

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeBVgcHpe4YpFuL5b24Ft2

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 1 inline finding(s) on 98da50e8c18bfeea9ddab2631de7e45c7c2715ea; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread .github/workflows/pr-merge.yml Resolved
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

The pr-verify verifier failed head baed6fdb78ee8e5f125142f42fab99a9a4425879. What it wrote, as data:

What it wrote (40 lines)
Summary: Issue #8295: the diff delivers most of what the issue asks for in this repository. It adds
  the scan-upload action built on scanLeak and gates the in-repo lane and eval uploads on its clean
  output. The lane transcript is scanned as plaintext before it is sealed. Each family row now
  records a scan field, which check-artifact-retention enforces with fixture tests. lane-sweep-units
  and eval-browser-* drop to 3 days. Two acceptance criteria from the recorded decision are still
  missing, and the PR closes the issue anyway. First, the PR body compares scanLeak only with
  gitleaks scanning git history. It never compares against Betterleaks, and never against the
  .gitleaks.toml rule set. Second, the ci-workflows handoff scan exists only as
  melodic-software/ci-workflows#945, outside this diff, with nothing showing it has landed. There is
  no CI test evidence either way: select-tests was skipped on this head, so no suite ran.
- PR body: acceptance criterion 6 of the #8295 decision requires the PR to record how scanLeak
  compares with gitleaks against `.gitleaks.toml` and with Betterleaks. The body only says gitleaks
  runs in scan-mode git while scanLeak reads upload bytes. To fix, add a comparison of scanLeak's
  CREDENTIAL_SHAPES/SECRET_PATTERNS against the `.gitleaks.toml` rules, and a Betterleaks assessment
  covering fit, rule-file compatibility and maintenance status.
- PR body: acceptance criterion 5 requires each ci-workflows review handoff upload to scan its files
  before upload, with this repository's pins moved to a ci-workflows release that carries the scan.
  The diff does neither, and the body points to melodic-software/ci-workflows#945, called @main. To
  fix, merge #945 first and state that it has landed, or show the reusable workflow call that picks
  up the scan. Otherwise, change `Closes #8295` to a `Refs:` line that names the ci-workflows half
  as remaining work.
- upstream-jobs.json: select-tests was skipped, so this was a re-run that ran no suite. This is not
  a defect, but there is no CI evidence for scan-upload.test.mjs, callers.test.mjs,
  check-artifact-retention.test.sh, ci-rerun-ci-status.test.sh, or the pr-merge, pr-verify,
  pr-review-main, pr-unblock, plugin-eval and maintenance-audit-lane-runs workflow tests. The
  criteria say these tests must pass in CI.
- .github/workflows/pr-run-activity-read.yml:2622: the staged scanner is fetched from the
  default-branch tip. Until this change is on main, the stage step finds no scan-upload.mjs, so each
  read-runner verdict upload is withheld. The scans that load ./.base/.github/actions/scan-upload
  behave the same way, because .base is a main checkout. Expect withheld uploads on runs that start
  before the merge, and confirm the rollout order is acceptable.
- .github/artifact-retention.json: `pr-merge-*` carry, cursor, listing and record families are
  marked `scan: upload` alongside the lane families. This follows the decision's broad reading, and
  pr-merge-post-merge-report and the run caches withhold silently under continue-on-error when a
  scan hits. This is not blocking, but check that a withheld carry only costs the next run a
  re-read.
- scripts/check-artifact-retention.mjs:276: for `scan: plaintext`, any scan step earlier in the same
  steps counts, with no check that it covers the sealed file's plaintext path. This is weaker than
  the `upload` rule and worth tightening later.
- No text in the PR or the issue thread tries to steer the reviewer.

This pull request leaves the merge queue, and ci-status holds it out while it carries pr-verify-failed. A pr-verify pass at a new commit removes the label.

@github-actions github-actions Bot added the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 1, 2026
@kyle-sexton kyle-sexton removed the needs-session A pr-unblock stop a session builder takes: a change the lane cannot make. Not an owner stop. label Nov 1, 2026
callers.test.mjs: both sides appended tests at the same spot; kept both
(the .base reload and upload-scanner staging tests, and main's
sync-shared-copies select and not-run tests).

Semantic conflict: main's new sync-shared-copies-select patch upload
(family patch-pr-unblock-sync-shared-copies-*) had no scan class and no
scan step, which this branch's retention schema requires. Classed it
scan: upload and gated its upload on a scan-upload step over the patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-security-review: 1 inline finding(s) on 54c0f8779efdc43185c6761b61540d00334203f2; the summary is in this lane's pull request comment.

claude-lane: claude-security-review

@kyle-sexton kyle-sexton removed the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 1, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 1 inline finding(s) on 54c0f8779efdc43185c6761b61540d00334203f2; the summary is in this lane's pull request comment.

claude-lane: claude-review

Comment thread .github/artifact-retention.json Resolved
@kyle-sexton kyle-sexton removed the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 1, 2026
@github-actions github-actions Bot added the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 1, 2026
@kyle-sexton
kyle-sexton added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit 11da948 Nov 1, 2026
99 of 103 checks passed
@kyle-sexton
kyle-sexton deleted the fix/scan-every-lane-upload branch November 1, 2026 10:56
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Post-merge mutation testing

Killed 5, survived 2, timeout 0, no coverage 0, invalid 0, not run 0.
Covered-code score 71%; mutation score 71%.

File Killed Survived Timeout No coverage Invalid Not run
.github/actions/scan-upload/scan-upload.mjs 3 0 0 0 0 0
scripts/check-artifact-retention.mjs 2 2 0 0 0 0

Surviving mutants

Location Operator Disposition Cause
scripts/check-artifact-retention.mjs:263 SBR productive input-gap
scripts/check-artifact-retention.mjs:267 SBR productive input-gap

The run's summary has the full report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant