Skip to content

feat(workflow_audit): report actions.lock records pointing at a moved repository - #1109

Merged
hyperpolymath merged 5 commits into
mainfrom
claude/lock-moved-repo
Nov 14, 2026
Merged

hyperpolymath merged 5 commits into
mainfrom
claude/lock-moved-repo

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

CASE-054 (CoprocessorRuntime.jl): the required scan / gitleaks never reported, because secret-scanner and five other workflows died at startup with no job and no log. The repo's actions.lock still recorded hyperpolymath/standards@da2c748 with owner_id 6759885. That repository moved to metadatastician (owner_id 297579217, same repo_id 1116521501), so the runner could not verify the record.

Owner ruling, 14 Nov: build a static rule from the lock alone, info severity, referred to the squabbler, never autofixed.

What

  • ActionsLock.moved_repository_keys/1 reads the lock against a table of known transfers, currently hyperpolymath/standards → metadatastician/standards. It reports:
    • :old_name: the key names the old location;
    • :stale_owner_id: the key names the new location with the old owner's owner_id.
  • One repo_id under two names is already refused by parse/2 (:repository_id_reused, reported as an invalid lock), so it isn't repeated here.
  • WorkflowAudit.check_actions_lock_moved_repositories/2, wired into audit/3 (skipped in partial audits), gives one advisory info finding per audit, hypatia/workflow_audit/actions_lock_moved_repository, action: :report. The referral is a relock against the new owner (cicd-squabbler). Nothing is rewritten.

Proof

  • CoprocessorRuntime.jl main e57738b: reports exactly hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540 (:old_name → metadatastician/standards).
  • Hypatia's own actions.lock: clean. Its standards record is already metadatastician/standards with owner_id 297579217.

Tests

  • 5 new tests in test/rules/actions_lock_test.exs:

    • an old name is reported;
    • a stale owner_id under the new name is reported;
    • the new name with the new owner_id is clean;
    • Hypatia's own lock is clean;
    • the audit reports it once, and not in a partial audit.

    All fail on main.

  • mix test test/rules: 933 tests, 0 failures. test/soundness_test.exs: 16/16.

  • gen-rules-praxis.sh --check, check-readme-claims.sh and rules-praxis-test.sh (22/22) are clean.

Extending

When a repository is transferred, add it to @moved_repositories in lib/rules/actions_lock.ex with the new owner's ids.


Generated by Claude Code

… repository

CASE-054 (CoprocessorRuntime.jl): the required "scan / gitleaks" never
reported because secret-scanner and five other workflows died at startup,
with no job and no log. The lock still recorded
metadatastician/standards@da2c748 with owner_id 6759885; the repository
moved to metadatastician (owner_id 297579217, same repo_id 1116521501),
so the runner could not verify the record.

ActionsLock.moved_repository_keys/1 reads the lock alone against a table
of known transfers: a key naming the old location (:old_name), or the new
location with the old owner's id (:stale_owner_id). The audit reports one
advisory info finding, referred to the relock (cicd-squabbler); nothing is
rewritten. One repo_id under two names is already refused by parse/2.

Verified on CoprocessorRuntime.jl main e57738b: reports exactly that
record. Hypatia's own lock is clean. Owner ruling 14 Nov.

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

coderabbitai Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 22 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 4c9939b3-74a2-4028-a237-26b131314ea4
Commits

Reviewing files that changed from the base of the PR and between ec90609 and feba296.

Files selected for processing: 5

  • hypatia_rules_praxis.deed
  • lib/rules/actions_lock.ex
  • lib/rules/moved_repositories.ex
  • lib/rules/workflow_audit.ex
  • test/rules/actions_lock_test.exs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@hyperpolymath
hyperpolymath marked this pull request as ready for review November 14, 2026 18:43
@chatgpt-codex-connector

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

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

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

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-14T19:50:00.018897Z feba296 New commits
🔒 Security Review ✅ Completed 2026-11-14T19:48:03.950416Z feba296 New commits
ℹ️ 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66fd5cb7e4

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/rules/actions_lock.ex Resolved
Comment thread lib/rules/actions_lock.ex Resolved

Copy link
Copy Markdown
Owner Author

The red governance / Validate Hypatia Baseline is not from this PR. It flags String.to_atom at lib/hypatia/coverage/evidence_formatter.ex:197, which came to main with #1107 and is not in this diff. Fix at source: #1122. Once that merges, I'll update this branch from main so the gate re-runs.


Generated by Claude Code

…repo_id

Codex on #1109:
- The moves now come from Hypatia.Rules.MovedRepositories, which WF028
  also reads, so a new transfer is recorded once. The entry gains
  current_owner_id (additive; the schema version is unchanged).
- :old_name needs the moved repository's repo_id, so a new repository
  created at the old name is not reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0136P7ZkGCWGEyedTtdEHAPT
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0136P7ZkGCWGEyedTtdEHAPT
hyperpolymath added a commit that referenced this pull request Nov 14, 2026
## What

`Hypatia.Coverage.EvidenceFormatter` turned every string key of an `@tag
evidence:` map into an atom with `String.to_atom/1` (line 197). With
this change it uses `String.to_existing_atom/1` and keeps the string
when no atom exists.

## Why

Since #1107 merged, `main` has a high finding from Hypatia's own
`code_safety/elixir_atom_from_user` rule. #1107's own baseline run was
cancelled, so the gate never saw it. Now `governance / Validate Hypatia
Baseline` is red on every open PR, for example #1109:
[job](https://github.com/hyperpolymath/hypatia/actions/runs/43625366706/job/132550885569)
"1 unfiltered finding(s) at or above 'info'", which is this line.

Behaviour is unchanged for the keys the formatter reads. They are atom
literals in the module, so they already exist.

## Evidence

- New test: "an unknown string key in the tag does not create an atom".
It fails on `main` (`23 tests, 1 failure`) and passes with the fix (`23
tests, 0 failures`).
- The rule's pattern is `~r/String\.to_atom\(/`, which does not match
`to_existing_atom`.
- `mix format --check-formatted` passes on both files.
`gen-rules-praxis.sh --check` and `check-readme-claims.sh` also pass.

## Not done

- 28 other `test/coverage` tests fail in my local copy of the repo. They
fail the same way on unchanged `main`, because the copy lacks some
modules. CI runs the full suite.

---
_Generated by [Claude
Code](https://claude.ai/code/session_0136P7ZkGCWGEyedTtdEHAPT)_

Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
@hyperpolymath
hyperpolymath enabled auto-merge (squash) November 14, 2026 19:45
@hyperpolymath
hyperpolymath merged commit f955ace into main Nov 14, 2026
42 of 43 checks passed
@hyperpolymath
hyperpolymath deleted the claude/lock-moved-repo branch November 14, 2026 19:45
@sonarqubecloud

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: feba296276

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/rules/actions_lock.ex
Comment on lines +721 to +724
Map.new(Hypatia.Rules.MovedRepositories.entries(), fn m ->
{String.downcase(m.former),
%{to: String.downcase(m.current), owner_id: m.current_owner_id, repo_id: m.repo_id}}
end)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve chained transfers to the final repository

When a repository moves twice and the shared registry contains A -> B and B -> C under its one-row-per-move extension model, this map preserves A's immediate destination as B. Consequently, an actions.lock record still naming A is reported as A -> B, and the audit instructs users to relock against an address that is itself obsolete and still fails. Collapse same-repo_id move chains to the final current repository before producing the finding.

Useful? React with 👍 / 👎.

Comment on lines +611 to +612
"#{length(moved)} actions.lock record(s) point at a moved repository's old " <>
"home (e.g. #{describe_moved(hd(moved))}). The runner cannot verify them, so " <>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Distinguish stale owner metadata in the advisory

When the detected reason is :stale_owner_id, the key already names the current repository, so this message incorrectly says it points at the old home and renders a self-mapping such as metadatastician/standards@<sha> -> metadatastician/standards. That obscures the actual owner-ID mismatch the user must diagnose; format this case separately as stale owner metadata, ideally including the observed and expected IDs.

Useful? React with 👍 / 👎.

Comment thread lib/rules/actions_lock.ex
Comment on lines +721 to +724
Map.new(Hypatia.Rules.MovedRepositories.entries(), fn m ->
{String.downcase(m.former),
%{to: String.downcase(m.current), owner_id: m.current_owner_id, repo_id: m.repo_id}}
end)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve transfers that share a former slug

If repository R1 moves from A to B, a replacement R2 is created at A, and R2 later moves to C, the registry correctly needs two rows with the same former slug but different stable repo_id values. This Map.new/2 silently retains only one of them, so locks for the other moved repository fail the new repo_id guard and are never reported. Group entries by former slug and select the row matching the dependency's repo_id instead of discarding one transfer.

Useful? React with 👍 / 👎.

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.

3 participants