From 7928b94f2453de455c6ed5eab68b12f358fb72ea Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:35:53 -0700 Subject: [PATCH 1/7] test(automerge): shared corpus + BB selftest for bb-automerge.py policy parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GH workflow regex (claude-author-automerge.yml) and the BB Python regex (bb-automerge.py) policed the same risk-tier policy in different languages. Wave 4.3 PR 133 incident showed the divergence: BB lacked several auth/billing/infra categories AND the bash wrapper bypassed policy entirely (fix in topcoder1/dotclaude@1a7ff39). Shared corpus (`selftest/risk_patterns_corpus.txt`) is now the single source of truth for RISKY/SAFE test cases. Format: RISKY: must match high-risk in BOTH BB and GH RISKY_BB: must match in BB only (e.g. bitbucket-pipelines.yml) SAFE: must NOT match in either Both selftests read from it, so any drift in either pattern set is caught: - `selftest/test_automerge_risk_patterns.sh` (existing, refactored from inline arrays) tests the GH regex; processes 120 cases. - `selftest/test_bb_automerge_risk_patterns.sh` (new) imports `find_high_risk` from bb-automerge.py via importlib and runs the corpus through it; processes 121 cases. Includes infra/crontabs/* SAFE entries (wxa_vpn#250 lesson) so over-broad infra/* patterns are caught. Companion to topcoder1/dotclaude@1a7ff39 — both repos must merge together (or the BB selftest will fail until dotclaude lands). Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 165 ++++++++++++++++++++ selftest/test_automerge_risk_patterns.sh | 115 ++------------ selftest/test_bb_automerge_risk_patterns.sh | 68 ++++++++ 3 files changed, 250 insertions(+), 98 deletions(-) create mode 100644 selftest/risk_patterns_corpus.txt create mode 100755 selftest/test_bb_automerge_risk_patterns.sh diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt new file mode 100644 index 0000000..923a9a7 --- /dev/null +++ b/selftest/risk_patterns_corpus.txt @@ -0,0 +1,165 @@ +# Shared corpus for automerge risk-tier selftests (BB + GH). +# One entry per line: : +# Types: +# RISKY — must match high-risk patterns in BOTH BB (bb-automerge.py) AND GH (claude-author-automerge.yml) +# RISKY_BB — must match in BB only (e.g. bitbucket-pipelines.yml — irrelevant to GH workflow) +# SAFE — must NOT match in either +# +# When you add a new pattern category to claude-author-automerge.yml OR +# bb-automerge.py's HIGH_RISK_PATTERNS, add a representative RISKY entry here. +# Both selftests (test_automerge_risk_patterns.sh + test_bb_automerge_risk_patterns.sh) +# read from this file, so drift between the two pattern sets is caught. + +# ── RISKY: auth — all variants the GH workflow detects ───────────────────── +RISKY: src/auth/login.py +RISKY: internal/auth/security.go +# oauth2 alternation — wxa-mcp-server#193/#197 gap (2026-05-24) +RISKY: internal/oauth2/server.go +RISKY: internal/oauth2/handler.go +RISKY: pkg/oauth2/token.go +# Auth-adjacent variants (sibling-gap audit, 2026-05-24). +# Pattern is path-segment-anchored, not filename-prefix. So +# `internal/signin/handler.go` matches (signin is a segment) but +# `controllers/sessions_controller.rb` does NOT (sessions is a +# filename prefix, not a segment). Per-repo Rails/Django/Express +# conventions belong in caller's risk-paths.yml. +RISKY: internal/signin/handler.go +RISKY: internal/signup/form.go +RISKY: src/logout/handler.go +# sessions (plural) — Go convention +RISKY: internal/sessions/store.go +RISKY: internal/jwt/sign.go +RISKY: services/mfa/verify.py +RISKY: app/totp/generate.go +RISKY: lib/webauthn/register.go +RISKY: internal/passkey/store.go + +# ── RISKY: Go entrypoints — wxa-mcp-server#193 gap ───────────────────────── +RISKY: main.go +RISKY: cmd/server/main.go +RISKY: cmd/wxa-mcp-server/main.go + +# ── RISKY: billing variants ──────────────────────────────────────────────── +RISKY: internal/subscription/manager.go +RISKY: app/subscriptions/cancel.rb +RISKY: src/checkout/flow.tsx +RISKY: api/checkout/session.go +RISKY: internal/refund/process.go +RISKY: services/refund/issuer.py +RISKY: api/refunds/handler.go +RISKY: internal/billing/invoice.go +RISKY: billing/invoices.py +RISKY: internal/payment/charge.go +RISKY: internal/pricing/calc.go + +# ── RISKY: database (migrations + .sql) ──────────────────────────────────── +RISKY: internal/db/migrations/00049_workflows.sql +RISKY: internal/db/migrations/00050_workflow_executions.sql +RISKY: migrations/031_cdn_operator.sql +RISKY: schema/users.sql +RISKY: src/db/schema.sql + +# ── RISKY: secrets / credentials / env ───────────────────────────────────── +RISKY: internal/secret/manager.go +RISKY: config/secret/keystore.go +RISKY: src/secrets/vault.go +RISKY: secrets/api-keys.json +RISKY: config/credentials.yml +RISKY: credentials.py +RISKY: .env +RISKY: .env.production +RISKY: src/keychain_helpers.py + +# ── RISKY: containers + compose ──────────────────────────────────────────── +RISKY: Dockerfile +RISKY: deploy/Dockerfile.api +RISKY: docker-compose.yml +RISKY: docker-compose.staging.yml + +# ── RISKY: CI/CD ─────────────────────────────────────────────────────────── +RISKY: .github/workflows/ci.yml +RISKY: .github/workflows/deploy.yml +RISKY: .github/workflows/lint.yml +RISKY: .github/risk-paths.yml +RISKY: .github/CODEOWNERS +# BB-only: GH workflow doesn't include bitbucket-pipelines pattern (irrelevant to GH repos) +RISKY_BB: bitbucket-pipelines.yml + +# ── RISKY: infra (IAM, IaC, systemd, nginx, deploy scripts) ──────────────── +RISKY: infra/iam/scanner-role.json +RISKY: infra/iam/wxa-vpn-api-policy.json +RISKY: infra/iam/admin-role.tf +RISKY: infra/terraform/main.tf +RISKY: infra/k8s/api-deployment.yaml +RISKY: infra/digitalocean/systemd/wxa.service +RISKY: infra/scanner-id/identity.json +RISKY: infra/nginx/honeypot.conf +RISKY: infra/nginx/site.conf +RISKY: infra/nginx-checkip-vhost.conf +RISKY: infra/wxa-vpn-api.service +RISKY: infra/wxa-workload.slice +RISKY: infra/wxa-gt-builder.timer +RISKY: infra/systemd/api.service +RISKY: infra/some.tf +RISKY: infra/deploy-netflow-cron.sh +RISKY: infra/setup-actions-runner.sh +RISKY: infra/deploy-systemd.sh +RISKY: terraform/main.tf +RISKY: terraform/prod/main.tf +RISKY: pulumi/index.ts +RISKY: k8s/deployment.yaml +RISKY: fly.toml +RISKY: deploy/prod.sh +RISKY: deploy/deploy.sh +RISKY: deploy.sh +RISKY: deploy-staging.yml +RISKY: scripts/deploy/run.sh + +# ── SAFE: must NOT match ─────────────────────────────────────────────────── +SAFE: src/wxa_vpn/api/routes.py +SAFE: src/components/Button.tsx +SAFE: src/pages/Dashboard.tsx +SAFE: src/utils/format.ts +SAFE: src/api/findingsSlice.ts +SAFE: docs/architecture.md +SAFE: docs/data-dictionary.md +SAFE: README.md +SAFE: tests/test_anything.py +SAFE: tests/unit/utils_test.go +SAFE: openapi/paths/findings.yaml +SAFE: internal/api/handlers/findings.go +SAFE: internal/workflows/engine.go +SAFE: package.json +SAFE: package-lock.json +SAFE: go.mod +SAFE: go.sum +SAFE: scripts/run_analysis.py +# Adjacent to main.go but a test file +SAFE: main_test.go +SAFE: internal/foo/main_test.go +# Doc file mentioning oauth2 — pattern needs trailing / or end +SAFE: internal/oauth2.md +# Starts with "main" but not the literal main.go +SAFE: cmd/server/mainview.go +SAFE: mainview.go +# oauth2 substring but not a path segment +SAFE: src/oauth2helper.go +SAFE: oauth2helper.go +# Substrings that look auth/billing-ish but aren't path segments +SAFE: internal/sessionsutil.go +SAFE: pkg/jwtutil.go +SAFE: lib/passkeystore.go +SAFE: internal/totps.go +SAFE: docs/checkout-flow.md +SAFE: lib/subscriber.go +SAFE: internal/secretly.go +SAFE: tests/test_authorization_logic.py +SAFE: docs/signin-flow.md +# Cron files SHOULD be safe — wxa_vpn#250 / wxa_vpn#439 lesson +SAFE: infra/crontabs/wxa-scanner.crontab +SAFE: infra/crontabs/wxa-scanner-active.crontab +SAFE: infra/crontabs/wxa-scanner-slow.crontab +SAFE: infra/crontabs/README.md +SAFE: infra/aws-scanner-setup.md +SAFE: infra/crontab.example +SAFE: infra/README.md diff --git a/selftest/test_automerge_risk_patterns.sh b/selftest/test_automerge_risk_patterns.sh index 00fb531..62769ad 100755 --- a/selftest/test_automerge_risk_patterns.sh +++ b/selftest/test_automerge_risk_patterns.sh @@ -50,104 +50,23 @@ matches() { return 1 } -# Cases the regex MUST flag as risky (manual click-merge). -RISKY=( - "src/auth/login.py" - "internal/auth/security.go" # auth segment, Go layout - "internal/oauth2/server.go" # oauth2 alternation — wxa-mcp-server#193/#197 gap (2026-05-24) - "internal/oauth2/handler.go" - "pkg/oauth2/token.go" - "main.go" # Go entrypoint at root — wxa-mcp-server#193 gap - "cmd/server/main.go" # Go entrypoint under cmd/ - "cmd/wxa-mcp-server/main.go" - # Auth-adjacent variants (sibling-gap audit, 2026-05-24). - # NOTE: Pattern is path-segment-anchored, not filename-prefix. So - # `internal/signin/handler.go` matches (signin is a segment) but - # `controllers/sessions_controller.rb` does NOT (sessions is a - # filename prefix, not a segment). Per-repo Rails/Django/Express - # conventions belong in caller's risk-paths.yml. - "internal/signin/handler.go" - "internal/signup/form.go" - "src/logout/handler.go" - "internal/sessions/store.go" # sessions (plural) — Go convention - "internal/jwt/sign.go" - "services/mfa/verify.py" - "app/totp/generate.go" - "lib/webauthn/register.go" - "internal/passkey/store.go" - # Billing-adjacent variants - "internal/subscription/manager.go" - "app/subscriptions/cancel.rb" - "api/checkout/session.go" - "services/refund/issuer.py" - "api/refunds/handler.go" - # Secret (singular) variants - "internal/secret/manager.go" - "config/secret/keystore.go" - "secrets/api-keys.json" - ".env.production" - "src/keychain_helpers.py" - "credentials.py" - "migrations/031_cdn_operator.sql" - "src/db/schema.sql" - "billing/invoices.py" - "Dockerfile" - "docker-compose.yml" - ".github/workflows/deploy.yml" - ".github/risk-paths.yml" - ".github/CODEOWNERS" - "infra/iam/scanner-role.json" # IAM policy - "infra/iam/wxa-vpn-api-policy.json" - "infra/terraform/main.tf" # IaC under infra/ - "infra/digitalocean/systemd/wxa.service" - "infra/scanner-id/identity.json" - "infra/nginx/honeypot.conf" - "infra/nginx-checkip-vhost.conf" # top-level nginx config - "infra/wxa-vpn-api.service" # systemd unit - "infra/wxa-workload.slice" - "infra/wxa-gt-builder.timer" - "infra/some.tf" - "infra/deploy-netflow-cron.sh" # shell script - "infra/setup-actions-runner.sh" - "infra/deploy-systemd.sh" - "terraform/main.tf" - "pulumi/index.ts" - "k8s/deployment.yaml" - "fly.toml" - "deploy/prod.sh" - "deploy.sh" - "deploy-staging.yml" -) - -# Cases the regex MUST allow through to auto-merge (the historical false positives). -SAFE=( - "src/wxa_vpn/api/routes.py" - "tests/test_anything.py" - "docs/data-dictionary.md" - "main_test.go" # adjacent to main.go but a test file - "internal/foo/main_test.go" - "internal/oauth2.md" # doc file mentioning oauth2 — pattern needs trailing / or end - "cmd/server/mainview.go" # starts with "main" but not the literal main.go - "src/oauth2helper.go" # oauth2 substring but not a path segment - # Substrings that look auth/billing-ish but aren't path segments — must NOT over-block - "internal/sessionsutil.go" # "sessionsutil" segment, not "sessions" - "pkg/jwtutil.go" # "jwtutil" not "jwt" - "lib/passkeystore.go" # "passkeystore" not "passkey" - "internal/totps.go" # "totps" — neither "totp/" nor "totp$" - "docs/checkout-flow.md" # "checkout-flow.md" not literal "checkout" - "lib/subscriber.go" # "subscriber" not "subscription" - "internal/secretly.go" # "secretly" not "secret" - "tests/test_authorization_logic.py" # "test_authorization_logic.py" — not literal "auth" - "docs/signin-flow.md" # doc with "signin-flow" substring - "scripts/run_analysis.py" - "infra/crontabs/wxa-scanner.crontab" # cron schedule — wxa_vpn#439 case - "infra/crontabs/wxa-scanner-active.crontab" - "infra/crontabs/README.md" - "infra/crontabs/wxa-scanner-slow.crontab" - "infra/aws-scanner-setup.md" # runbook docs - "infra/crontab.example" # example config - "infra/README.md" -) +# Test cases sourced from shared corpus at selftest/risk_patterns_corpus.txt. +# The corpus is single source of truth for BOTH the GH selftest (this file) +# AND the BB selftest (test_bb_automerge_risk_patterns.sh), so drift between +# claude-author-automerge.yml's regex and bb-automerge.py's HIGH_RISK_PATTERNS +# is caught here. +CORPUS="$(dirname "$0")/risk_patterns_corpus.txt" +[ ! -f "$CORPUS" ] && { echo "FAIL: corpus not found at $CORPUS"; exit 2; } +RISKY=() +SAFE=() +while IFS= read -r line; do + case "$line" in + RISKY_BB:*) ;; # BB-only entries skipped by GH selftest + RISKY:*) RISKY+=("${line#RISKY: }") ;; + SAFE:*) SAFE+=("${line#SAFE: }") ;; + "#"*|"") ;; + esac +done < "$CORPUS" failed=0 diff --git a/selftest/test_bb_automerge_risk_patterns.sh b/selftest/test_bb_automerge_risk_patterns.sh new file mode 100755 index 0000000..a6859f0 --- /dev/null +++ b/selftest/test_bb_automerge_risk_patterns.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Selftest for bb-automerge.py — exercises HIGH_RISK_PATTERNS against the +# shared corpus at selftest/risk_patterns_corpus.txt. ANY drift between +# bb-automerge.py and claude-author-automerge.yml is caught here OR by the +# parallel test_automerge_risk_patterns.sh; matching corpora means matching +# coverage. +# +# Run from the repo root: +# bash selftest/test_bb_automerge_risk_patterns.sh +# +# Resolution of bb-automerge.py: +# - Prefer $BB_AUTOMERGE_PY if set +# - Else $HOME/.claude/templates/ci-workflows/scripts/bb-automerge.py +# - Else fail +set -euo pipefail + +SCRIPT="${BB_AUTOMERGE_PY:-$HOME/.claude/templates/ci-workflows/scripts/bb-automerge.py}" +if [ ! -f "$SCRIPT" ]; then + echo "FAIL: bb-automerge.py not found at $SCRIPT" + echo "Hint: set BB_AUTOMERGE_PY env var to its location" + exit 2 +fi + +CORPUS="$(dirname "$0")/risk_patterns_corpus.txt" +[ ! -f "$CORPUS" ] && { echo "FAIL: corpus not found at $CORPUS"; exit 2; } + +# One-shot Python harness — load the script via importlib, exercise find_high_risk +HARNESS=$(cat <<'PYEOF' +import importlib.util, os, sys +spec = importlib.util.spec_from_file_location('bba', os.environ['SCRIPT']) +mod = importlib.util.module_from_spec(spec) +# Register in sys.modules BEFORE exec_module so @dataclass can resolve cls.__module__ +# (Python 3.14 stricter behavior — see CPython dataclasses.py line 814). +sys.modules['bba'] = mod +spec.loader.exec_module(mod) +fail = 0 +for line in open(os.environ['CORPUS']): + line = line.strip() + if not line or line.startswith('#'): + continue + if line.startswith('RISKY_BB:'): + path = line[len('RISKY_BB: '):] + hits = mod.find_high_risk([path]) + if not hits: + print(f"FAIL [risky_bb->safe]: '{path}' not classified as high-risk by BB patterns") + fail = 1 + elif line.startswith('RISKY:'): + path = line[len('RISKY: '):] + hits = mod.find_high_risk([path]) + if not hits: + print(f"FAIL [risky->safe]: '{path}' not classified as high-risk") + fail = 1 + elif line.startswith('SAFE:'): + path = line[len('SAFE: '):] + hits = mod.find_high_risk([path]) + if hits: + print(f"FAIL [safe->risky]: '{path}' incorrectly classified as high-risk") + fail = 1 +sys.exit(fail) +PYEOF +) +SCRIPT="$SCRIPT" CORPUS="$CORPUS" python3 -c "$HARNESS" +EXIT=$? +if [ $EXIT -eq 0 ]; then + COUNT=$(grep -cE '^(RISKY(_BB)?|SAFE):' "$CORPUS") + echo "PASS — all $COUNT cases" +fi +exit $EXIT From 12141cb984f419f22bafebe0a572aab3f6d13bf8 Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:39:37 -0700 Subject: [PATCH 2/7] test(automerge): pin intentional parity-not-coverage tradeoffs in corpus Add SAFE corpus entries for the 5 paths Codex CAP-6 round 1 flagged as regressions vs prior BB-only behavior: - src/auth.ts (filename-named, not path-segment) - api/login.go (filename-named) - billing.py (filename-named) - stripe/webhooks.ts (stripe segment dropped for GH parity) - src/stripe/client.go All 5 are INTENTIONALLY ungated per Wave 4.4 plan (goal is parity with GH workflow, which has the same segment-anchored behavior). Repos that need filename or Stripe coverage put the path in their `.github/risk-paths.yml` blocked list. Adding to corpus pins the behavior so future regex edits that "accidentally" start catching these paths get caught by the selftest (false-positive direction is just as important to gate). Total corpus now 126 cases. Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index 923a9a7..6f10154 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -145,6 +145,16 @@ SAFE: mainview.go # oauth2 substring but not a path segment SAFE: src/oauth2helper.go SAFE: oauth2helper.go +# Intentional parity-not-coverage tradeoffs (Wave 4.4 — match GH workflow): +# Filenames like `auth.ts`, `login.go`, `billing.py` are intentionally SAFE +# (NOT matched globally). Repos that need filename coverage put the path in +# their .github/risk-paths.yml blocked list. Stripe paths likewise — only +# repos using Stripe declare it as risky per-repo. +SAFE: src/auth.ts +SAFE: api/login.go +SAFE: billing.py +SAFE: stripe/webhooks.ts +SAFE: src/stripe/client.go # Substrings that look auth/billing-ish but aren't path segments SAFE: internal/sessionsutil.go SAFE: pkg/jwtutil.go From 6ad4bea1ce59e329d178f7ae49664a3627d8d54a Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:43:26 -0700 Subject: [PATCH 3/7] test(automerge): pin secret.py / secret_key.py SAFE (same class as auth.ts gap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex CAP-6 round 3 flagged `secret.py` / `config/secret_key.py` as regressions vs the prior `(^|/)(secret|...)` substring pattern. Same underlying class as round 1's `auth.ts` / `billing.py` finding — the new `(/|$)` segment anchor doesn't match leaf filenames. Per Wave 4.4 plan, this matches GH workflow behavior intentionally. Adding these specific paths to corpus as SAFE so the accepted gap is explicitly version-pinned (false-positive direction is gated too — if a future regex edit accidentally widens to match these, the selftest catches it). Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index 6f10154..958fcd4 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -146,13 +146,18 @@ SAFE: mainview.go SAFE: src/oauth2helper.go SAFE: oauth2helper.go # Intentional parity-not-coverage tradeoffs (Wave 4.4 — match GH workflow): -# Filenames like `auth.ts`, `login.go`, `billing.py` are intentionally SAFE -# (NOT matched globally). Repos that need filename coverage put the path in -# their .github/risk-paths.yml blocked list. Stripe paths likewise — only -# repos using Stripe declare it as risky per-repo. +# Filenames like `auth.ts`, `login.go`, `billing.py`, `secret.py`, +# `secret_key.py` are intentionally SAFE (NOT matched globally because the +# `(/|$)` segment anchor doesn't match leaf filenames). BB repos that need +# filename coverage either add an explicit HIGH_RISK_PATTERNS entry (and a +# RISKY: line here) or rely on Claude Review / Codex Review as the gate. +# Stripe paths likewise — only the BB-only HIGH_RISK_PATTERNS would have +# caught `stripe/webhooks.ts`; dropped for GH parity. SAFE: src/auth.ts SAFE: api/login.go SAFE: billing.py +SAFE: secret.py +SAFE: config/secret_key.py SAFE: stripe/webhooks.ts SAFE: src/stripe/client.go # Substrings that look auth/billing-ish but aren't path segments From 7d1054aeb986737ba888e4cc17d2c406e44cd7f5 Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:45:59 -0700 Subject: [PATCH 4/7] test(automerge): pin src/secrets.py + secret_keys.yml (round 4 same class) Codex CAP-6 round 4 flagged src/secrets.py + config/secret_keys.yml as regressions vs prior pattern. Same class as rounds 1-3 (leaf filename vs path-segment matching tradeoff). Adding to corpus pins the accepted gap; rounds 1-3 lessons apply identically. Total corpus: 130 cases. Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index 958fcd4..a71424c 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -158,6 +158,8 @@ SAFE: api/login.go SAFE: billing.py SAFE: secret.py SAFE: config/secret_key.py +SAFE: src/secrets.py +SAFE: config/secret_keys.yml SAFE: stripe/webhooks.ts SAFE: src/stripe/client.go # Substrings that look auth/billing-ish but aren't path segments From 1123d93f180d4385069affbd816b5218494ddca0 Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:47:34 -0700 Subject: [PATCH 5/7] =?UTF-8?q?test(automerge):=20pin=20round-5=20findings?= =?UTF-8?q?=20=E2=80=94=20secret.json=20+=20nested=20docker-compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex CAP-6 round 5 flagged: - config/secret.json + src/secrets.ts: same leaf-filename class as rounds 1/3/4 (parity tradeoff vs GH). - deploy/local/docker-compose.yml + services/api/docker-compose.yaml: nested compose files. GH workflow has the same `^docker-compose.*` start-anchor gap, so this is also a parity tradeoff. Both classes pinned as SAFE to make the accepted-gap explicit in version control. Repos that nest compose files for staging/local should add an explicit HIGH_RISK_PATTERNS entry + RISKY: corpus line. Total corpus: 134 cases. Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index a71424c..1c65ce7 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -160,6 +160,13 @@ SAFE: secret.py SAFE: config/secret_key.py SAFE: src/secrets.py SAFE: config/secret_keys.yml +SAFE: config/secret.json +SAFE: src/secrets.ts +# Nested docker-compose paths — intentional parity gap (GH workflow anchors +# at start-of-path too; only root docker-compose*.yaml is gated). Repos that +# nest compose files for staging/local should add an explicit RISKY entry. +SAFE: deploy/local/docker-compose.yml +SAFE: services/api/docker-compose.yaml SAFE: stripe/webhooks.ts SAFE: src/stripe/client.go # Substrings that look auth/billing-ish but aren't path segments From 05e1f880d3f29a825397674641d4e28ec6d8ce3a Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:48:16 -0700 Subject: [PATCH 6/7] test(automerge): correct round-5 nested-docker-compose findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy/local/docker-compose.yml is actually already gated (matches `^deploy/.*` infra rule in both BB and GH). Codex round 5's claim was a false positive on that specific path. services/api/docker-compose.yaml IS a real gap (no `services/` prefix rule in either side). Keep that as SAFE — pins the intentional parity tradeoff. Direct verification: $ python3 -c "import bba; print(bba.find_high_risk(['deploy/local/docker-compose.yml']))" ['deploy/local/docker-compose.yml'] # caught $ python3 -c "import bba; print(bba.find_high_risk(['services/api/docker-compose.yaml']))" [] # gap (intentional, parity with GH) Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index 1c65ce7..28d6a37 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -162,10 +162,12 @@ SAFE: src/secrets.py SAFE: config/secret_keys.yml SAFE: config/secret.json SAFE: src/secrets.ts -# Nested docker-compose paths — intentional parity gap (GH workflow anchors -# at start-of-path too; only root docker-compose*.yaml is gated). Repos that -# nest compose files for staging/local should add an explicit RISKY entry. -SAFE: deploy/local/docker-compose.yml +# Nested docker-compose paths — partial parity gap: +# - `deploy/local/docker-compose.yml` IS still gated (matches `^deploy/.*`). +# - `services/api/docker-compose.yaml` is NOT gated globally. Repos that +# nest compose files outside `deploy/`, `infra/`, etc. should add an +# explicit HIGH_RISK_PATTERNS entry + RISKY: line here. +RISKY: deploy/local/docker-compose.yml SAFE: services/api/docker-compose.yaml SAFE: stripe/webhooks.ts SAFE: src/stripe/client.go From 3ce80bbcbd909c795f3fcd70e50ed0811d142fed Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Mon, 25 May 2026 11:50:15 -0700 Subject: [PATCH 7/7] test(automerge): pin round-6 leaf-filename findings Codex CAP-6 round 6 (hard cap) flagged secrets.yaml + secret_manager.py as regressions. Same parity-tradeoff class as rounds 1/3/4/5. Pinned to corpus as SAFE. Codex rounds: 6 (hard cap; same-class findings recur but each new specific path is now corpus-pinned for explicit documentation). Total corpus: 136 cases. Co-Authored-By: Claude Opus 4.7 (1M context) --- selftest/risk_patterns_corpus.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selftest/risk_patterns_corpus.txt b/selftest/risk_patterns_corpus.txt index 28d6a37..b5e04a0 100644 --- a/selftest/risk_patterns_corpus.txt +++ b/selftest/risk_patterns_corpus.txt @@ -162,6 +162,8 @@ SAFE: src/secrets.py SAFE: config/secret_keys.yml SAFE: config/secret.json SAFE: src/secrets.ts +SAFE: secrets.yaml +SAFE: secret_manager.py # Nested docker-compose paths — partial parity gap: # - `deploy/local/docker-compose.yml` IS still gated (matches `^deploy/.*`). # - `services/api/docker-compose.yaml` is NOT gated globally. Repos that