ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset - #25
ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset#25yakimoto wants to merge 4 commits into
Conversation
…never in the ruleset
Bugbot couldn't run - usage limit reachedBugbot 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_e755fd0d-902b-41d6-b7cc-0f3c1e1173d4) |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. 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 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Comment |
ApprovabilityVerdict: Needs human review Unable to check for correctness in 2d08570. Multiple unresolved review comments raise operational concerns about this security workflow: fork PR authentication, potential content-policy scan failures from internal repo name references, and fallback behavior. These should be addressed before merging. You can customize Macroscope's approvability policy. Learn more. |
PR Summary by QodoCI: add governance-enforce A_BLOCK diff-scoped secrets/path gate
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
| # VENDORED 2026-08-05 (claude-workstation#1624, E4 T4.9a). This repo was never in that ruleset's | ||
| # include list, because the list is 112 hand-maintained names and every one of them matches | ||
| # `wave-*`. A naming convention had silently become a security boundary: the repos that publish | ||
| # our npm packages — cli, sdk, adk, mcp-server, workflow-sdk — were the ones running with no | ||
| # A_BLOCK secrets scan at all. Copied verbatim from wave-moq-edge, and proven on a PUBLIC repo | ||
| # before fan-out: wave-av/cli#20, run 31011943790, conclusion success. |
There was a problem hiding this comment.
🔍 Header comment names potentially private repos, which the repo's own content gate can block
The header references claude-workstation#1624 and wave-moq-edge. This repo runs public-repo-guard which blocks any occurrence of names supplied via the org variable GUARD_PRIVATE_REPOS (scripts/public-repo-guard/content-policy.sh:100-114), matched as whole words across the whole tree including .github/** (the scan uses --hidden --no-ignore-vcs). If either name is in that variable, this PR will fail the existing secrets/content gate. Worth checking the variable's contents, or annotating the lines with # guard:allow <reason>.
Was this helpful? React with 👍 or 👎 to provide feedback.
| - 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 |
There was a problem hiding this comment.
🔍 Fork PRs will attempt an authenticated GitHub Packages install
On pull_request from a fork of this public repo, NODE_AUTH_TOKEN is the base repo's read-only GITHUB_TOKEN. If the @wave-av/governance package's repo access list does not grant this repo read access, npm install fails and the job errors rather than skipping — which matters because the comment intends this check to eventually become required. Verify a fork PR run succeeds before adding the check to the ruleset.
Was this helpful? React with 👍 or 👎 to provide feedback.
Code Review by Qodo
1.
|
Qodo FixerNo findings are within the configured fix scope. To change which findings are fixed, adjust the setting on your Qodo configuration page. |
There was a problem hiding this comment.
2 issues found across 1 file
Confidence score: 3/5
- In
.github/workflows/governance-enforce.yml, forkedpull_requestruns may not be able to install@wave-av/governancebecauseNODE_AUTH_TOKENmaps to the base repo’s read-onlyGITHUB_TOKEN, which can make governance checks fail for external contributors and block PR validation — add a fork-safe auth path or skip/private-package fallback for forked PRs. - In
.github/workflows/governance-enforce.yml, the vendored header references internal repo names that can trippublic-repo-guardif they matchGUARD_PRIVATE_REPOS, causing policy-check failures unrelated to this repo’s code changes — remove/sanitize those identifiers or adjust guard matching for vendored headers.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/governance-enforce.yml">
<violation number="1" location=".github/workflows/governance-enforce.yml:12">
P2: The vendored header comment names internal repos (`claude-workstation#1624`, `wave-moq-edge`). If either name is present in the org's `GUARD_PRIVATE_REPOS` variable, this repo's own `public-repo-guard` content-policy scan (which matches whole words across the tree, including `.github/**`) will flag these lines and fail the PR. Worth checking the variable's contents or annotating with `# guard:allow <reason>` if intentional.</violation>
<violation number="2" location=".github/workflows/governance-enforce.yml:48">
P2: For pull_request events from a fork, `NODE_AUTH_TOKEN` resolves to the base repo's read-only `GITHUB_TOKEN`. If the `@wave-av/governance` package's repo access list doesn't include this repo, `npm install` will fail and the enforce job will error rather than gracefully skip — a real problem once this check becomes a required status check on fork PRs. Verify a fork PR run succeeds before adding this to the required ruleset.</violation>
</file>
Architecture diagram
sequenceDiagram
participant PR as Pull Request Event
participant GH as GitHub Actions Runner
participant Repo as Repo Checkout
participant NPM as GitHub npm Registry
participant Gov as @wave-av/governance
participant Workflow as Workflow Run
Note over PR,Workflow: NEW: governance-enforce workflow (A_BLOCK gate)
PR->>GH: trigger on pull_request or push to main/master
GH->>GH: evaluate concurrency group
GH->>GH: allocate job with contents:read + packages:read
GH->>Repo: actions/checkout@df4cb1c (persist-credentials:false)
Repo-->>GH: full git history (fetch-depth:0)
GH->>GH: actions/setup-node@48b55a0 (node-22)
Note over GH,NPM: Isolated install into RUNNER_TEMP
GH->>GH: create ~/gov dir + write .npmrc
Note over GH: printf writes literal ${NODE_AUTH_TOKEN}<br/>npm expands at runtime (no secret written to disk)
GH->>NPM: npm install @wave-av/governance@^0.4.4 --no-save
NPM-->>GH: package + dependencies (internal visibility)
Note over GH,Gov: Diff-scoped enforcement
GH->>GH: resolve BASE SHA (PR base.sha or event.before)
alt PR event with base.sha
GH->>GH: BASE = github.event.pull_request.base.sha
else push event
GH->>GH: BASE = github.event.before
else fallback (first commit)
GH->>GH: BASE = HEAD~1 or HEAD
end
GH->>Gov: node enforce.mjs --changed $BASE
Gov->>Repo: read git diff against BASE
Gov->>Gov: scan diff for secrets (Doppler tokens, hardcoded credentials)
Gov->>Gov: scan diff for hardcoded paths
alt Diff has NEW violations
Gov-->>GH: exit non-zero (fail)
GH-->>PR: status: ❌ enforce (blocking)
else Clean diff or only legacy debt
Gov-->>GH: exit zero (pass)
GH-->>PR: status: ✅ enforce (green)
end
Note over PR,Workflow: Governance ruleset (17901847) is NOT yet updated<br/>This check runs but is not required until ruleset is extended
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| # include list, because the list is 112 hand-maintained names and every one of them matches | ||
| # `wave-*`. A naming convention had silently become a security boundary: the repos that publish | ||
| # our npm packages — cli, sdk, adk, mcp-server, workflow-sdk — were the ones running with no | ||
| # A_BLOCK secrets scan at all. Copied verbatim from wave-moq-edge, and proven on a PUBLIC repo |
There was a problem hiding this comment.
P2: The vendored header comment names internal repos (claude-workstation#1624, wave-moq-edge). If either name is present in the org's GUARD_PRIVATE_REPOS variable, this repo's own public-repo-guard content-policy scan (which matches whole words across the tree, including .github/**) will flag these lines and fail the PR. Worth checking the variable's contents or annotating with # guard:allow <reason> if intentional.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/governance-enforce.yml, line 12:
<comment>The vendored header comment names internal repos (`claude-workstation#1624`, `wave-moq-edge`). If either name is present in the org's `GUARD_PRIVATE_REPOS` variable, this repo's own `public-repo-guard` content-policy scan (which matches whole words across the tree, including `.github/**`) will flag these lines and fail the PR. Worth checking the variable's contents or annotating with `# guard:allow <reason>` if intentional.</comment>
<file context>
@@ -0,0 +1,59 @@
+# include list, because the list is 112 hand-maintained names and every one of them matches
+# `wave-*`. A naming convention had silently become a security boundary: the repos that publish
+# our npm packages — cli, sdk, adk, mcp-server, workflow-sdk — were the ones running with no
+# A_BLOCK secrets scan at all. Copied verbatim from wave-moq-edge, and proven on a PUBLIC repo
+# before fan-out: wave-av/cli#20, run 31011943790, conclusion success.
+#
</file context>
| 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 |
There was a problem hiding this comment.
P2: For pull_request events from a fork, NODE_AUTH_TOKEN resolves to the base repo's read-only GITHUB_TOKEN. If the @wave-av/governance package's repo access list doesn't include this repo, npm install will fail and the enforce job will error rather than gracefully skip — a real problem once this check becomes a required status check on fork PRs. Verify a fork PR run succeeds before adding this to the required ruleset.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/governance-enforce.yml, line 48:
<comment>For pull_request events from a fork, `NODE_AUTH_TOKEN` resolves to the base repo's read-only `GITHUB_TOKEN`. If the `@wave-av/governance` package's repo access list doesn't include this repo, `npm install` will fail and the enforce job will error rather than gracefully skip — a real problem once this check becomes a required status check on fork PRs. Verify a fork PR run succeeds before adding this to the required ruleset.</comment>
<file context>
@@ -0,0 +1,59 @@
+ 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
+ - name: A_BLOCK enforce (secrets + hardcoded paths on the diff)
+ env:
</file context>
…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.
Bugbot couldn't run - usage limit reachedBugbot 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_8721ba0f-99dd-4b7b-8c30-170edc2c2dc8) |
| # pointing at a commit this checkout no longer contains. | ||
| if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] \ | ||
| || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then | ||
| BASE="$(git rev-parse --verify --quiet 'HEAD~1' || true)" |
There was a problem hiding this comment.
🔴 Critical workflows/governance-enforce.yml:80
When a force-push makes github.event.before unreachable, the fallback resolves BASE to HEAD~1 whenever that commit exists. The scan then diffs only the newest commit against its immediate parent, so any secret or hardcoded-path violation in the other commits introduced by the rewritten push is silently unexamined while the gate can still pass. This defeats the fix's stated goal of scanning every commit the push introduced. HEAD~1 is a per-commit base, not a per-push base. Consider falling straight through to the empty-tree scan (or otherwise resolving the oldest commit in the push range) so the full set of introduced commits is examined.
- BASE="$(git rev-parse --verify --quiet 'HEAD~1' || true)"🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @.github/workflows/governance-enforce.yml around line 80:
When a force-push makes `github.event.before` unreachable, the fallback resolves `BASE` to `HEAD~1` whenever that commit exists. The scan then diffs only the newest commit against its immediate parent, so any secret or hardcoded-path violation in the other commits introduced by the rewritten push is silently unexamined while the gate can still pass. This defeats the fix's stated goal of scanning every commit the push introduced. `HEAD~1` is a per-commit base, not a per-push base. Consider falling straight through to the empty-tree scan (or otherwise resolving the oldest commit in the push range) so the full set of introduced commits is examined.
Evidence trail:
https://github.com/wave-av/sdk-python/tree/320e1b4637df89fe3e155629c9c7f98871349d9a/.github/workflows/governance-enforce.yml#L30-L35
https://github.com/wave-av/sdk-python/tree/320e1b4637df89fe3e155629c9c7f98871349d9a/.github/workflows/governance-enforce.yml#L74-L93
git diff MERGE_BASE REVIEWED_COMMIT -- .github/workflows/governance-enforce.yml
…d of scanning only HEAD~1 Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
| if [ -z "$BASE" ]; then | ||
| BASE="$(git hash-object -t tree /dev/null)" | ||
| echo "::notice::no usable diff base (branch creation, root commit, or force-push with unreachable before-sha) — scanning the whole tree against the empty tree" | ||
| fi |
There was a problem hiding this comment.
🔍 Empty-tree fallback turns any missing base into a whole-repo scan that can block main
When no usable base is found (branch creation, root commit, force-push with unreachable before-sha) the workflow diffs against the empty tree, so every tracked file counts as added. This is intentional fail-closed behavior, but it contradicts the diff-scoped promise in the header comment (.github/workflows/governance-enforce.yml:4-5): any pre-existing legacy violation in the repo will fail that run, including on pushes to main after a force-push. Worth confirming the repo currently scans clean whole-tree before this check is added to the org ruleset.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
The empty-tree fallback is the intentional FIX 5 fail-closed design for exceptional paths (root commit, force-push with unreachable before-sha), where failing loudly on legacy debt beats passing having scanned nothing. The rollout plan already gates the ruleset entry on observed green runs, which addresses the "confirm it scans clean" advisory.
…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.
| if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] \ | ||
| || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then | ||
| BASE="$(git hash-object -t tree /dev/null)" | ||
| echo "::warning::indeterminate diff base (root commit, branch creation, or unreachable before-sha) — scanning the full tree against the empty-tree object so no commit is skipped" | ||
| fi |
There was a problem hiding this comment.
🔍 Empty-tree fallback assumes the enforcer's --changed accepts a bare tree object
The fallback sets BASE to the empty-tree object hash (git hash-object -t tree /dev/null) and passes it as --changed. This only works if @wave-av/governance builds its diff with a two-dot form (e.g. git diff --name-only <base> HEAD). If the enforcer internally uses the symmetric three-dot form (git diff <base>...HEAD) or ever resolves the argument with <base>^{commit}, the empty-tree hash is not a commit and git will abort — turning the "loud, never partial" path into a hard job failure on every branch-creation push or force-push with an unreachable before-sha. Worth verifying against 0.4.6's bin/enforce.mjs once, since this path is exactly the one that cannot be exercised in a normal PR.
Was this helpful? React with 👍 or 👎 to provide feedback.
Adds the
governance-enforceA_BLOCK gate (secrets / hardcoded-paths, diff-scoped) to this repo. Part of claude-workstation#1624 E4 T4.9a, following thewave-av/cli#20pilot.Why this repo had no secrets scan
The org ruleset
governance-a-block-enforce(17901847) requires anenforcecheck across the fleet. Its scope is an explicit include list of 112 hand-maintained repository names — and every one of them matcheswave-*.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_checksruleset 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. Ifenforcereports 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/governanceis aninternal-visibility package owned byclaude-workstation, so whether a public repo'sGITHUB_TOKENcan read it was the one real assumption. Rather than fan out on the inference, it was piloted on a single repo first: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 becauseauto-approve.ymlfails silently on every public repo — it calls a reusable workflow in the privatewave-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/checkout@df4cb1c,actions/setup-node@48b55a0)persist-credentials: falseon checkoutcontents: read+packages: readRUNNER_TEMP,--no-save, so nothing touches this repo's dependency tree.npmrcis written with a literal${NODE_AUTH_TOKEN}(single-quotedprintf) which npm expands at run time — no secret value is ever written to disk or a log${{ }}inputs (base.sha,event.before) are routed throughenv:and read as"$VAR", never interpolated into the script bodyDiff-scoped by design: it blocks new violations without failing on legacy debt.
Refs wave-av/claude-workstation#1624.
Note
Low Risk
CI-only addition with no application or runtime code changes; risk is limited to workflow misconfiguration (e.g. false greens) rather than production behavior.
Overview
Adds
.github/workflows/governance-enforce.yml, introducing an org A_BLOCK gate that scans PR/push diffs for secrets-in-git, Doppler policy, and hardcoded paths via an isolated install of@wave-av/governance@0.4.6.The workflow runs on
pull_requestandpushtomain/master, reports anenforcejob, and is meant to go green here before this repo is added to thegovernance-a-block-enforceruleset (required check with no reporter would deadlock merges).Hardening baked into the vendored copy: SHA-pinned Actions,
packages: read, registry token only on the npm install step with--ignore-scripts, push concurrency that does not cancel in-progress push runs (so each push range stays scanned), and fail-closed diff-base resolution (empty tree fallback instead ofHEADvsHEAD).Reviewed by Cursor Bugbot for commit 320e1b4. Configure here.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.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.6on 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 2d08570.