Skip to content

ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset - #84

Open
yakimoto wants to merge 3 commits into
mainfrom
ci/vendor-governance-enforce
Open

ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset#84
yakimoto wants to merge 3 commits into
mainfrom
ci/vendor-governance-enforce

Conversation

@yakimoto

@yakimoto yakimoto commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Adds the governance-enforce A_BLOCK gate (secrets / hardcoded-paths, diff-scoped) to this repo. Part of claude-workstation#1624 E4 T4.9a, following the wave-av/cli#20 pilot.

Why this repo had no secrets scan

The org ruleset governance-a-block-enforce (17901847) requires an enforce check across the fleet. Its scope is an explicit include list of 112 hand-maintained repository names — and every one of them matches wave-*.

The 16 public repos absent from that list are exactly the 16 not named wave-*: .github, adk, api-spec, cli, companion-module-wave, create-wave-app, crest-console, dispatch-edge, examples, mcp-server, obs-wave-plugin, sdk, sdk-python, sdks, vmix-wave-integration, workflow-sdk.

Read the intersection: the repos that publish our npm packages are precisely the repos running with no A_BLOCK secrets scan. Nobody excluded them. A naming convention silently became a security boundary, and it drew the line in the worst possible place.

Why the workflow lands before the ruleset entry

Adding a repo to a required_status_checks ruleset before it emits that check is a permanent deadlock — a required check that never reports can never go green, and every PR on the repo becomes unmergeable. So the order is: vendor the workflow, observe it green, then extend the list. Doing it the intuitive way round would have bricked all sixteen.

This PR is also its own liveness drill. The workflow triggers on pull_request, so it runs on the PR that adds it. If enforce reports green here, the vendored shape works in this repo. If it does not, nothing was required and nothing is blocked — which is the point of this ordering.

Proven before fan-out, not assumed

@wave-av/governance is an internal-visibility package owned by claude-workstation, so whether a public repo's GITHUB_TOKEN can read it was the one real assumption. Rather than fan out on the inference, it was piloted on a single repo first:

wave-av/cli#20 — workflow run 31011943790, conclusion success.

That is the receipt this PR rides on. The shape is copied verbatim from wave-av/wave-moq-edge (public, 12/12 green), which matters because auto-approve.yml fails silently on every public repo — it calls a reusable workflow in the private wave-foundation, and a public repo cannot do that (parse-time failure, zero jobs, no annotation). This workflow calls nothing cross-repo, so that trap does not apply.

Security properties, unchanged from the source:

  • actions SHA-pinned (actions/checkout@df4cb1c, actions/setup-node@48b55a0)
  • persist-credentials: false on checkout
  • least privilege: contents: read + packages: read
  • isolated install into RUNNER_TEMP, --no-save, so nothing touches this repo's dependency tree
  • the .npmrc is written with a literal ${NODE_AUTH_TOKEN} (single-quoted printf) which npm expands at run time — no secret value is ever written to disk or a log
  • both ${{ }} inputs (base.sha, event.before) are routed through env: and read as "$VAR", never interpolated into the script body

Diff-scoped by design: it blocks new violations without failing on legacy debt.

Refs wave-av/claude-workstation#1624.


Note

Low Risk
CI-only change with no application runtime impact; hardening reduces false-pass risk on the security gate itself.

Overview
Adds .github/workflows/governance-enforce.yml, bringing this repo under the same diff-scoped A_BLOCK scan (secrets, hardcoded paths) that org ruleset governance-a-block-enforce expects via an enforce job—repos not named wave-* were previously off that hand-maintained list.

The workflow runs on pull requests and pushes to main/master: full-history checkout, isolated install of @wave-av/governance@0.4.6 into RUNNER_TEMP with registry token limited to the install step, --ignore-scripts, and enforce.mjs --changed <base>. Concurrency only cancels in-progress runs on PRs so push runs are not skipped. Base resolution validates the SHA, then fails closed to the empty tree (full scan) instead of a no-op HEAD diff.

Do not add this repo to the ruleset until enforce is green here; the PR description treats this merge as the liveness drill before making the check required.

Reviewed by Cursor Bugbot for commit cc0e626. Configure here.

Note

Add governance enforcement workflow to scan diffs on PRs and pushes to main

Adds governance-enforce.yml, which runs @wave-av/governance@0.4.6 on pull requests, merge groups, and pushes to main/master. The enforcer scans only changed files using a computed diff base; if no valid base is found, it falls back to the empty tree and scans the full repository. Push and merge group runs are not cancelable to ensure no committed ranges are skipped.

Macroscope summarized 0b8cf79.

@yakimoto yakimoto added the rr:skip-cubic RF.P1 reviewer routing (#1039) label Aug 5, 2026
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_aa33bf01-e471-4bdd-ab5e-0768a697b982)

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 44 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2625e259-462c-4811-ad65-68307e922cfe

📥 Commits

Reviewing files that changed from the base of the PR and between 8411936 and 0b8cf79.

📒 Files selected for processing (1)
  • .github/workflows/governance-enforce.yml

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

@macroscopeapp

macroscopeapp Bot commented Aug 5, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

Unable to check for correctness in 0b8cf79. Multiple unresolved review comments identify potential bugs where this security gate could pass without actually scanning code (fork auth failures, false-green on force-pushes/branch creation). These concerns should be addressed before a required security check is merged.

You can customize Macroscope's approvability policy. Learn more.

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 5, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: add governance-enforce A_BLOCK diff-scoped secrets/path gate

⚙️ Configuration changes ✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Add a GitHub Actions enforce check to run A_BLOCK governance on PR diffs.
• Fetch and run @wave-av/governance from GitHub Packages with least-privilege permissions.
• Ensure the check reports before adding this repo to the org-required ruleset.
Diagram

graph TD
  A(("PR / Push")) --> B["Workflow: governance-enforce"] --> C["Checkout (full history)"] --> D["Setup Node 22"] --> E["Install @wave-av/governance (isolated)"] --> F["Run enforce.mjs on diff base"] --> G["Status check: enforce"]
  E --> H["GitHub Packages (npm registry)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Publish a pinned GitHub Action/composite action for enforcement
  • ➕ Avoids per-repo inline shell scripting and repeated workflow boilerplate
  • ➕ Can pin logic/version centrally while keeping SHA-pinning semantics
  • ➖ Still requires public accessibility; cannot depend on private repos for public consumers
  • ➖ Requires maintaining and versioning an action release pipeline
2. Rely solely on GitHub Advanced Security secret scanning
  • ➕ Native GitHub feature with less workflow maintenance
  • ➕ No dependency on internal npm package access
  • ➖ May not cover custom rules (e.g., hardcoded-paths policy) or match the org’s A_BLOCK semantics
  • ➖ Less control over diff-scoped gating and rule evolution
3. Use an org-level reusable workflow
  • ➕ Single source of truth; minimal per-repo config
  • ➖ Not viable if hosted in a private repo (public repos cannot call it)
  • ➖ Creates the same deadlock risk if required checks are enforced before the workflow exists in-repo

Recommendation: Keep the current vendored-workflow approach for public repos: it avoids cross-repo reusable-workflow access issues, preserves SHA-pinning and least privilege, and (critically) allows validating liveness before the repo is added to a required-status-check ruleset. Consider migrating later to a public composite action if you want to reduce workflow duplication without losing public-repo compatibility.

Files changed (1) +59 / -0

Other (1) +59 / -0
governance-enforce.ymlAdd A_BLOCK governance-enforce workflow and required 'enforce' check +59/-0

Add A_BLOCK governance-enforce workflow and required 'enforce' check

• Introduces a new GitHub Actions workflow that runs on pull requests and pushes to main/master. The job installs '@wave-av/governance' from GitHub Packages using the repo GITHUB_TOKEN and executes a diff-scoped enforcement command to block new secrets or hardcoded-path violations.

.github/workflows/governance-enforce.yml

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines +44 to +48
- name: fetch governance enforcer (isolated install)
run: |
mkdir -p "$RUNNER_TEMP/gov" && cd "$RUNNER_TEMP/gov"
printf '@wave-av:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' > .npmrc
npm install @wave-av/governance@^0.4.4 --no-save --no-audit --no-fund

@devin-ai-integration devin-ai-integration Bot Aug 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Fork PRs may not be able to authenticate to the private GitHub Packages registry

The install step authenticates to npm.pkg.github.com with secrets.GITHUB_TOKEN to pull @wave-av/governance. This repo is public and CONTRIBUTING.md instructs external contributors to fork before opening a PR. For pull_request runs originating from forks the token is read-only and package access depends on the package being explicitly linked/granted to this repository; if it is not, the isolated install will 401/403 and the enforce job will always fail for community PRs. Worth confirming before the check is added to the governance-a-block-enforce ruleset as required, otherwise fork contributions become unmergeable.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread .github/workflows/governance-enforce.yml Outdated
@qodo-code-review

qodo-code-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Fork PR gate deadlock 🐞 Bug ☼ Reliability
Description
The workflow always installs @wave-av/governance from GitHub Packages using GITHUB_TOKEN auth;
for pull_request events from forks, that token commonly cannot read org packages, so npm install
fails and the enforce check will go red for external contributors. This repo’s contributing guide
explicitly requires a fork-based PR flow, so once this check is required it can block those PRs from
merging.
Code

.github/workflows/governance-enforce.yml[R45-48]

+        run: |
+          mkdir -p "$RUNNER_TEMP/gov" && cd "$RUNNER_TEMP/gov"
+          printf '@wave-av:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' > .npmrc
+          npm install @wave-av/governance@^0.4.4 --no-save --no-audit --no-fund
Evidence
The workflow writes an auth-bearing .npmrc and installs @wave-av/governance using
GITHUB_TOKEN, which is exactly the part that will fail when the token can’t read packages; the
repo also documents that contributors should fork and open PRs, making fork PRs a first-class
scenario.

.github/workflows/governance-enforce.yml[34-48]
CONTRIBUTING.md[7-14]
README.md[1-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`governance-enforce` installs an org-scoped package from GitHub Packages using `GITHUB_TOKEN`. On `pull_request` events from forks, the token frequently lacks access to org packages, causing the workflow to fail and (once required) blocking fork-based contributions.

## Issue Context
The repo’s documented contribution flow is to fork and open a PR.

## Fix Focus Areas
- .github/workflows/governance-enforce.yml[18-59]

## Suggested fix
Add a fork-aware guard so the job still reports a green/usable check for fork PRs, e.g.:

- Add a job-level `if:` or step-level `if:` that detects forks:
 - `github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository`
- For fork PRs, either:
 - Run a safe, vendored alternative (e.g., reuse/duplicate the existing public-repo-guard logic), or
 - No-op with a clear `::notice::` explaining that the governance gate can’t run on forks due to package auth constraints.

(If you consider `pull_request_target` to regain package access, keep it strictly non-executing: do not run repository-provided scripts, and carefully check out the head SHA without evaluating PR-controlled code beyond read-only scanning.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Caret dependency drift ✓ Resolved 🐞 Bug ⛨ Security
Description
The workflow installs @wave-av/governance@^0.4.4, allowing the enforcement behavior of this merge
gate to change over time without any change in this repo. This reduces reproducibility for a
security gate and increases supply-chain blast radius compared to the repo’s existing approach of
pinning/verifying security tooling versions.
Code

.github/workflows/governance-enforce.yml[R47-48]

+          printf '@wave-av:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' > .npmrc
+          npm install @wave-av/governance@^0.4.4 --no-save --no-audit --no-fund
Evidence
The workflow explicitly uses a caret range for the enforcer dependency, while an existing security
gate in this repo pins tooling versions and even verifies checksums, demonstrating the repo’s
expected standard for deterministic security gates.

.github/workflows/governance-enforce.yml[44-48]
.github/workflows/public-repo-guard.yml[47-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow installs `@wave-av/governance` using a caret range (`^0.4.4`), so future runs can silently pick up newer releases and change enforcement behavior without a PR in this repo.

## Issue Context
This job is intended to be an A_BLOCK gate / required check; reproducibility matters for debugging and incident response.

## Fix Focus Areas
- .github/workflows/governance-enforce.yml[44-49]

## Suggested fix
- Change the install to an exact version, e.g. `npm install @wave-av/governance@0.4.4 ...`.
- Optionally add a lightweight integrity hardening step (e.g., record and verify the resolved version, or move to an immutable artifact strategy) so the gate is deterministic like other security tooling in this repo.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Fail-open base fallback ✓ Resolved 🐞 Bug ≡ Correctness
Description
If github.event.before is empty/all-zero and HEAD~1 is unavailable, the script falls back to
BASE=HEAD, which can make --changed "$BASE" compute an empty change set and skip scanning the
pushed content. This creates a narrow but real fail-open gap on initial/atypical pushes to
main/master.
Code

.github/workflows/governance-enforce.yml[R54-57]

+          BASE="${PR_BASE_SHA:-$PUSH_BEFORE_SHA}"
+          if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ]; then
+            BASE=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)
+          fi
Evidence
The script logic explicitly assigns BASE from PR base/before SHA, then replaces it with HEAD~1
or HEAD when those values are missing/zero; using HEAD as the base is the problematic fail-open
case.

.github/workflows/governance-enforce.yml[50-59]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When the workflow can’t determine a meaningful diff base (missing/zero `before` SHA and no `HEAD~1`), it falls back to using `HEAD` as `BASE`, which can result in an empty diff and skip enforcement.

## Issue Context
This is most relevant for unusual/initial push scenarios; PRs typically have `pull_request.base.sha`.

## Fix Focus Areas
- .github/workflows/governance-enforce.yml[54-57]

## Suggested fix
- Replace the `BASE=$(... || git rev-parse HEAD)` fail-open fallback with a fail-closed behavior (emit `::error::` and exit non-zero), OR
- Implement an explicit “initial push” base computation that still represents the full change set (only if the governance tool supports it), rather than using `HEAD` as the base.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 2 rules
Review mode: ⚖️ Balanced: This adds a security-sensitive CI enforcement workflow that affects pull-request liveness and secret scanning, so it warrants a careful single-pass review despite being localized to one file.

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +45 to +48
run: |
mkdir -p "$RUNNER_TEMP/gov" && cd "$RUNNER_TEMP/gov"
printf '@wave-av:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' > .npmrc
npm install @wave-av/governance@^0.4.4 --no-save --no-audit --no-fund

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Fork pr gate deadlock 🐞 Bug ☼ Reliability

The workflow always installs @wave-av/governance from GitHub Packages using GITHUB_TOKEN auth;
for pull_request events from forks, that token commonly cannot read org packages, so npm install
fails and the enforce check will go red for external contributors. This repo’s contributing guide
explicitly requires a fork-based PR flow, so once this check is required it can block those PRs from
merging.
Agent Prompt
## Issue description
`governance-enforce` installs an org-scoped package from GitHub Packages using `GITHUB_TOKEN`. On `pull_request` events from forks, the token frequently lacks access to org packages, causing the workflow to fail and (once required) blocking fork-based contributions.

## Issue Context
The repo’s documented contribution flow is to fork and open a PR.

## Fix Focus Areas
- .github/workflows/governance-enforce.yml[18-59]

## Suggested fix
Add a fork-aware guard so the job still reports a green/usable check for fork PRs, e.g.:

- Add a job-level `if:` or step-level `if:` that detects forks:
  - `github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository`
- For fork PRs, either:
  - Run a safe, vendored alternative (e.g., reuse/duplicate the existing public-repo-guard logic), or
  - No-op with a clear `::notice::` explaining that the governance gate can’t run on forks due to package auth constraints.

(If you consider `pull_request_target` to regain package access, keep it strictly non-executing: do not run repository-provided scripts, and carefully check out the head SHA without evaluating PR-controlled code beyond read-only scanning.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .github/workflows/governance-enforce.yml Outdated
@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

✅ Merged (0) · ☑ Fixed (0)

Process

  • No fixes were applied (no_fixes_applied)

cubic-dev-ai[bot]

This comment was marked as resolved.

…othing

Five defects, none of them cosmetic. Refs wave-av/claude-workstation#1747.

1. FAIL-OPEN DIFF BASE. `BASE=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)`
   — on a root commit `git rev-parse HEAD~1` prints its unresolved argument to stdout AND
   fails, so the `||` branch appends and BASE becomes a two-line string. `git diff` then
   exits 128, and the pinned enforcer turned that into zero files and a green check. Now:
   a reachability-checked base (a force-push can leave `github.event.before` pointing at a
   commit this checkout does not have), and with no resolvable base at all it diffs against
   the EMPTY TREE so the whole repo is scanned rather than nothing.

2. THE PINNED ENFORCER ITSELF FAILED OPEN. `^0.4.4` resolved to 0.4.4, whose file lister is
   `catch { return []; }` — any git error became zero files and rendered as
   `OK[enforce]: 0 changed file(s) scanned — 0 A_BLOCK violations`. A git error and a clean
   diff were byte-identical in the output. The fix had sat unreleased on claude-workstation
   main since 2026-07-29 because no `governance-v*` tag was ever pushed. Released now as
   0.4.6 and pinned exactly here.

3. TOKEN IN SCOPE FOR THE WRONG STEPS. `NODE_AUTH_TOKEN` was job-level, so it was also in
   the environment of the step that executes the downloaded package. Now step-scoped, and
   the .npmrc holding it is removed on exit.

4. INSTALL SCRIPTS RAN WITH THAT TOKEN. `npm install` runs preinstall/postinstall by
   default. Added `--ignore-scripts`.

5. CANCELLED PUSH RUNS WERE SCANNED BY NOBODY. `cancel-in-progress: true` applied to push
   runs, and each push run only diffs its own before..HEAD range — so a cancelled run's
   commits were never examined by anything. Now PR-only.

Also: `timeout-minutes: 10` and `set -euo pipefail`.

Receipt, against a scratch repo whose root commit carries a no-hardcoded-paths violation,
simulating a branch-creation push (`before` = all zeros):
  old logic -> malformed base -> caught error -> [] -> OK, 0 files scanned, PASS
  new logic -> "no diff base resolved ... scanning the whole tree" -> BLOCK, exit 1

Credit where it is due: several of these were found by the review bots on the sibling
vendoring PRs and are folded in here — the step-scoped token, the .npmrc cleanup, the exact
pin, `--ignore-scripts`, the force-push reachability check, `timeout-minutes`, and the
concurrency hole (5), which was crest-console#7's catch and which I had missed entirely.
@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_87d6d646-71f5-4228-9e49-7d4b2b84949d)

[ -n "$BASE" ] || BASE="${PUSH_BEFORE_SHA:-}"
# A base can be PRESENT and still unusable: a force-push leaves `github.event.before`
# pointing at a commit this checkout no longer contains.
if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High workflows/governance-enforce.yml:78

When a force-push makes github.event.before unreachable, the fallback to HEAD~1 only scans the latest commit, so any other commits introduced by the force-push are never examined while the gate can pass green. The HEAD~1 fallback narrows the diff to one commit instead of scanning from the empty tree like the documented fail-closed path. Remove the HEAD~1 fallback so an unreachable push base falls through to the empty-tree scan, or fail the job.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @.github/workflows/governance-enforce.yml around line 78:

When a force-push makes `github.event.before` unreachable, the fallback to `HEAD~1` only scans the latest commit, so any other commits introduced by the force-push are never examined while the gate can pass green. The `HEAD~1` fallback narrows the diff to one commit instead of scanning from the empty tree like the documented fail-closed path. Remove the `HEAD~1` fallback so an unreachable push base falls through to the empty-tree scan, or fail the job.

Evidence trail:
.github/workflows/governance-enforce.yml:74-93 at cc0e626f
.git diff MERGE_BASE REVIEWED_COMMIT -- .github/workflows/governance-enforce.yml

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +88 to +91
if [ -z "$BASE" ]; then
BASE="$(git hash-object -t tree /dev/null)"
echo "::notice::no diff base resolved (root commit or unreachable before-sha) — scanning the whole tree against the empty tree"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Empty-tree fallback base may not be accepted by the enforcer's diff path

The fail-closed fallback sets BASE to the empty-tree object id (git hash-object -t tree /dev/null) and passes it to enforce.mjs --changed "$BASE". Git special-cases the empty tree so git diff <empty-tree> HEAD works even though the object was never written, but this only holds if the enforcer runs a two-dot git diff. If enforce.mjs validates the argument as a commit (rev-parse <base>^{commit}, cat-file -e ...^{commit}) or uses a three-dot range (which requires a merge-base), the empty tree id will error out. Given 0.4.6 is described as failing closed on git errors, the job would then hard-fail rather than scan the tree — the fallback path would never actually deliver a full-tree scan. Worth confirming against the 0.4.6 implementation, since this path is precisely the one that only fires in rare situations (root commit, force-pushed before-sha) and would otherwise go untested.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…l a false pass

Correction to the previous commit on this branch. Refs wave-av/claude-workstation#1747.

That commit replaced the fail-open `BASE=HEAD` with a fallback to `HEAD~1`. `HEAD~1` is
also wrong: it scans exactly ONE commit, so a five-commit push whose base is indeterminate
(branch creation, force-push, unreachable `github.event.before`) examines the last commit
and reports a confident pass on the other four. A narrowed scan reported as a full pass is
the same defect in a quieter costume.

Receipt — scratch repo, five-commit push, violation planted in commit 1:
  HEAD~1 base      -> OK[enforce]: 1 changed file(s) scanned  -> PASS   (never saw it)
  empty-tree base  -> 5 changed file(s) scanned -> BLOCK[enforce]: no-hardcoded-paths, exit 1

Now: with no resolvable base of any kind, diff against git's empty-tree object so every
tracked file reads as added and the whole repo is scanned. Loud, never partial, never empty.

Credit: wave-av/wave-rig's copy on main already had this right, with the reasoning in a
comment ("HEAD~1 would skip earlier commits in a multi-commit push and let a violation
through"). The fan-out copied the broken shape from elsewhere and I did not check the one
repo that had already solved it.

Also from wave-rig: `merge_group` is now a declared trigger and `github.event.merge_group.
base_sha` joins the base chain. None of these repos runs a merge queue today, so the trigger
is inert — but a required check that never reports on an event the repo actually uses is a
permanent deadlock, and this closes that in advance rather than after someone hits it.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 new potential issues.

Open in Devin Review

Comment on lines +53 to +70
- name: fetch governance enforcer (isolated install)
# FIX 1 — token scoped to THIS STEP. At job level it was also in scope for the step that
# executes the downloaded package, and for anything else the job ever grows.
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/gov" && cd "$RUNNER_TEMP/gov"
trap 'rm -f "$RUNNER_TEMP/gov/.npmrc"' EXIT
printf '@wave-av:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n' > .npmrc
# FIX 2 — --ignore-scripts. npm runs preinstall/install/postinstall by default, so this
# step would execute dependency-authored code with the registry token in its environment.
# FIX 3 — exact pin, and 0.4.6 specifically. `^0.4.4` resolved to 0.4.4, whose file lister
# is `catch { return []; }` — ANY git error became zero files and rendered as
# `OK[enforce]: 0 changed file(s) scanned`. 0.4.6 fails closed on a git error instead.
# A caret is also a standing authorization for whatever is published next; a bump is now
# a visible commit in this file.
npm install @wave-av/governance@0.4.6 --no-save --no-audit --no-fund --ignore-scripts

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Pull requests from forks will always fail the new governance check

The new check downloads a private organization package using the automatic per-run token (npm install @wave-av/governance@0.4.6 at .github/workflows/governance-enforce.yml:70), which forked pull requests do not have permission to read, so every outside contribution fails this check with an install error instead of a real result.
Impact: External contributors following the documented fork-and-PR flow get a permanently red, unfixable check on their pull requests.

Why the token cannot read the package on fork PRs

For pull_request events triggered from a fork, GitHub issues a GITHUB_TOKEN scoped to the fork repository with read-only contents permission; it is not granted read access to private packages owned by wave-av. The .npmrc written at .github/workflows/governance-enforce.yml:62 authenticates to npm.pkg.github.com with that token, so npm install returns 401/404 and, with set -euo pipefail, the step fails. CONTRIBUTING.md documents fork-based contributions as the expected flow, and README/repo guard indicate this is a public repo, so this path is reachable.

Mitigations would be to skip or soft-fail the job when github.event.pull_request.head.repo.fork is true (while keeping it enforcing for same-repo PRs and pushes), or to make the package publicly readable / vendor the enforcer.

Prompt for agents
The governance-enforce workflow installs the private @wave-av/governance package from GitHub Packages using secrets.GITHUB_TOKEN. On pull_request events originating from forks (the flow documented in CONTRIBUTING.md for this public repo), that token is scoped to the fork and cannot read org-private packages, so the install step fails and the check is permanently red for external contributors. Consider gating the job on github.event.pull_request.head.repo.full_name == github.repository (running it only for same-repo PRs, pushes and merge_group), or moving the enforcement to a pull_request_target/scheduled job, or publishing the enforcer where fork PRs can fetch it. Note the workflow header explicitly warns against adding this repo to the required-check ruleset until the check is observed green, so the fork behaviour should be decided before that happens.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +73 to +81
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
MERGE_BASE_SHA: ${{ github.event.merge_group.base_sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
ENFORCE="$RUNNER_TEMP/gov/node_modules/@wave-av/governance/bin/enforce.mjs"
BASE="${PR_BASE_SHA:-}"
[ -n "$BASE" ] || BASE="${MERGE_BASE_SHA:-}"
[ -n "$BASE" ] || BASE="${PUSH_BEFORE_SHA:-}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 PR base sha may drag in unrelated base-branch commits into the scanned diff

github.event.pull_request.base.sha is the base commit at the time the PR event fired, and the checkout for a pull_request event is the merge commit. If the enforcer diffs two-dot (base..HEAD), any commits merged into main since the PR was opened appear in the diff, so unrelated legacy debt from other people's commits can fail this PR's gate. The workflow header explicitly claims "blocks NEW violations without failing on legacy debt", which only holds if the enforcer uses a merge-base (three-dot) diff. Note this expectation conflicts with ANALYSIS-0001's requirement that a bare tree object be acceptable — the two fallback paths want opposite diff semantics, so both should be confirmed against 0.4.6's implementation.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

rr:skip-cubic RF.P1 reviewer routing (#1039)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant