Skip to content

Wave 11: guard registry, privacy guard on GitHub writes, new-row flags in review_checks - #2157

Merged
remigiusz-antczak merged 1 commit into
mainfrom
land/wave-11
Oct 30, 2026
Merged

remigiusz-antczak merged 1 commit into
mainfrom
land/wave-11

Conversation

@remigiusz-antczak

Copy link
Copy Markdown
Collaborator

Wave 11: three changes; the commit message has detail.

What changes

  1. Guard registry (refs Audit and retire stale guard rails and tests #1592): one row per shipped hook and gate. ci-gates.sh guard-registry fails closed on a missing or malformed row; read-only --report marks a guard with no dated fire log unknown.
  2. Privacy guard: a new PreToolUse hook denies a gh or API write whose title or body matches the repo banlist, or that names the session's repository in a write to Perun. Writes into a same-owner sibling repo are not scanned, so an audit PR can name its repo. Reasons give location and pattern number only; it fails open and has an opt-out.
  3. New-row flags: review_checks.sh diff mode marks each row "new": true|false, so a reviewer checks new rows first. No row is dropped.

Verification

  • Affected suites ran locally; the last run (9 suites the final commits touch) passed 259 tests. CI runs the full set.
  • Independent review blocked the first privacy-guard version (1 red, 2 yellows); all three are fixed. Re-run probes (37, fictional fixtures): 36 as expected, 1 safe-side deny of a write naming two repos.
  • Size ratchet ok, one documented raise.

🤖 Generated with Claude Code

…s in review_checks

Guard registry (refs #1592):
- Every guard rail Perun ships has one row in scripts/guard-registry.tsv: id,
  owner role (never a person), reason, review-by date and an optional fire
  log path. A guard is a hook a shipped settings template runs, a git hook
  install.sh writes, or a ci-gates.sh subcommand.
- ci-gates.sh guard-registry fails closed on a guard with no row, an empty
  reason, an owner that is not a lowercase role slug, a review-by that is not
  a real calendar day, a duplicate or over-wide row, a row for a guard that no
  longer ships, an empty scan, and a cmd_ function whose dispatcher line the
  scan cannot read. A past review-by never fails it.
- guard-registry --report [--days N] is read-only: rows past review-by, and
  logged guards with no dated fire line in the last N days. A guard with no
  fire log, or an empty, rotated or undated one, is "unknown", never "zero
  fires". The report never edits or deletes a row.
- The header-parity test pins each hook row's owner, reason and review date
  to its script's `# rail:` line. Evil-twin record: keeping both the headers
  and the TSV can drift; if they still disagree after about 30 days, retire
  one.

Privacy guard (new hook, agentic-delivery/privacy_guard.py):
- A PreToolUse(Bash) hook runs the repo's banlist (.banlist.txt, then the
  gitignored .banlist.local.txt at the git top-level; a linked worktree reads
  the main checkout's local file) over the title and body of every gh
  pr|issue|release create|edit|comment and every curl or gh api write to
  repos/<owner>/<repo>/pulls or /issues in the command. A hit exits 2 with a
  reason that names the title or the body line and the pattern's number,
  never the matched text. No repo, no banlist, no title or body, an
  unreadable body file (a readable title is still checked), bad input or
  any error allows; PERUN_PRIVACY_GUARD=off opts out.
- Scope: the banlist covers writes into the session's own repository,
  into protocol/perun and into any repository under another owner. A write
  whose explicit target (read from that write's own flags, never from its
  title, body or a later command) is a different repository under the
  session origin's owner is not scanned, so an audit PR may name the repo
  it is filed in. A write naming several repositories is skipped only when
  every one is a sibling. With no usable origin every write is scanned.
  Known residuals, documented in the module: a public sibling repository in
  the session's own organisation is not scanned, a write into another
  owner's private repository is still scanned (a private target cannot be
  told from a public one without a network call), and a target set only by
  GH_REPO is not seen.
- `-f`/`--raw-field` text is scanned as written even when it starts with
  `@`; only `-F`/`--field` reads `@FILE`, and a failed read scans the
  literal text.
- A body file the command writes itself (`> F`, `>> F`, `tee F`) is read from
  the heredoc that feeds that file, not from the stale or missing file.
- Own-repository rule: a write into Perun's public repository from another
  repository's session is denied when its title or body names the session's
  repository (owner/name, or the bare name when distinctive), because a
  project's banlist does not list its own name. The reason gives the location
  only.
- comms_guard.py extraction, shared by import: `-F body=@FILE` reads @file
  as one shell word, and `_flags` masks quoted strings and heredoc bodies so
  a flag-like word inside a title or body is not taken for a flag.
- Wired into the operating-layer template, perun_doctor HOOK_SCRIPTS, a
  guard-registry row, a `# rail:` header and a changelog fragment. Unscanned
  forms (stdin pipes, merge and review, --fill, text built from a shell
  variable) are listed in the module docstring.

Review (review_checks.sh):
- In diff mode every JSON row carries "new": true|false, true when the row's
  file and line sit on a line BASE...HEAD adds (rename-aware `git diff -U0`
  with pinned flags, so a reviewer's git config cannot change which lines
  count). Line 0 and file-level rows are false; --full and --history omit
  the key; no row is dropped. stderr gains "review_checks: N of M rows are on
  lines this diff adds".
- references/method-situational.md: verify new rows first, and report a
  pre-existing row in a touched file only when the diff makes it reachable or
  worse.

Index: the agentic-delivery script-lookup cap in
scripts/test_skill_index_split.py rises by 200 bytes for the new
privacy_guard row (core plus lazy index measured at 22303 bytes).

size-budget-raise: .claude/skills/deep-code-review/references/method-situational.md 44396→44702 one sentence on the new flag: verify new rows first, report a pre-existing row only when the diff makes it reachable or worse

Tests: 259/259 in the 9 suites affected by the last two commits, re-run
at the final head (audit-pr-body 46, comms-quality 51, conflict-free-prs
23, guard-registry 92, privacy-guard 40, skill-index-split 7). Earlier
assembly runs: the suites affected by the first commits were green; 47 of
the 48 suites affected by the review round were green and the 48th (the
index cap) is fixed here. The full set runs in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@remigiusz-antczak
remigiusz-antczak merged commit 11c6a74 into main Oct 30, 2026
10 checks passed
@remigiusz-antczak
remigiusz-antczak deleted the land/wave-11 branch October 30, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant