feat(guards): actions/runner-isolated — no pull_request-triggered job on a self-hosted runner #60

Merged
claude-bot-andresmgsl merged 3 commits from build/58-runner-isolated into main 2026-07-23 12:24:42 +00:00
claude-bot-andresmgsl commented 2026-07-23 11:16:43 +00:00 (Migrated from github.com)

Closes #58. Part of epic #56 (decision D5): a fifth guard in the family — no pull_request-triggered workflow may name a self-hosted runner. Unreviewed fork-branch code must never execute on our own hardware; the write-token and secrets toggles protect credentials, not the runner.

One deliberate strengthening over the spec's letter, flagged for review: §2's same-line rule (runs-onself-hosted on one line) would pass the block-sequence form (runs-on: newline - self-hosted) — a false negative, which §3 itself declares the one defect this guard must not have ("file-level granularity has none for the modelled threat"). The script therefore also opens a window over a bare runs-on: key's list items. Every spec'd case behaves exactly as written; the change is strictly stricter, tested (three cases), and carried in the script header. If triage or the panel reads §2 as binding to the letter, I'll take a ruling.

Worklog

  • actions/runner-isolated/runner-isolated.sh — the scan, the two-condition rule, the exit codes, header carrying the why (threat, file-level trade, known limits)
  • actions/runner-isolated/action.yml — composite wrapper, workflows-dir input, description naming the gaps
  • test/runner-isolated.test.sh — the 12-case fixture matrix, the block-sequence cases, env wiring, ceremony's own tree as a pass (22 checks)
  • .github/workflows/ci.yml — the guard joins self-guards
  • docs/CONSUMERS.md — the consumer-facing entry (+ the pin-count in Version pinning: four guards, five references)
  • CHANGELOG.md — one line under ## Unreleased, inserted above ## 0.1.0
  • shellcheck + actionlint + bash test/run.sh green locally (13 test files, 0 failed; self-ref-check green)
  • red-run evidence on a scratch branch, linked below
  • mark ready-for-review, request the panel (codex, grok, kimi)

Acceptance criteria (from #58)

  • Fails PR trigger + self-hosted in all three on: shapes (block, inline list, scalar) and for the quoted "on": key
  • Fails pull_request_target + self-hosted
  • Passes push-only + self-hosted (incubator's deploy.yml shape)
  • Passes pull_request + ubuntu-latest (incubator's pr-checks.yml shape)
  • Passes self-hosted in a comment only, and a missing workflows directory
  • Failure names every offending file and the offending runs-on line; success prints a one-line count
  • Action description names both known gaps (workflow_call reachability, runner groups / expression indirection)
  • ci.yml self-guards runs the guard against ceremony's real tree, green
  • Guard shown red once on a scratch branch, run linked here
  • No \x escapes, no gawk-only constructs; bash test/run.sh, shellcheck, actionlint all green

Verification

  • Local: bash test/run.sh — 13 test files pass (runner-isolated adds 22 checks); shellcheck-all.sh and actionlint-all.sh clean; self-ref-check.sh green.
  • Green on the real tree: this PR's CI at head 5d709d7self-guards (now four steps) passes, run 30002671018.
  • Red once, on a scratch branch: claude-bot-andresmgsl/ceremony#2 (fork, never merges) adds a pull_request-triggered runs-on: self-hosted workflow (never-matching paths filter, so it cannot execute — its text is the violation). Run 30002684920: self-guards fails at the runner-isolated step naming scratch-selfhosted.yml and its runs-on: self-hosted line (job log), and the unit suite's own-tree case catches the same violation independently (test job) — a workflow change that breaks the guard shows up twice, as designed.

Incubator's adoption of this guard is deliberately not here — it rides on #16's follow-ups, per the issue's §7.

Closes #58. Part of epic #56 (decision D5): a fifth guard in the family — no `pull_request`-triggered workflow may name a self-hosted runner. Unreviewed fork-branch code must never execute on our own hardware; the write-token and secrets toggles protect credentials, not the runner. **One deliberate strengthening over the spec's letter, flagged for review:** §2's same-line rule (`runs-on` ∧ `self-hosted` on one line) would pass the block-sequence form (`runs-on:` newline `- self-hosted`) — a false negative, which §3 itself declares the one defect this guard must not have ("file-level granularity has none for the modelled threat"). The script therefore also opens a window over a bare `runs-on:` key's list items. Every spec'd case behaves exactly as written; the change is strictly stricter, tested (three cases), and carried in the script header. If triage or the panel reads §2 as binding to the letter, I'll take a ruling. ## Worklog - [x] `actions/runner-isolated/runner-isolated.sh` — the scan, the two-condition rule, the exit codes, header carrying the why (threat, file-level trade, known limits) - [x] `actions/runner-isolated/action.yml` — composite wrapper, `workflows-dir` input, description naming the gaps - [x] `test/runner-isolated.test.sh` — the 12-case fixture matrix, the block-sequence cases, env wiring, ceremony's own tree as a pass (22 checks) - [x] `.github/workflows/ci.yml` — the guard joins `self-guards` - [x] `docs/CONSUMERS.md` — the consumer-facing entry (+ the pin-count in Version pinning: four guards, five references) - [x] `CHANGELOG.md` — one line under `## Unreleased`, inserted above `## 0.1.0` - [x] shellcheck + actionlint + `bash test/run.sh` green locally (13 test files, 0 failed; self-ref-check green) - [x] red-run evidence on a scratch branch, linked below - [x] mark ready-for-review, request the panel (codex, grok, kimi) ## Acceptance criteria (from #58) - [x] Fails PR trigger + self-hosted in all three `on:` shapes (block, inline list, scalar) and for the quoted `"on":` key - [x] Fails `pull_request_target` + self-hosted - [x] Passes `push`-only + self-hosted (incubator's `deploy.yml` shape) - [x] Passes `pull_request` + `ubuntu-latest` (incubator's `pr-checks.yml` shape) - [x] Passes `self-hosted` in a comment only, and a missing workflows directory - [x] Failure names every offending file and the offending `runs-on` line; success prints a one-line count - [x] Action description names both known gaps (`workflow_call` reachability, runner groups / expression indirection) - [x] `ci.yml` `self-guards` runs the guard against ceremony's real tree, green - [x] Guard shown red once on a scratch branch, run linked here - [x] No `\x` escapes, no gawk-only constructs; `bash test/run.sh`, shellcheck, actionlint all green ## Verification - **Local**: `bash test/run.sh` — 13 test files pass (runner-isolated adds 22 checks); `shellcheck-all.sh` and `actionlint-all.sh` clean; `self-ref-check.sh` green. - **Green on the real tree**: this PR's CI at head `5d709d7` — `self-guards` (now four steps) passes, [run 30002671018](https://github.com/heavy-duty/ceremony/actions/runs/30002671018). - **Red once, on a scratch branch**: [claude-bot-andresmgsl/ceremony#2](https://github.com/claude-bot-andresmgsl/ceremony/pull/2) (fork, never merges) adds a `pull_request`-triggered `runs-on: self-hosted` workflow (never-matching `paths` filter, so it cannot execute — its text is the violation). [Run 30002684920](https://github.com/claude-bot-andresmgsl/ceremony/actions/runs/30002684920): `self-guards` fails at the runner-isolated step naming `scratch-selfhosted.yml` and its `runs-on: self-hosted` line ([job log](https://github.com/claude-bot-andresmgsl/ceremony/actions/runs/30002684920/job/89191326086)), and the unit suite's own-tree case catches the same violation independently ([test job](https://github.com/claude-bot-andresmgsl/ceremony/actions/runs/30002684920/job/89191326096)) — a workflow change that breaks the guard shows up twice, as designed. Incubator's adoption of this guard is deliberately **not** here — it rides on #16's follow-ups, per the issue's §7.
danmt (Migrated from github.com) reviewed 2026-07-23 11:16:43 +00:00
codex-bot-andresmgsl commented 2026-07-23 11:25:39 +00:00 (Migrated from github.com)

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405
grok-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 11:25:55 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: Approve — I agree with this as-is.

Cold-start first review of tip 5d709d7. Convo (body only; no prior reviews/comments) + full tip diff + checks re-read.

What holds

  • Threat model and file-level granularity match #58 §3: false positives fix by splitting workflows; false negatives are the forbidden class. Header + action description both carry that trade and the known gaps (workflow_call reachability, runner groups / expression indirection).
  • Block-sequence strengthening is correct, not scope creep. Same-line-only would miss runs-on: / - self-hosted and create a false negative — exactly what a security guard must not have. Window open/close logic looks sound; tested for both PR-fail and push-pass.
  • Trigger half covers block, inline list, scalar, and quoted "on": / 'on':; pull_request substring also catches pull_request_target as specified. Comment skip on both halves; missing/empty workflows dir passes.
  • Fixture matrix covers the acceptance table (fail shapes, mixed jobs, multi-file naming, deploy/pr-checks shapes, comment-only, schedule-only, pull_request outside on:, env/workflows-dir wiring, ceremony's own tree).
  • Wiring: self-guards adds ./actions/runner-isolated; CONSUMERS pin list + "four CI guards" / five-ref pin-bump note updated; CHANGELOG one line under Unreleased on 0.1.1-dev.
  • CI green on this head including self-guards and test; red-once scratch evidence linked.

Judgment call

Agree with the stricter block-sequence detection over the letter of §2 — it is still strictly the §3 rule.

Nits (non-blocking)

  • None that affect merge.

No blockers. Re-request if the scan rules or action surface moves.

**Verdict: Approve** — I agree with this as-is. Cold-start first review of tip `5d709d7`. Convo (body only; no prior reviews/comments) + full tip diff + checks re-read. ### What holds - Threat model and file-level granularity match #58 §3: false positives fix by splitting workflows; false negatives are the forbidden class. Header + action description both carry that trade and the known gaps (`workflow_call` reachability, runner groups / expression indirection). - **Block-sequence strengthening is correct**, not scope creep. Same-line-only would miss `runs-on:` / `- self-hosted` and create a false negative — exactly what a security guard must not have. Window open/close logic looks sound; tested for both PR-fail and push-pass. - Trigger half covers block, inline list, scalar, and quoted `"on":` / `'on':`; `pull_request` substring also catches `pull_request_target` as specified. Comment skip on both halves; missing/empty workflows dir passes. - Fixture matrix covers the acceptance table (fail shapes, mixed jobs, multi-file naming, deploy/pr-checks shapes, comment-only, schedule-only, `pull_request` outside `on:`, env/`workflows-dir` wiring, ceremony's own tree). - Wiring: `self-guards` adds `./actions/runner-isolated`; CONSUMERS pin list + "four CI guards" / five-ref pin-bump note updated; CHANGELOG one line under Unreleased on `0.1.1-dev`. - CI green on this head including `self-guards` and `test`; red-once scratch evidence linked. ### Judgment call Agree with the stricter block-sequence detection over the letter of §2 — it is still strictly the §3 rule. ### Nits (non-blocking) - None that affect merge. No blockers. Re-request if the scan rules or action surface moves.
grok-bot-andresmgsl commented 2026-07-23 11:25:55 +00:00 (Migrated from github.com)

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405
kimi-bot-andresmgsl commented 2026-07-23 11:26:15 +00:00 (Migrated from github.com)

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405

🔎 reviewing head 5d709d7dd21601e7a9eebafb270c5b442a135405
codex-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 11:27:03 +00:00
codex-bot-andresmgsl (Migrated from github.com) left a comment

Verified head 5d709d7dd21601e7a9eebafb270c5b442a135405 against #58 and the repository's guard conventions.

The implementation satisfies the acceptance criteria: all required trigger shapes and pull_request_target are caught, legal push/hosted-runner and absent-directory cases pass, every offender is reported with its matching runner line, the composite action names both known gaps, ceremony dogfoods the guard, the consumer docs and changelog are updated, and the linked scratch run supplies the required red evidence. The block-sequence strengthening is consistent with the issue's security invariant and closes an otherwise real false negative without weakening any specified behavior.

Verification:

  • bash test/run.sh — 13 test files passed, 0 failed; the new suite contributes 22 passing checks.
  • bash .github/scripts/shellcheck-all.sh — 29 tracked scripts clean.
  • bash .github/scripts/actionlint-all.sh — 6 workflow/action files clean.
  • Current head checks are green, including self-guards, test, and action-exercise.
Verified head `5d709d7dd21601e7a9eebafb270c5b442a135405` against #58 and the repository's guard conventions. The implementation satisfies the acceptance criteria: all required trigger shapes and `pull_request_target` are caught, legal push/hosted-runner and absent-directory cases pass, every offender is reported with its matching runner line, the composite action names both known gaps, ceremony dogfoods the guard, the consumer docs and changelog are updated, and the linked scratch run supplies the required red evidence. The block-sequence strengthening is consistent with the issue's security invariant and closes an otherwise real false negative without weakening any specified behavior. Verification: - `bash test/run.sh` — 13 test files passed, 0 failed; the new suite contributes 22 passing checks. - `bash .github/scripts/shellcheck-all.sh` — 29 tracked scripts clean. - `bash .github/scripts/actionlint-all.sh` — 6 workflow/action files clean. - Current head checks are green, including `self-guards`, `test`, and `action-exercise`.
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 11:32:48 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Approve — head 5d709d7.

Reviewed against #58's spec, ran everything runnable in a detached worktree, and probed past the fixture matrix.

Verification I ran myself (worktree at 5d709d7):

  • bash test/runner-isolated.test.sh — 22/22 checks pass, including the block-sequence cases and ceremony's own tree as a non-vacuous pass.
  • bash test/run.sh — the only failures are the package-json cases requiring node, which this box lacks; main fails them identically, so they are pre-existing and environmental, not from this PR. CI's test job (which has node, plus the shellcheck and actionlint steps at ci.yml:18-29) is green at this head, as are self-guards (now four steps) and action-exercise.
  • Six adversarial fixtures of my own, all behaved correctly: comment inside a block sequence does not close the runs-on: window (fails, as it must); single-quoted 'on': key fails; trailing comment after a bare runs-on: key still opens the window (fails); a pull_request mention in a comment inside the on: block does not trigger (passes); the trigger block ends at the next top-level key (passes); runs-on: {group: …} passes — the documented runner-group gap, named in action.yml's description so silence is not read as coverage.
  • Red-run evidence verified live: run 30002684920 on the fork failed at self-guards, with the test job's own-tree case catching the same violation independently — exactly the twice-caught design the PR body claims.

Acceptance criteria, one pass over #58's list: all ten hold. The three on: shapes plus the quoted key fail; pull_request_target fails; deploy.yml's push+self-hosted and pr-checks.yml's PR+hosted shapes pass; comment-only and missing-dir pass; failure output names every offending file and its runs-on line (fixture 11 pins both-files-named); success prints a one-line count; the action description names both known gaps; self-guards runs it against the real tree; no \x escapes — the script uses no awk at all. Changelog line matches the spec verbatim, inserted above ## 0.1.0. CONSUMERS.md gains the entry and the pin-count edit (three→four guards, four→five references) — I checked the surrounding text, both counts read correctly.

The flagged strengthening (block-sequence window): endorsed. #58 §3 declares false negatives the one defect this guard must not have, and the block-sequence form is ordinary, GitHub-accepted YAML — a same-line-only rule would have shipped a real false negative. The change is strictly stricter than §2's letter, every spec'd case behaves as written, it's tested (three cases), and it's carried in the script header. This is the spec's own intent applied, not a departure from it; no ruling needed from where I sit.

Conventions: set -euo pipefail in the executable, set -u in the test, the decision lives in a script a test drives, and the header carries the why — the threat, the file-level trade, both known limits, with issue references. Matches the four guards beside it.

Non-blocking nit (rides the approval): runner-isolated.sh:144 keys the runner half off any line containing runs-on, so a job named e.g. x-runs-on-y with self-hosted elsewhere on the line would false-positive. Strictly stricter, self-inflicted, and the fix message says exactly what to do — fine as-is.

**Approve** — head `5d709d7`. Reviewed against #58's spec, ran everything runnable in a detached worktree, and probed past the fixture matrix. **Verification I ran myself (worktree at `5d709d7`):** - `bash test/runner-isolated.test.sh` — 22/22 checks pass, including the block-sequence cases and ceremony's own tree as a non-vacuous pass. - `bash test/run.sh` — the only failures are the `package-json` cases requiring node, which this box lacks; `main` fails them identically, so they are pre-existing and environmental, not from this PR. CI's `test` job (which has node, plus the shellcheck and actionlint steps at ci.yml:18-29) is green at this head, as are `self-guards` (now four steps) and `action-exercise`. - Six adversarial fixtures of my own, all behaved correctly: comment inside a block sequence does **not** close the `runs-on:` window (fails, as it must); single-quoted `'on':` key fails; trailing comment after a bare `runs-on:` key still opens the window (fails); a `pull_request` mention in a comment inside the `on:` block does not trigger (passes); the trigger block ends at the next top-level key (passes); `runs-on: {group: …}` passes — the documented runner-group gap, named in `action.yml`'s description so silence is not read as coverage. - Red-run evidence verified live: run 30002684920 on the fork failed at `self-guards`, with the `test` job's own-tree case catching the same violation independently — exactly the twice-caught design the PR body claims. **Acceptance criteria, one pass over #58's list:** all ten hold. The three `on:` shapes plus the quoted key fail; `pull_request_target` fails; `deploy.yml`'s push+self-hosted and `pr-checks.yml`'s PR+hosted shapes pass; comment-only and missing-dir pass; failure output names every offending file and its `runs-on` line (fixture 11 pins both-files-named); success prints a one-line count; the action description names both known gaps; `self-guards` runs it against the real tree; no `\x` escapes — the script uses no awk at all. Changelog line matches the spec verbatim, inserted above `## 0.1.0`. CONSUMERS.md gains the entry and the pin-count edit (three→four guards, four→five references) — I checked the surrounding text, both counts read correctly. **The flagged strengthening (block-sequence window):** endorsed. #58 §3 declares false negatives the one defect this guard must not have, and the block-sequence form is ordinary, GitHub-accepted YAML — a same-line-only rule would have shipped a real false negative. The change is strictly stricter than §2's letter, every spec'd case behaves as written, it's tested (three cases), and it's carried in the script header. This is the spec's own intent applied, not a departure from it; no ruling needed from where I sit. **Conventions:** `set -euo pipefail` in the executable, `set -u` in the test, the decision lives in a script a test drives, and the header carries the why — the threat, the file-level trade, both known limits, with issue references. Matches the four guards beside it. Non-blocking nit (rides the approval): `runner-isolated.sh:144` keys the runner half off any line containing `runs-on`, so a job *named* e.g. `x-runs-on-y` with `self-hosted` elsewhere on the line would false-positive. Strictly stricter, self-inflicted, and the fix message says exactly what to do — fine as-is.
claude-bot-andresmgsl commented 2026-07-23 11:36:50 +00:00 (Migrated from github.com)

Round summary — handoff

Round 1 (head 5d709d7, the only head this PR has had): codex, grok, kimi all approve. No fixes were required — the head the panel approved is the head under handoff.

What each verdict verified:

  • grok — cold-start read of the full tip diff against #58: threat model, trigger/runner halves, fixture matrix vs the acceptance table, wiring (self-guards, CONSUMERS, CHANGELOG), CI green, red-run evidence. Endorsed the flagged block-sequence strengthening as the spec's own §3 intent, not scope creep.
  • codex — ran the full local gauntlet (test/run.sh 13 files green, shellcheck 29 scripts, actionlint 6 files) and checked all ten acceptance criteria against head.
  • kimi — independent worktree run (22/22 checks), six adversarial fixtures of their own beyond the matrix (comment-in-block-sequence, single-quoted 'on':, trailing comment after bare runs-on:, comment inside on: block, trigger-block termination, runner-group gap), live re-verification of the red-run evidence, and a line-by-line pass over all ten acceptance criteria. Also endorsed the strengthening: "the spec's own intent applied", no ruling needed.

The one flagged judgment call (block-sequence runs-on: window, stricter than #58 §2's same-line letter) was reviewed explicitly by all three and endorsed by all three — no triage ruling required.

Non-blocking nit on record (kimi, rides the approval): runner-isolated.sh:144 keys the runner half off any line containing runs-on, so a job named like x-runs-on-y with self-hosted elsewhere on that line would false-positive. Strictly stricter (a false positive, never a false negative), and the failure message tells the author exactly what to do. Left as-is per the approval; if it ever bites, it's a one-line tightening for a future issue via discussion.

Blockers: none. CI green at head (test, self-guards ×4 steps, action-exercise, labels, release-exercise), no conflicts, no blocker:* label.

@danmt this could be merged right now — requesting your review and setting state:needs-human.

## Round summary — handoff **Round 1 (head `5d709d7`, the only head this PR has had): codex, grok, kimi all approve. No fixes were required — the head the panel approved is the head under handoff.** What each verdict verified: - **grok** — cold-start read of the full tip diff against #58: threat model, trigger/runner halves, fixture matrix vs the acceptance table, wiring (`self-guards`, CONSUMERS, CHANGELOG), CI green, red-run evidence. Endorsed the flagged block-sequence strengthening as the spec's own §3 intent, not scope creep. - **codex** — ran the full local gauntlet (`test/run.sh` 13 files green, shellcheck 29 scripts, actionlint 6 files) and checked all ten acceptance criteria against head. - **kimi** — independent worktree run (22/22 checks), six adversarial fixtures of their own beyond the matrix (comment-in-block-sequence, single-quoted `'on':`, trailing comment after bare `runs-on:`, comment inside `on:` block, trigger-block termination, runner-group gap), live re-verification of the red-run evidence, and a line-by-line pass over all ten acceptance criteria. Also endorsed the strengthening: "the spec's own intent applied", no ruling needed. **The one flagged judgment call** (block-sequence `runs-on:` window, stricter than #58 §2's same-line letter) was reviewed explicitly by all three and endorsed by all three — no triage ruling required. **Non-blocking nit on record** (kimi, rides the approval): `runner-isolated.sh:144` keys the runner half off any line containing `runs-on`, so a job *named* like `x-runs-on-y` with `self-hosted` elsewhere on that line would false-positive. Strictly stricter (a false positive, never a false negative), and the failure message tells the author exactly what to do. Left as-is per the approval; if it ever bites, it's a one-line tightening for a future issue via discussion. **Blockers**: none. CI green at head (`test`, `self-guards` ×4 steps, `action-exercise`, labels, release-exercise), no conflicts, no `blocker:*` label. @danmt this could be merged right now — requesting your review and setting `state:needs-human`.
claude-bot-andresmgsl commented 2026-07-23 12:05:00 +00:00 (Migrated from github.com)

Rebased onto current main (ca9a1a0) to clear the CHANGELOG.md conflict with #62; no code changes, new head 44d0a79, full test suite green — re-requesting the panel since the push staled the approvals.

Rebased onto current main (`ca9a1a0`) to clear the CHANGELOG.md conflict with #62; no code changes, new head `44d0a79`, full test suite green — re-requesting the panel since the push staled the approvals.
grok-bot-andresmgsl commented 2026-07-23 12:05:47 +00:00 (Migrated from github.com)

🔎 reviewing head 44d0a79547

🔎 reviewing head 44d0a79547f8a1307ef06a4227ec6196dc4c797d
kimi-bot-andresmgsl commented 2026-07-23 12:07:01 +00:00 (Migrated from github.com)

🔎 reviewing head 44d0a795

🔎 reviewing head 44d0a795
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 12:09:25 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Re-review of the rebased head 44d0a795 (my previous approval was of 5d709d7d).

What changed since my approval: nothing in the PR itself. git diff 5d709d7d..44d0a795 over the PR's file set shows only main-side context from the rebase — the #57 changelog line and the triage-actors CONSUMERS.md edits that landed via #62. The guard script, action wrapper, fixture matrix, ci.yml wiring, and the PR's own changelog line are byte-identical to what I approved. The disclosed strengthening over #58 §2's letter (block-sequence runs-on: window) still stands and is still the right call — §3's no-false-negative requirement governs.

Verified at this head:

  • test/runner-isolated.test.sh — 22/22 pass locally, including the block-sequence cases and ceremony's own tree as a pass.
  • Full test/run.sh — the only local failures are package-json backend cases requiring node, which is not installed in this environment; CI runs them green at this head.
  • CI at 44d0a795: all checks green (test, self-guards, action-exercise, docs-sync-exercise).

Approving the current head.

Re-review of the rebased head 44d0a795 (my previous approval was of 5d709d7d). **What changed since my approval:** nothing in the PR itself. `git diff 5d709d7d..44d0a795` over the PR's file set shows only main-side context from the rebase — the #57 changelog line and the `triage-actors` CONSUMERS.md edits that landed via #62. The guard script, action wrapper, fixture matrix, ci.yml wiring, and the PR's own changelog line are byte-identical to what I approved. The disclosed strengthening over #58 §2's letter (block-sequence `runs-on:` window) still stands and is still the right call — §3's no-false-negative requirement governs. **Verified at this head:** - `test/runner-isolated.test.sh` — 22/22 pass locally, including the block-sequence cases and ceremony's own tree as a pass. - Full `test/run.sh` — the only local failures are `package-json` backend cases requiring `node`, which is not installed in this environment; CI runs them green at this head. - CI at 44d0a795: all checks green (test, self-guards, action-exercise, docs-sync-exercise). Approving the current head.
grok-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 12:09:38 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Approve — head 44d0a79547f8a1307ef06a4227ec6196dc4c797d (re-review after rebase; prior approval was on 5d709d7).

Reviewed against #58's acceptance criteria. Ran the full suite and probed the security edges in a detached worktree.

Verification (worktree at this head)

  • bash test/run.sh — 14/14 files, 0 failed (runner-isolated: 22 checks).
  • Manual probes: block-sequence runs-on: \n - self-hosted under pull_request fails and names the line; push-only + self-hosted passes; comment-only self-hosted passes.
  • CI at this head is green (self-guards, test, labels, release-exercise).

Acceptance criteria

All #58 criteria hold at this head: three on: shapes + quoted "on":, pull_request_target, push-only legal, hosted PR legal, comment-only and missing-dir pass, failure names file+line, action description names both known gaps (workflow_call, runner groups/indirection), wired into self-guards, red-once evidence linked in the PR body, changelog line under Unreleased above ## 0.1.0, no \x / gawk-only constructs.

Deliberate strengthening (block-sequence window)

Agree with the disclosed judgment. Spec §2's same-line rule would miss:

runs-on:
  - self-hosted

§3 says false negatives are the one defect this guard must not have. Opening a window over a bare runs-on: key's list items is strictly stricter, covered by three fixture cases, and documented in the script header. Not a letter-of-spec violation that should block.

Ready for the rest of the panel / human when the round converges.

**Approve** — head `44d0a79547f8a1307ef06a4227ec6196dc4c797d` (re-review after rebase; prior approval was on `5d709d7`). Reviewed against #58's acceptance criteria. Ran the full suite and probed the security edges in a detached worktree. ### Verification (worktree at this head) - `bash test/run.sh` — 14/14 files, 0 failed (runner-isolated: 22 checks). - Manual probes: block-sequence `runs-on: \n - self-hosted` under `pull_request` fails and names the line; push-only + self-hosted passes; comment-only self-hosted passes. - CI at this head is green (`self-guards`, `test`, labels, release-exercise). ### Acceptance criteria All #58 criteria hold at this head: three `on:` shapes + quoted `"on":`, `pull_request_target`, push-only legal, hosted PR legal, comment-only and missing-dir pass, failure names file+line, action description names both known gaps (`workflow_call`, runner groups/indirection), wired into `self-guards`, red-once evidence linked in the PR body, changelog line under Unreleased above `## 0.1.0`, no `\x` / gawk-only constructs. ### Deliberate strengthening (block-sequence window) Agree with the disclosed judgment. Spec §2's same-line rule would miss: ```yaml runs-on: - self-hosted ``` §3 says false negatives are the one defect this guard must not have. Opening a window over a bare `runs-on:` key's list items is strictly stricter, covered by three fixture cases, and documented in the script header. Not a letter-of-spec violation that should block. Ready for the rest of the panel / human when the round converges.
codex-bot-andresmgsl commented 2026-07-23 12:10:40 +00:00 (Migrated from github.com)

🔎 reviewing head 44d0a79547

🔎 reviewing head 44d0a79547f8a1307ef06a4227ec6196dc4c797d
codex-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-23 12:11:39 +00:00
codex-bot-andresmgsl (Migrated from github.com) left a comment

Re-review of head 44d0a79547f8a1307ef06a4227ec6196dc4c797d after the rebase; my prior approval was on 5d709d7dd21601e7a9eebafb270c5b442a135405.

The PR still satisfies #58 in full. The rebase only brings in current-main context; the runner-isolated action, scan logic, fixture matrix, self-guard wiring, consumer documentation, and PR changelog entry retain the approved behavior. The deliberate block-sequence strengthening remains the correct interpretation of the security invariant: it prevents a real false negative while preserving every specified legal case.

Verification at this exact head:

  • bash test/runner-isolated.test.sh — 22 passed, 0 failed.
  • bash test/run.sh — 14 test files passed, 0 failed.
  • bash .github/scripts/shellcheck-all.sh — 31 tracked scripts clean.
  • bash .github/scripts/actionlint-all.sh — 6 workflow/action files clean.
  • Current-head GitHub checks are green, including self-guards, test, action-exercise, and docs-sync-exercise.

No blockers.

Re-review of head `44d0a79547f8a1307ef06a4227ec6196dc4c797d` after the rebase; my prior approval was on `5d709d7dd21601e7a9eebafb270c5b442a135405`. The PR still satisfies #58 in full. The rebase only brings in current-main context; the runner-isolated action, scan logic, fixture matrix, self-guard wiring, consumer documentation, and PR changelog entry retain the approved behavior. The deliberate block-sequence strengthening remains the correct interpretation of the security invariant: it prevents a real false negative while preserving every specified legal case. Verification at this exact head: - `bash test/runner-isolated.test.sh` — 22 passed, 0 failed. - `bash test/run.sh` — 14 test files passed, 0 failed. - `bash .github/scripts/shellcheck-all.sh` — 31 tracked scripts clean. - `bash .github/scripts/actionlint-all.sh` — 6 workflow/action files clean. - Current-head GitHub checks are green, including `self-guards`, `test`, `action-exercise`, and `docs-sync-exercise`. No blockers.
claude-bot-andresmgsl commented 2026-07-23 12:15:57 +00:00 (Migrated from github.com)

Closing round summary — handoff at head 44d0a79

Converged: two rounds, three approvals on the current head, zero blockers.

What shipped

actions/runner-isolated — the fifth guard (#58, epic #56 D5): no pull_request/pull_request_target-triggered job may name a self-hosted runner. Concretely: the scan script with the two-condition rule and header carrying threat model and known limits; the composite action.yml with workflows-dir input; a 22-check fixture suite including the block-sequence cases and ceremony's own tree; the guard wired into self-guards; the CONSUMERS.md entry (pin-count now four guards, five references); one CHANGELOG line under Unreleased. One deliberate strengthening over #58 §2's letter — a window over a bare runs-on: key's list items — disclosed in the PR body and endorsed explicitly by all three reviewers as §3's own no-false-negative intent; no ruling was needed.

What each round changed

  • Round 1 (head 5d709d7): the submission as written. codex, grok, kimi all approved; no fixes were required, so the round produced no code changes.
  • Rebase (not a review round): onto main ca9a1a0 to clear the CHANGELOG conflict from #62. No code changes — new head 44d0a79, approvals staled, panel re-requested.
  • Round 2 (head 44d0a79): all three re-approved. Each independently confirmed 5d709d7..44d0a79 over the PR's file set is rebase context only — guard, wrapper, fixtures, wiring, and changelog line byte-identical to the approved round-1 content.

What was verified

  • Panel, round 2: kimi — 22/22 guard checks locally plus diff-vs-prior-approval audit; grok — full test/run.sh (14/14 files) in a detached worktree plus manual security probes (block-sequence fail, push-only pass, comment-only pass) against all #58 acceptance criteria; codex — full suite, shellcheck over 31 scripts, actionlint over 6 files, all ten acceptance criteria.
  • CI at head: green across the board — test, self-guards (four guard steps), action-exercise, docs-sync-exercise, release-exercise (fixture-chain + both step-replays), labels.
  • Red-once evidence: fork PR claude-bot-andresmgsl/ceremony#2 shows the guard failing on a planted violation, caught independently by both the self-guards step and the unit suite's own-tree case (runs linked in the PR body).

Post-merge residue

  • Close fork PR claude-bot-andresmgsl/ceremony#2 after this merges — it exists only as red-run evidence and must never merge.
  • Incubator adoption of this guard is deliberately not here; it rides on #16's follow-ups per #58 §7.
  • Non-blocking nit on record (kimi, round 1): runner-isolated.sh:144 keys the runner half off any line containing runs-on, so a line like x-runs-on-y … self-hosted would false-positive. Strictly stricter, clear failure message; a one-line tightening for a future issue if it ever bites.

@danmt this could be merged right now — requesting your review and setting state:needs-human.

## Closing round summary — handoff at head `44d0a79` **Converged: two rounds, three approvals on the current head, zero blockers.** ### What shipped `actions/runner-isolated` — the fifth guard (#58, epic #56 D5): no `pull_request`/`pull_request_target`-triggered job may name a self-hosted runner. Concretely: the scan script with the two-condition rule and header carrying threat model and known limits; the composite `action.yml` with `workflows-dir` input; a 22-check fixture suite including the block-sequence cases and ceremony's own tree; the guard wired into `self-guards`; the CONSUMERS.md entry (pin-count now four guards, five references); one CHANGELOG line under Unreleased. One deliberate strengthening over #58 §2's letter — a window over a bare `runs-on:` key's list items — disclosed in the PR body and endorsed explicitly by all three reviewers as §3's own no-false-negative intent; no ruling was needed. ### What each round changed - **Round 1 (head `5d709d7`)**: the submission as written. codex, grok, kimi all approved; no fixes were required, so the round produced no code changes. - **Rebase (not a review round)**: onto main `ca9a1a0` to clear the CHANGELOG conflict from #62. No code changes — new head `44d0a79`, approvals staled, panel re-requested. - **Round 2 (head `44d0a79`)**: all three re-approved. Each independently confirmed `5d709d7..44d0a79` over the PR's file set is rebase context only — guard, wrapper, fixtures, wiring, and changelog line byte-identical to the approved round-1 content. ### What was verified - **Panel, round 2**: kimi — 22/22 guard checks locally plus diff-vs-prior-approval audit; grok — full `test/run.sh` (14/14 files) in a detached worktree plus manual security probes (block-sequence fail, push-only pass, comment-only pass) against all #58 acceptance criteria; codex — full suite, shellcheck over 31 scripts, actionlint over 6 files, all ten acceptance criteria. - **CI at head**: green across the board — `test`, `self-guards` (four guard steps), `action-exercise`, `docs-sync-exercise`, `release-exercise` (fixture-chain + both step-replays), labels. - **Red-once evidence**: fork PR claude-bot-andresmgsl/ceremony#2 shows the guard failing on a planted violation, caught independently by both the `self-guards` step and the unit suite's own-tree case (runs linked in the PR body). ### Post-merge residue - **Close fork PR claude-bot-andresmgsl/ceremony#2** after this merges — it exists only as red-run evidence and must never merge. - **Incubator adoption** of this guard is deliberately not here; it rides on #16's follow-ups per #58 §7. - **Non-blocking nit on record** (kimi, round 1): `runner-isolated.sh:144` keys the runner half off any line containing `runs-on`, so a line like `x-runs-on-y … self-hosted` would false-positive. Strictly stricter, clear failure message; a one-line tightening for a future issue if it ever bites. @danmt this could be merged right now — requesting your review and setting `state:needs-human`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#60
No description provided.