Skip to content

fix(ci): defer actions.lock verification to governance in the workflow linter - #113

Merged
hyperpolymath merged 3 commits into
mainfrom
claude/new-session-th4e9w-linter
Oct 14, 2026
Merged

hyperpolymath merged 3 commits into
mainfrom
claude/new-session-th4e9w-linter

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

"Workflow Security Linter / lint-workflows" has failed on every main push since 2026-10-05, and now on #111/#112. It failed for two reasons, both caused by the copied "Verify immutable action lockfile" step:

  • Version skew. The step pins its own gh-actions-lock v0.1.6, which cannot read lockfile v0.0.3:

    lockfile version v0.0.3 is newer than this binary supports (v0.0.2)
    

    Every current gh actions-lock writes v0.0.3, including the regeneration in fix(ci): regenerate actions.lock after the Dependabot actions bump (#110) #112.

  • Duplicate verifier. It ran a second lock check beside governance's, with its own version pin. That pin drifts from the estate's, and the raw tool doesn't apply the reusable-workflow coverage rule that governance's verifier applies.

Governance's Actions lockfile verify (standards scripts/update-actions-lock.sh) is the single tested implementation. This PR removes the duplicate step and leaves a comment pointing at the gate.

Owner decision 2026-10-14 (selector): drop the lock step from copied linters, and move the estate to gh-actions-lock v0.2.0 (hyperpolymath/standards#1250). Pilot repos first: proof-burrower, echidna, echidnabot. The rest of the estate follows in the wave.

Changes

  • .github/workflows/workflow-linter.yml: the pinned-binary lock step is replaced by a comment. The SPDX, permissions and duplicate-workflow checks are unchanged.
  • No uses: changes, so actions.lock is untouched.

RSR Quality Checklist

Required

  • Tests pass: the remaining linter steps are unchanged.
  • Code is formatted (n/a, YAML only).
  • Linter is clean: actionlint v1.7.7 clean; the workflow duplicate-key check is clean.
  • No banned language patterns.
  • No unsafe blocks without // SAFETY: comments (n/a).
  • No banned functions (n/a).
  • SPDX license headers present on all new/modified source files.
  • No secrets, credentials, or .env files included.

As Applicable

  • .machine_readable/descriptiles/STATE.a2ml updated (n/a; A2ML is retired)

Testing

🤖 Generated with Claude Code

https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3


Generated by Claude Code

)

#110 bumped four `uses:` refs and left actions.lock pinning the old ones:
- github/codeql-action v4.38.2 -> v4.38.3
- hyperpolymath/smtp-notify-action v0.3.0 -> v0.5.0
- actions/upload-artifact v7.0.1 -> v7.0.2
- actions/download-artifact v8.0.1 -> v8.0.2

Since then "Governance / Actions lockfile verify" has been red on main.
GitHub also refuses to start codeql.yml and static-analysis-gate.yml
(startup_failure on every run), so the required "analyze (actions, none)"
check never reports, and CodeQL has not scanned main since 11 Oct.

Only actions.lock changes. It is the output of the estate repair order:
gh actions-lock v0.2.0, relock-sha-keys, complete-job-refs, close-lock and
prune-stale, run by `regen_lock` from standards
scripts/regen-dependabot-locks.sh at ae36881b. GitHub Actions ran it on
main e81d057, because Claude Code sessions cannot reach GitHub GraphQL.
The lockfile schema moves v0.0.2 -> v0.0.3. Dependabot's lock support
reads both versions.

Verified:
- in CI, check-actions-lock-gate.sh at this repo's governance pin
  (standards 8f2ee508): pass;
- here, the decoded bytes match the CI job's SHA-256 (6a06b778...);
- the same gate passes locally;
- `gh actions-lock --verify-local` reports only the 7 known
  reusable-workflow false positives, which the gate accepts as coverage.

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

The copied "Verify immutable action lockfile" step pinned its own
gh-actions-lock v0.1.6 binary. v0.1.6 cannot read lockfile v0.0.3, which
every current `gh actions-lock` writes, so the step failed whenever a
current tool refreshed the lock (proof-burrower#111/#112). It also kept a
second version pin that drifts from the estate's.

Governance already verifies the lock through standards
scripts/update-actions-lock.sh. That is one tested implementation, with
one pin, and it accepts reusable-workflow coverage. The step is removed
and a comment points at the gate. No `uses:` changes, so actions.lock is
untouched.

Owner decision 2026-10-14: drop the lock step from copied linters; the
estate moves to gh-actions-lock v0.2.0 in hyperpolymath/standards#1250.

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

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 59a15ac9-beec-4ee3-832f-bf927d277b84
📥 Commits

Reviewing files that changed from the base of the PR and between e81d057 and 79d3836.

⛔ Files ignored due to path filters: 1
  • .github/workflows/actions.lock is excluded by !**/*.lock
📒 Files selected for processing: 1
  • .github/workflows/workflow-linter.yml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout: 2
  • GitHub Check: rust-ci / Cargo audit (security)
  • GitHub Check: Burrower proof safety
⚠️ CI failures not shown inline: 5

GitHub Actions: Central Estate CI/CD Audit / 0_estate-audit.txt: fix(ci): defer actions.lock verification to governance in the workflow linter

Conclusion: failure

View job details

##[group]Run # Accepted placements widened per owner ruling 2026-09-19: estate
 �[36;1m# Accepted placements widened per owner ruling 2026-09-19: estate�[0m
 �[36;1m# repos legitimately keep governance docs under docs/ (and GitHub�[0m
 �[36;1m# renders under .github/) — rsr-template-repo itself was failing on�[0m
 �[36;1m# layout, not on substance. Root forms still win (first hit).�[0m
 �[36;1m# Template-repo mode: see below.�[0m
 �[36;1m#�[0m
 �[36;1m# Presence-only checking rewards filler. This gate previously demanded�[0m
 �[36;1m# ARCHITECTURE.md / MAINTAINERS.adoc / GOVERNANCE.md and checked only�[0m
 �[36;1m# that the paths existed — so the cheapest way to pass was to commit�[0m
 �[36;1m# template boilerplate. That happened: an estate repo acquired an�[0m
 �[36;1m# ARCHITECTURE.md describing a directory layout it does not have, a�[0m
 �[36;1m# MAINTAINERS naming a different account as owner, and a mise.toml�[0m
 �[36;1m# pinning `zig = "latest"` against that repo's own .tool-versions.�[0m
 �[36;1m# All three would have passed. So: presence, THEN format, THEN substance.�[0m
 �[36;1m#�[0m
 �[36;1m# Format policy (estate):�[0m
 �[36;1m#   .adoc  documentation (default)�[0m
 �[36;1m#   .md    wiki content only — plus a transitional allowance for the�[0m
 �[36;1m#          GitHub-mandated files, which are migrating to berrywiki format�[0m
 �[36;1m#   .txt   licence texts�[0m
 �[36;1m#   fixed  names GitHub or convention dictates (CODEOWNERS, funding.yml,�[0m
 �[36;1m#          NOTICE, AUTHORS, MAINTAINERS) keep their form�[0m
 �[36;1mset -uo pipefail�[0m
 �[36;1mfail=0�[0m
 �[36;1m�[0m
 �[36;1m# --- template-repo mode -------------------------------------------�[0m
 �[36;1m# An un-instantiated template legitimately SHIPS {{OWNER}}/{{AUTHOR}}�[0m
 �[36;1m# placeholders and self-references its own template name; judging it�[0m
 �[36;1m# like an instantiated repo is a category error. Detection is explicit�[0m
 �[36;1m# and auditable: the repo name says it is a temp...

GitHub Actions: Central Estate CI/CD Audit / estate-audit: fix(ci): defer actions.lock verification to governance in the workflow linter

Conclusion: failure

View job details

##[group]Run # Accepted placements widened per owner ruling 2026-09-19: estate
 �[36;1m# Accepted placements widened per owner ruling 2026-09-19: estate�[0m
 �[36;1m# repos legitimately keep governance docs under docs/ (and GitHub�[0m
 �[36;1m# renders under .github/) — rsr-template-repo itself was failing on�[0m
 �[36;1m# layout, not on substance. Root forms still win (first hit).�[0m
 �[36;1m# Template-repo mode: see below.�[0m
 �[36;1m#�[0m
 �[36;1m# Presence-only checking rewards filler. This gate previously demanded�[0m
 �[36;1m# ARCHITECTURE.md / MAINTAINERS.adoc / GOVERNANCE.md and checked only�[0m
 �[36;1m# that the paths existed — so the cheapest way to pass was to commit�[0m
 �[36;1m# template boilerplate. That happened: an estate repo acquired an�[0m
 �[36;1m# ARCHITECTURE.md describing a directory layout it does not have, a�[0m
 �[36;1m# MAINTAINERS naming a different account as owner, and a mise.toml�[0m
 �[36;1m# pinning `zig = "latest"` against that repo's own .tool-versions.�[0m
 �[36;1m# All three would have passed. So: presence, THEN format, THEN substance.�[0m
 �[36;1m#�[0m
 �[36;1m# Format policy (estate):�[0m
 �[36;1m#   .adoc  documentation (default)�[0m
 �[36;1m#   .md    wiki content only — plus a transitional allowance for the�[0m
 �[36;1m#          GitHub-mandated files, which are migrating to berrywiki format�[0m
 �[36;1m#   .txt   licence texts�[0m
 �[36;1m#   fixed  names GitHub or convention dictates (CODEOWNERS, funding.yml,�[0m
 �[36;1m#          NOTICE, AUTHORS, MAINTAINERS) keep their form�[0m
 �[36;1mset -uo pipefail�[0m
 �[36;1mfail=0�[0m
 �[36;1m�[0m
 �[36;1m# --- template-repo mode -------------------------------------------�[0m
 �[36;1m# An un-instantiated template legitimately SHIPS {{OWNER}}/{{AUTHOR}}�[0m
 �[36;1m# placeholders and self-references its own template name; judging it�[0m
 �[36;1m# like an instantiated repo is a category error. Detection is explicit�[0m
 �[36;1m# and auditable: the repo name says it is a temp...

GitHub Actions: Central Estate CI/CD Audit / estate-audit: fix(ci): defer actions.lock verification to governance in the workflow linter

Conclusion: failure

View job details

##[group]Run echo "Validating Idris2 ABI Purity..."
 �[36;1mecho "Validating Idris2 ABI Purity..."�[0m
 �[36;1mif [ -f "abi.ipkg" ]; then�[0m
 �[36;1m  # Check for forbidden side-effects in Abi.* modules�[0m
 �[36;1m  if git grep -E 'IO |unsafePerformIO|primIO' -- 'src/Abi*' 'src/Hypatia/ABI*'; then�[0m
 �[36;1m    echo "::error::Idris2 ABI modules contain prohibited side-effects (IO/unsafe)."�[0m

GitHub Actions: Central Estate CI/CD Audit / estate-audit: fix(ci): defer actions.lock verification to governance in the workflow linter

Conclusion: failure

View job details

##[group]Run echo "Validating the Zig unified-api-adapter..."
 �[36;1mecho "Validating the Zig unified-api-adapter..."�[0m
 �[36;1mif find . -name "*.zig" | grep -q .; then�[0m
 �[36;1m  # Ensure it doesn't use standard library io or std.fs directly for FFI�[0m
 �[36;1m  if git grep -E 'std\.fs|std\.io' -- '*.zig'; then�[0m
 �[36;1m    echo "::error::Zig FFI modules contain prohibited side-effects (std.fs/std.io). Must use the unified-api-adapter."�[0m

GitHub Actions: Central Estate CI/CD Audit / estate-audit: fix(ci): defer actions.lock verification to governance in the workflow linter

Conclusion: failure

View job details

##[group]Run # This gate used to be a stub: it printed "Checking Justfile syntax..."
 �[36;1m# This gate used to be a stub: it printed "Checking Justfile syntax..."�[0m
 �[36;1m# with the actual command commented out, then delegated "extensiveness"�[0m
 �[36;1m# to a semantic-audit action that is itself unimplemented, then printed�[0m
 �[36;1m# "Recipes Set Validation Passed." It could not fail, and checked nothing.�[0m
 �[36;1m#�[0m
 �[36;1m# It now does the one thing it can do honestly and cheaply: prove the�[0m
 �[36;1m# recipe files parse. `just --summary` parses the whole file and lists�[0m
 �[36;1m# targets WITHOUT executing anything, so it is safe in CI.�[0m
 �[36;1m#�[0m
 �[36;1m# Deliberately NOT checked: whether the recipe set is "extensive" or�[0m
 �[36;1m# "justifiable". Those are judgements, not properties, and a gate that�[0m
 �[36;1m# pretends to measure them is how this action became a stub in the�[0m
 �[36;1m# first place.�[0m
 �[36;1m#�[0m
 �[36;1m# `just --fmt --check` is also deliberately avoided: it enforces�[0m
 �[36;1m# formatting, not validity, and would fail repositories whose Justfiles�[0m
 �[36;1m# are correct but hand-formatted.�[0m
 �[36;1mset -uo pipefail�[0m
 �[36;1mfail=0�[0m
 �[36;1mchecked=0�[0m
 �[36;1m�[0m
 �[36;1m# --- Justfile -----------------------------------------------------�[0m
 �[36;1mjf=""�[0m
 �[36;1mfor c in Justfile justfile .justfile; do [ -f "$c" ] && { jf="$c"; break; }; done�[0m
 �[36;1mif [ -n "$jf" ]; then�[0m
 �[36;1m  if ! command -v just >/dev/null 2>&1; then�[0m
 �[36;1m    echo "Installing just (not on PATH)..."�[0m
 �[36;1m    mkdir -p "$RUNNER_TEMP/just-bin"�[0m
 �[36;1m    if curl --proto '=https' --tlsv1.2 -sSf \�[0m
 �[36;1m         https://just.systems/install.sh \�[0m
 �[36;1m       | bash -s -- --to "$RUNNER_TEMP/just-bin" >/dev/null 2>&1; then�[0m
 �[36;1m      export PATH="$RUNNER_TEMP/just-bin:$PATH"�[0m
 �[36;1m    fi�[0m
 �[36;1m  fi�[0m
 �[36;1m�[0m
 �[36;1m  if command -v just >/dev/null 2...
🔇 Additional comments: 1
.github/workflows/workflow-linter.yml: 1

69-73: 🔒 Security & Privacy | 🛡️ Detected with Advanced Tier

The replacement governance check is not established by the available evidence. The repository files inspected do not show whether governance runs for pull requests, validates reusable workflows, or is a required merge check.

📝 Summary

Summary by CodeRabbit

  • Chores
    • Removed a workflow check that could not read the current lockfile format. The lockfile is still checked by the existing governance check.

The workflow removes the step that downloaded, checksum-verified and ran gh-actions-lock v0.1.6. Comments now state that governance checks actions.lock and that the removed binary cannot read lockfile v0.0.3.

Changes: Workflow lockfile check

Layer / File(s) Summary
Remove local lockfile verification
.github/workflows/workflow-linter.yml
The workflow removes the gh-actions-lock download, checksum verification and execution. Comments identify governance as the verifier and note the binary’s lockfile version limitation.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~5 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 79d38

No concrete merge-blocking issue was established. Governance still runs for pull requests to the supported target branches, though its lockfile-verification behavior remains unconfirmed.

Security architecture risk: 🔵 Low · up to 79d38

Centralizing lockfile verification can reduce version drift, but the replacement check's implementation and required status have not been confirmed. Its pull-request trigger also covers fewer target branches. No exploitable bypass or increased execution privileges were established.

Retained concerns

  • Low · security · inferred: Removing local verification makes lockfile assurance depend on an external gate whose implementation and mandatory status remain unverified. Its wrapper covers main/master pull requests, whereas the removed check applied to workflow-changing pull requests without a target-branch restriction. This is an unresolved control-equivalence concern, not a verified merge bypass.
Security review details

Security Blast Radius

  • inferred — The direct exposure concerns this repository's CI action-dependency verification. Any downstream security impact would depend on unverified dependency changes being accepted and on the authority of consuming workflows; that downstream reachability has not been established.

Security Findings and Attack Paths

  • inferred — A contributor's workflow changes could lose local lockfile scrutiny if governance does not provide equivalent verification. Mandatory merge enforcement is also unresolved. The candidate remains deferred rather than a verified attack path, and the already-failing-check explanation has not been established for the full comparison base.

Trust Boundaries and Controls

  • observed — The checked-in main branch configuration requires hypatia-scan, codeql, and openssf-compliance, not governance or the linter. This declaration is unchanged across the comparison and is not evidence of deployed branch protection.

Hardening Proposals

  • proposed — Establish the pinned governance implementation's lockfile and reusable-workflow coverage, identify the intended target branches, and confirm deployed required-check enforcement before treating governance as an equivalent replacement.
Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main change: deferring actions.lock verification to governance in the workflow linter.
Description check Passed The description includes the required Summary, Changes, quality checklist, and Testing sections. It explains the failure, the proposed fix, and validation results. The optional Screenshots section is …
✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • 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

A rabbit checks the workflow trail,
The old lock tool is set to fail.
Governance checks the lockfile there,
With comments noting versions fair.
The rabbit hops, the steps now clear.

Comment @coderabbitai help to get the list of available commands.

…on-th4e9w-lock

Ports #113 (drop the copied pinned lock step) so lint-workflows stops
failing on this PR's lockfile v0.0.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3
hyperpolymath pushed a commit that referenced this pull request Oct 14, 2026
…on-th4e9w

Ports #113 (drop the copied pinned lock step) so lint-workflows stops
failing on this PR's lockfile v0.0.3.

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

Copy link
Copy Markdown
Owner Author

The first run here failed governance / Actions lockfile verify, and CodeQL analyze (actions, none) never reported. Neither failure comes from this PR. Both come from main's actions.lock, which #110 left stale: 6 ref-changed entries, so GitHub rejects CodeQL at startup.

The fix is #112, and I've ported it here. This branch now fast-forwards to #112's head (79d3836). #112 already carried this PR's linter change, ported earlier, so the two PRs now have the same content. Merging either one makes the other a no-op.


Generated by Claude Code

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 14, 2026 10:11
@hyperpolymath
hyperpolymath merged commit 435df7c into main Oct 14, 2026
102 of 103 checks passed
@hyperpolymath
hyperpolymath deleted the claude/new-session-th4e9w-linter branch October 14, 2026 10:15
hyperpolymath added a commit that referenced this pull request Oct 14, 2026
…YAML / I-JSON+JCS / UUID v8 (#111)

## Summary

Adds a root `CLAUDE.md` that records the owner's norms of 2026-10-14 for
agent sessions in this repository. It goes at the root because
`.claude/` is gitignored here. echidna and echidnabot carry the same
text.

## Changes

- `CLAUDE.md` (new):
  - decisions are asked with the selector tool;
  - work lands as stacked PRs;
- findings are fixed at source. The exemption ratchet admits ledger
growth only with a `Ratchet-exception:` line naming that ledger.
- The format standards point at their normative source in
`hyperpolymath/standards` rather than restating it:
- `3-practice/YAML-POLICY.adoc` §3.4: KYAML, `.kyaml` (D280, D307,
D293);
  - the workflow gate in standards#1025;
  - `3-practice/JSON-POLICY.adoc`: I-JSON as JCS plus a final LF (D306);
- ADR-008: UUID v8. It is scoped as the owner accepted from review: v7
records and v8 JCS content ids until the
`crates/burrower-core/src/ids.rs` migration lands.

The `actions.lock` regeneration this PR used to carry (ported from #112)
is now on `main` via #113, together with the workflow-linter change.
Since the merge of `main` (ae9a0a9), this PR changes `CLAUDE.md` only.

## RSR Quality Checklist

### Required

- [x] Tests pass (docs only)
- [x] Code is formatted (n/a)
- [x] Linter is clean (n/a)
- [x] No banned language patterns
- [x] No `unsafe` blocks without `// SAFETY:` comments
- [x] No banned functions
- [x] SPDX license headers present on all new/modified source files
- [x] No secrets, credentials, or `.env` files included

### As Applicable

- [x] Documentation updated for user-facing changes
- [ ] Others: n/a

The repo's migration to these formats follows in its own stacked PRs.
That includes the `ids.rs` move to profiles T and C, with the shared
`proof-goal` domain from standards#1246, and the ledger `goal_id`
migration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_019a95kk88TZ1iegsBK3kox3

---------

Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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