Skip to content

Replace --rescan with sticky locks and runner-parity checks - #145

Open
nodeselector wants to merge 8 commits into
mainfrom
nodeselector-sticky-lock-parity-verify
Open

nodeselector wants to merge 8 commits into
mainfrom
nodeselector-sticky-lock-parity-verify

Conversation

@nodeselector

@nodeselector nodeselector commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

This drops --rescan so a lock stays put until you --relock, and every run (or --verify) checks locked pins against their repository ID in one batched query, which ends up cheaper than what we had. When a repo was renamed or transferred we now rewrite uses: to the new name and keep the locked commit, and if the old name points at a different repo we stop instead of trusting it. New pins don't get written if we can't confirm who they belong to, since that's exactly when a takeover would slip in.

A pinned commit fixes the dependencies it declares, so every run now
re-checks the recorded closure in one flat batch instead of walking it:
same repository identity, commit still present, and exact-version tags
still at the locked commit. Mutable refs stay locked until --relock.
--verify becomes the frozen mode.
Generation followed repository renames silently, so a transferred action
was pinned on the first run and only flagged on the next. Fresh pins now
join the same parity batch as locked ones; a blocking finding keeps the
workflow out of the write.
Treat repo_id as the only identity: a redirect with the same repo ID
warns with the replacement uses: line, while a missing repository or a
repo ID mismatch blocks. Any blocking parity finding now skips the
commit, so --relock can no longer write a reclaimed repository's SHA.
Walk the closure only from live roots, print parity findings in the
default run, and reuse the Phase 2 resolution for fresh closures.
…tity

Align with repo-ID lockfile identity: resolution follows renames and
transfers, compares repo IDs, and rewrites `uses:` plus the lockfile key
to the canonical name when the ID matches. A redirect inside a remote
composite blocks with the parent named. Fresh pins whose identity can't
be confirmed are not written. --verify warns on same-ID redirects.

Cache only definitive anonymous SSO probe answers; rate limits and 5xx
retry, and a rate-limited fallback surfaces SSO guidance instead of the
bare SAML 403.
@nodeselector
nodeselector marked this pull request as ready for review October 9, 2026 22:26
@nodeselector
nodeselector requested a review from a team as a code owner October 9, 2026 22:26
Copilot AI balanced review requested due to automatic review settings October 9, 2026 22:26
@nodeselector
nodeselector added this pull request to stack #146 October 9, 2026 22:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Case-sensitive refs can bypass fresh-pin parity, and several rename and fallback edge cases can produce incomplete or incorrect behavior.

4 open findings
What changed in this PR

Replaces --rescan with sticky locks, explicit --relock, and batched runner-parity validation.

Changes:

  • Adds repository identity, commit, and immutable-tag parity checks.
  • Rewrites renamed repository references while preserving locked commits.
  • Updates fallback behavior, documentation, integration fixtures, and tests.
File Description
test/​scenarios/​catalog.yml Updates CLI and parity scenarios.
test/​integration/​run.rb Adds parity stubs and rename fixtures.
test/​integration/​harness.rb Replaces rescan commands with relock.
README.md Documents sticky locks and verification.
internal/​workflowfile/​rewrite.go Validates mandatory reference rewrites.
internal/​resolve/​resolver.go Redirects cached dependencies after renames.
internal/​resolve/​discovery.go Tracks original redirected references.
internal/​pipeline/​run.go Integrates sticky resolution and parity.
internal/​pipeline/​run_test.go Removes obsolete fast-path tests.
internal/​pipeline/​parity.go Implements parity checking and findings.
internal/​pipeline/​parity_test.go Tests parity finding classification.
internal/​pipeline/​doc_urls.go Maps repository findings to guidance.
internal/​pipeline/​checks/​parsed.go Updates immutable-ref semantics.
internal/​pipeline/​checks/​finding.go Handles rename validity and warnings.
internal/​pipeline/​checks/​category.go Adds repository parity categories.
internal/​pipeline/​checks/​category_test.go Freezes new category names.
internal/​pin/​record.go Records required rename rewrites.
internal/​pin/​plan.go Plans canonical-name migrations.
internal/​pin/​commit.go Validates required rewrites before writing.
internal/​lockfile/​state.go Traverses closures and carries renamed entries.
internal/​lockfile/​state_test.go Tests transitive closure traversal.
internal/​lockfile/​direct_tracker.go Recognizes redirected direct references.
internal/​ghapi/​tagsource.go Improves SSO error reporting.
internal/​ghapi/​rest_fallback.go Adds rate-limit handling and canonicalization.
internal/​ghapi/​rest_fallback_test.go Tests fallback retries and guidance.
internal/​ghapi/​repos.go Retrieves canonical repository metadata.
internal/​ghapi/​graphql_parity.go Implements batched GraphQL/REST parity queries.
internal/​ghapi/​graphql_parity_test.go Tests batching, parsing, and REST parity.
internal/​ghapi/​graphql_action_files.go Canonicalizes redirected action results.
internal/​dep/​dependency.go Tracks and merges original references.
cmd/​gh-actions-lock/​verify.go Makes verify a frozen read-only check.
cmd/​gh-actions-lock/​verify_test.go Updates verify flag tests.
cmd/​gh-actions-lock/​selfrepository_test.go Accommodates parity requests.
cmd/​gh-actions-lock/​run.go Removes rescan and wires relock/parity.
cmd/​gh-actions-lock/​prune_workflow_test.go Stubs parity during pruning.
cmd/​gh-actions-lock/​proxima_test.go Uses verify and supports SHA checks.
cmd/​gh-actions-lock/​pin_summary.go Reports repository rewrites and errors.
cmd/​gh-actions-lock/​parity_test.go Adds end-to-end parity coverage.
cmd/​gh-actions-lock/​migrate_test.go Migrates rescan usage to relock.
cmd/​gh-actions-lock/​format/​terminal.go Renders repository parity findings.
cmd/​gh-actions-lock/​command_test.go Updates command fixtures for parity.
cmd/​gh-actions-lock/​check_json_golden_test.go Updates JSON tests to use relock.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/pin/plan.go
Comment on lines +522 to +523
if len(dep.OriginalRefs) > 0 {
entry.RenamedFrom = dep.OriginalRefs[0].NWO() + "@" + dep.OriginalRefs[0].Ref
Comment on lines +69 to +81
switch {
case resp.StatusCode == http.StatusOK:
anonRateLimited.Delete(key)
anonProbeCache.Store(key, true)
return true
case isRateLimited(resp):
anonRateLimited.Store(key, struct{}{})
return false
case resp.StatusCode >= 500:
return false
}
anonProbeCache.Store(key, false)
return false
Comment thread internal/pin/commit.go
Comment on lines +30 to +31
if err := validateRequiredRewrites(rec.Workflows); err != nil {
return err
Comment on lines +176 to +178
byKey[strings.ToLower(k)] = append(byKey[strings.ToLower(k)], d)
for _, p := range parents[k] {
children[strings.ToLower(p)] = append(children[strings.ToLower(p)], d)
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.

2 participants