diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index a6156ff01..cbd7f1cbb 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "perun", "description": "Perun: a suite of skills for building with AI agents. Deep code review, token and cost tools (spend reports, auto-compact with handoff, model-tier guidance), gated agentic delivery (merge train, lanes, destructive-command guard, auto-update), an idea critic, and product, growth, positioning, business-ops and output-safety specialists.", "skills": "./.claude/skills", - "version": "1.623.0", + "version": "1.624.0", "author": { "name": "deep-code-review contributors" }, diff --git a/.claude/skills/agentic-delivery/SKILL.md b/.claude/skills/agentic-delivery/SKILL.md index 8e5ae9ad0..1ad6e9410 100644 --- a/.claude/skills/agentic-delivery/SKILL.md +++ b/.claude/skills/agentic-delivery/SKILL.md @@ -8,7 +8,7 @@ description: >- license: MIT metadata: author: deep-code-review contributors - version: "1.623.0" + version: "1.624.0" --- # Agentic delivery diff --git a/.claude/skills/agentic-delivery/VERSION b/.claude/skills/agentic-delivery/VERSION index c395004bc..544107d45 100644 --- a/.claude/skills/agentic-delivery/VERSION +++ b/.claude/skills/agentic-delivery/VERSION @@ -1 +1 @@ -1.623.0 +1.624.0 diff --git a/.claude/skills/deep-code-review/SKILL.md b/.claude/skills/deep-code-review/SKILL.md index 9f25b9a16..0809f8002 100644 --- a/.claude/skills/deep-code-review/SKILL.md +++ b/.claude/skills/deep-code-review/SKILL.md @@ -8,7 +8,7 @@ description: >- license: MIT metadata: author: deep-code-review contributors - version: "1.623.0" + version: "1.624.0" --- # Deep Code Review diff --git a/.claude/skills/deep-code-review/VERSION b/.claude/skills/deep-code-review/VERSION index c395004bc..544107d45 100644 --- a/.claude/skills/deep-code-review/VERSION +++ b/.claude/skills/deep-code-review/VERSION @@ -1 +1 @@ -1.623.0 +1.624.0 diff --git a/.claude/skills/idea-critic/SKILL.md b/.claude/skills/idea-critic/SKILL.md index 6e4d3e4d2..7f1c7c3ab 100644 --- a/.claude/skills/idea-critic/SKILL.md +++ b/.claude/skills/idea-critic/SKILL.md @@ -8,7 +8,7 @@ description: >- license: MIT metadata: author: deep-code-review contributors - version: "1.623.0" + version: "1.624.0" --- # Veles: the evil twin (idea-critic) diff --git a/.claude/skills/idea-critic/VERSION b/.claude/skills/idea-critic/VERSION index c395004bc..544107d45 100644 --- a/.claude/skills/idea-critic/VERSION +++ b/.claude/skills/idea-critic/VERSION @@ -1 +1 @@ -1.623.0 +1.624.0 diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 3a2d5da07..daec1b442 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "perun", - "version": "1.623.0", + "version": "1.624.0", "description": "Perun: a skill suite for AI-agent teams: deep code review, token and cost tools, gated delivery, an idea critic and product specialists.", "skills": "./.claude/skills", "interface": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a55fdeec..b370bd0f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,35 @@ All notable changes to this repository are documented here. Format loosely follows Keep a Changelog; versioning follows Semantic Versioning. +## [1.624.0] — 2026-10-30 + +### Added +- `scripts/app_pentest.sh --openapi SPEC URL`: optional schemathesis API fuzzing of the validated target. SPEC is a local file or an http(s) URL. A URL must pass the same allowlist, userinfo, port and resolution rules as the target (and, with `--active`, the listed-and-private rule); it is fetched once, without redirects or env proxies (10 s, 10 MB), and schemathesis gets the local copy. A spec that is unreadable, over 10 MB, or has an absolute or scheme-relative `$ref` or `externalValue` in its text is refused (an early text match, not the guard). The binary is `schemathesis` on PATH, else `~/.local/bin/schemathesis`; no other name is run. Passive runs send only GET and HEAD; `--active` has no method filter and keeps the loopback/private-only rule. Both modes are rate-limited (`--rate-limit 5/s -n 10 -w 1`), pinned to the target origin with `--origin`, and run with an empty pinned config file. The run has an egress block: every proxy variable is a closed loopback port and `no_proxy` is the target host alone, so a remote `$ref` or `externalValue` that gets past the text match (a JSON unicode escape, a YAML tag) cannot be fetched and the caller's own proxy cannot divert target traffic. Residual, documented: the same host on another port, its subdomains, and a fetcher that ignores proxy variables. The JSON gains `tools.schemathesis` (`skipped`, `ran` or `error`; `ran` needs a complete report that tested at least one operation) and `openapi` (null without the flag, else `spec`, `ips` and the report's `tested` and `errored` operation counts, so `errored` above 0 is partial coverage). Each failure is a `{"tool":"schemathesis",...,"verified":false}` lead: medium for `ST101`, `ST102`, `ST401` and a `server_unavailable` crash, low for every other code. `references/app-pentest.md` documents the flags, the safety rules and the severity map. Test: `scripts/test_app_pentest.sh` (a fake schemathesis, a toy spec server, and, when the real binary is installed, wire-level runs that prove the passive and active methods, the egress block against hidden remote references and a user proxy, and the spec read limits). +- size-budget-raise: .claude/skills/deep-code-review/references/app-pentest.md 3033→6344 the --openapi safety rules, the egress block, the schemathesis flags with their reasons, the status semantics and the severity map + +### Added +- `references/domain-k.md`: a workflow file is not CI coverage until it runs. Before claiming CI coverage or recommending a CI-only fix, a review confirms workflows fire (`gh api repos///actions/permissions --jq .enabled`, `gh run list -L 5`). If Actions is off or unknown it says so and aims the fix at the gate that does run, or flags re-enabling Actions as an owner decision. The red-flag line gains "a CI-only fix where Actions is off or no run exists". Eval: `ci-only-fix-where-actions-is-disabled-confirm-workflows-run`. +- size-budget-raise: .claude/skills/deep-code-review/references/domain-k.md 5188→5674 the liveness rule (one checklist bullet plus one red-flag clause) + +### Added +- Committed personal-data check. New built-in `committed_personal_data.py` (stdlib only) counts the distinct email-shaped strings in every tracked file, plain binary data (sqlite, pickles) included, and reports a file with 10 or more as one `email-addresses` row: path and count only, never an address, a domain or a line. gitleaks, trufflehog and semgrep look for secrets, so a committed export of real people's addresses passed all of them clean. Not counted: the reserved example and test names (example.com, .net and .org with their subdomains, `.test`, `.example`, `.invalid`, `.localhost`), noreply forms, the ssh account `git@` of git remotes (`git@host:org/repo.git`, `ssh://git@host/org/repo`) and image asset-scale names (`icon@2x.png`, `icon@1.5x.png`, `icon@2x-dark.png`, for png, jpg, jpeg, gif, webp, avif, svg, pdf, ico, bmp, tif, tiff, heic, heif and apng). `--include-reserved` counts them so tests can use fictional example.com fixtures. Flags: `--threshold N` and `--repo-threshold N` (each 1 to 100000), `--strict` (exit 1 when a file or the repository total is flagged; the default is advisory, exit 0), `--stdin` (scan the listed paths instead of every tracked file), `--max-bytes N` (1 to 1 GiB, default 32 MiB; a larger file is scanned over its first bytes and a not-checked row says so). It exits 2 when `git ls-files` fails or a flag is out of range, never a silent pass and never a traceback. +- Addresses spread thin are summed. A leak of one address per file never reaches the per-file threshold, so the distinct addresses of all the scanned files that stay below it are also counted together, and a total of 50 or more (`--repo-threshold`) gives one `email-addresses` row with the path `.` ("69 distinct email addresses across 75 file(s) ..."). A file already reported is left out of the total, an address repeated across files counts once, and in diff mode only the changed files are counted. `--threshold 1` lists the files behind the row. The default 50 is five times the file threshold. The audit behind this check found about 850 addresses; this repository holds 69 distinct ones in 75 files that each stay below 10 (60 of them under docs/bench), so its own `--full` run shows the row as a lead. A default of 100 would stay quiet here but would miss a spread of 50 to 99 addresses. +- Compressed and columnar files do not pass silently, for exactly these formats. A file that stays under the threshold gets a `not-checked` row (path and format, never a value) when it is parquet (`PAR1` at the start and the end, or `PARE` at both for an encrypted footer), an Arrow IPC file (`ARROW1` at both ends), ORC (`ORC` at the start and before the last byte), zip (also docx, xlsx, jar), gzip, zstd, xz, 7z, lz4 frame, bzip2, Avro, PDF, or a zlib stream (a valid header whose data decodes). Parquet, Arrow and ORC need their footer, so a text file that merely begins with "PAR1" or "ORC" gets no row, and neither does a file too short to hold both magics (a bare "PAR1") or text whose first two bytes pass the zlib header check by chance. Every other format (brotli, snappy, rar, Arrow IPC streams, raw deflate, ORC 0.11 files, which lack the end magic) is read as plain bytes and can pass clean, as can UTF-16 text and obfuscated forms (`[at]`, `%40`); names are not detected. A file at or above the threshold is reported as usual, as a lower bound. +- Wired into `review_checks.sh` as the built-in `committed-data` step, right after `invisible-chars`: it scans the changed files in diff mode and every tracked file under `--full`, skips the paths every step skips (node_modules, vendor, build dirs) plus third_party/, Pods/, bower_components/ and *.min.js, as `invisible-chars` does, and is listed under "analyzers ran". A missing script gives one not-checked row. Rows are not tagged `likely-fixture`, because a real address in a fixture is still a leak. The `method-situational.md` analyzer list names the step with its `.` and `not-checked` rows. +- Threshold 10, measured rather than guessed: 5 x 8 MiB of seeded random bytes held 2 email-shaped strings (0.05 per MB); across this repository's 93,711 tracked files at this commit the most distinct non-reserved addresses in one file is 6 (75 files hold any, none is a recognised container), so 10 flags nothing here while a threshold of 5 would flag 3 files (two bench outputs and this check's own unit test); a whole-tree run took 3 to 7 s. A data export holds dozens to thousands. Contributor lists (AUTHORS, .mailmap) reach 10 by design and show up as leads for the reviewer. `--threshold` tunes it. +- New tests `scripts/test_committed_personal_data.py` (31 unit tests, one skipped on macOS: they cover the container rows for every format above, real gzip, bzip2, xz, zlib and PDF data, footerless and overlapping magics and zlib lookalikes, the `git@` and asset-scale names, the repository total with its edges, the flag ranges and the regex memory bound on a hostile dotted run) and `scripts/test-review-checks-committed-data.sh` (12 checks, with vendor/ and node_modules/ files planted and left unflagged, and the repository-total row passing through the hub), registered in `scripts/ci-tests.d/committed-personal-data.sh`. +- size-budget-raise: .claude/skills/deep-code-review/references/method-situational.md 43864→44018 the analyzer list names the new committed-data step and its rows + +### Added +- `supply_chain_check.sh` scans a git DIR with OpenSSF Scorecard offline when `--scorecard owner/repo` is not given; before, that run printed `SKIPPED scorecard`. It exports the repo root's HEAD (`git archive HEAD`, tracked files only) and runs `scorecard --local` on the export, so an untracked `node_modules` cannot score Binary-Artifacts 0. The export is removed afterwards, also when the run is stopped by SIGTERM, SIGHUP or SIGINT. Only the checks that work offline run: Binary-Artifacts, Dangerous-Workflow, Dependency-Update-Tool, License, Packaging, Pinned-Dependencies, SAST, Security-Policy and Token-Permissions. Vulnerabilities is never included because it reported a false 10 ("0 existing vulnerabilities") while api.osv.dev was unreachable, and Fuzzing errors offline. Scores stay informational with no pass/fail threshold, as in the remote mode, and the `ok scorecard` line says the offline view is partial and not comparable with a public Scorecard score. A subdirectory DIR scores the whole repo root, and the same line names the root and says so. `git archive` runs with `GIT_NO_LAZY_FETCH=1`, so a partial clone that is missing blobs is SKIPPED with a reason naming the partial clone instead of fetching over the network. A non-git DIR or a repo with no commit is also SKIPPED with the reason (exit 3 unless `--allow-skip`), and `--scorecard owner/repo` keeps the remote scan. The `supply-chain-check.md` table row and the script header say so. Test: `scripts/test-supply-chain-check.sh`, with a fake scorecard that records its arguments and the files it can see, and can hang so a test can signal the script. +- size-budget-raise: .claude/skills/deep-code-review/references/supply-chain-check.md 3421→3646 the scorecard table row now states the offline git-DIR mode, its whole-repo scope and its partial view + +### Fixed +- `comms_lint.py` `audit-pr`: the budget (1200 words and 160 lines, a failure above 1.5 times either) now counts everything outside `## Changes by area`, so a body written to the template with many areas no longer fails for its area text (eight areas came to 2024 words and failed). Inside that section each `### ` area fails above 200 words and the whole body fails above 3000 words, with no slack. The per-area cap covers `### ` areas only: bullet areas, text before the first `### ` heading and a `#### ` section that follows no `### ` heading are bound by the 3000-word cap alone. The `words` and `lines` the lint reports, and its score line, are the counted ones. `artifact-templates.md` states the scope and both caps. Test: `scripts/test_audit_pr_budget.py`, plus the eval `audit-pr-multi-area-body-budget`. +- `comms_lint.py` placeholder rule: a run of capital X inside a single-backtick code span on one line (a `mktemp` template such as `drain.XXXXXX`) is no longer read as a placeholder; the same run in a double-backtick span or a span split across lines still is. TBD, TODO and a lone XXX in prose still fail, also on a line that carries a template, and so does `` (also `` and ``), which the rule did not catch before. Test: `scripts/test_comms_lint_placeholder_template.py`. +- `scripts/test_destructive_guard_w21.py`: its waits on the guard process (20 s for the wall-clock case, 10 s or 20 s elsewhere) are one `WAIT = 60` ceiling, so a loaded host (load average near 13) no longer fails the wall-clock case while a real hang still fails, at 60 s. Test: `scripts/test_destructive_guard_w21_wait.py`, which also pins the guard's own `TIMEOUT` at 20 s or less so raising its watchdog still fails a test. +- size-budget-raise: .claude/skills/communication-structure/references/artifact-templates.md 9234→9443 the audit-pr paragraph states the budget scope and the two area and body caps + ## [1.623.0] — 2026-10-30 ### Fixed diff --git a/SHA256SUMS b/SHA256SUMS index 36e9cfa71..32889c7e6 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -20,8 +20,8 @@ b1080da06ca20a2807ea7be3a25cb95b94c97a6c2a686401796ffc3adac3ee37 .claude/skills 9521b057ff3db92d1e60923ebd88170713914f7e235ceb45717eb39419daf811 .claude/skills/agentic-ceo/scripts/weekly_receipt.py bb2f134ab9b07e2cf15c4c8a0fad4708bc398144195178960d63b8ea6fd4a2d4 .claude/skills/agentic-delivery/INDEX-scripts.md 5d66d96d22947e0528fd3f337f789d3a8445dfeeb8240b41fe28303aa5a5c8ad .claude/skills/agentic-delivery/INDEX.md -fa239ae429df6aa551803584e15ddf20452870bffa0c0e4e2db732cf81e2ea57 .claude/skills/agentic-delivery/SKILL.md -11e3c4a20b52c9b6ff67b62f689ad64790a55e4bb9743260025179320c23aa9b .claude/skills/agentic-delivery/VERSION +c4fae7dfe8732e83dcdc296ce7a0fd80730ad9c3da97e1c73e7f1494906f15b2 .claude/skills/agentic-delivery/SKILL.md +a411de2d7f2f0fa07d90bb54cac85173edc1c6932ab84778e78c8b439a86a424 .claude/skills/agentic-delivery/VERSION 94144c101f82399eb6c18f688321a00e1c195dd0ab334f3a5daa2543b40ff9f5 .claude/skills/agentic-delivery/evals/evals.json fba7699a2d0836b51b7c838d019b1f0cd20570522a362e8a32b8dacfb911298e .claude/skills/agentic-delivery/evals/triggers.json 39140ac0b40907dc2279bb3234ce1d8ffb4db56e8658c7f89cc370f777df0592 .claude/skills/agentic-delivery/references/cost-quality-guardrails.md @@ -187,7 +187,7 @@ f1832c344f02562376e6e6254524d0051e61fd7843fc59b767b19b4fbe8271aa .claude/skills 29020bf7db659b0e2019587f6857d34794d05dc1f693f909543b20dd7f0873b3 .claude/skills/communication-structure/evals/triggers.json 07413f998402d3910cd71931ae1a1b0d635aa6b09952c3bc4d0ba7d04b6dd91a .claude/skills/communication-structure/references/artifact-templates.md b9e8c4a71aa0102503226c0180379bc3bc25f13485ac35232521da88b88fc843 .claude/skills/communication-structure/references/leadership-status-update.md -5bc888cdb3fa30e9492b5a4fd9b58f10a6861209d6ffc6b52d184fb21e50e486 .claude/skills/communication-structure/scripts/comms_lint.py +80e335297c74ca2e83c525b7b3fe9186fcf7b6aa40543c035448fe18cff53464 .claude/skills/communication-structure/scripts/comms_lint.py aee1b377e135c8dcdb808cb02b29d7bd8ca9ab3038023ee4afd616f5ffc74d6f .claude/skills/communication-structure/scripts/reader_check.py 4195df9b36e2a1f73fd6ffa397fca22e95e86465064110fdfaea65c19f130d9d .claude/skills/communication-structure/scripts/status_update_lint.py 2524bf62b627d362d888535ec9ef6527642cd7eeccbfd518454ed68f30d21344 .claude/skills/communication-structure/scripts/status_vocab_lint.py @@ -204,8 +204,8 @@ d98f3e4380da3496061ba63be7beebe75017ab4ee1734eda9a3e798e19007b0e .claude/skills 18f20db537ab784460ad33cf218f7a01ac0a172fc38413f14c0e19e1ce6821cc .claude/skills/contribution/scripts/share_learning.py 17958281dce6f131591dcdba808153213372b8ecdc1005c6ff6fdf6cead34da0 .claude/skills/deep-code-review/INDEX-scripts.md 9067b2ebf9b7fc44db387ec27b73a921f7ed3cc0cabf738b8f2442c4d5706f21 .claude/skills/deep-code-review/INDEX.md -f7b6bb4c7b22d75df66ac9c63285a148f790f4d28e28e5e762448a2dcaf84df4 .claude/skills/deep-code-review/SKILL.md -11e3c4a20b52c9b6ff67b62f689ad64790a55e4bb9743260025179320c23aa9b .claude/skills/deep-code-review/VERSION +d351b1527c4565ac9780d18ad2f5622b90b54a67a5e39f52a31be6b9073212ef .claude/skills/deep-code-review/SKILL.md +a411de2d7f2f0fa07d90bb54cac85173edc1c6932ab84778e78c8b439a86a424 .claude/skills/deep-code-review/VERSION b97d95bc93cc23a8bc689e2e23db261a138367bcbc69d2f8a0452d5c2c6dfb68 .claude/skills/deep-code-review/evals/evals.json 13dbb52e4bf61b1093302e094151def167371d26e7cf67a026f3beeb09d34378 .claude/skills/deep-code-review/evals/triggers.json b84e3a2b88dc189f041784e76b97e68ad879915814e00f4b37cd27513746d6f1 .claude/skills/deep-code-review/references/a11y-aria.md @@ -420,8 +420,8 @@ e951eaa63fd0d1870701d69c89c29ef4fe74dbad9b680ab6ef0a4e993fb8bc20 .claude/skills fd316c63bbf1cc0ea64322d7d923d66692eaf7b545c056367bfecca35146c862 .claude/skills/growth-analytics/references/full-map.md 2f1d7499e9ed7ae59f550b5c00043e465b2a4e885821ad153438e2e33fcccb45 .claude/skills/growth-analytics/scripts/readout_check.py 67e71ec40535ad0b3931c51f3decf323bce809ed4c9586c6d422b7ad5698129f .claude/skills/idea-critic/INDEX.md -10b8080a8dfa83836cbfa88255f17b37626df9942618717cc3f35641d3377dc7 .claude/skills/idea-critic/SKILL.md -11e3c4a20b52c9b6ff67b62f689ad64790a55e4bb9743260025179320c23aa9b .claude/skills/idea-critic/VERSION +aaf3489bff46ea8575763405ef94f7cfbfc80831800b51aa11b5040a3f6bffc1 .claude/skills/idea-critic/SKILL.md +a411de2d7f2f0fa07d90bb54cac85173edc1c6932ab84778e78c8b439a86a424 .claude/skills/idea-critic/VERSION 9f77d5d6d13c99fd16cd8c398795d8be226d74bae5a5bf65369ed94a59fa56b7 .claude/skills/idea-critic/evals/evals.json 0e1c0db300661a1ab1a427a7157c75e2ed67130ab24d5c6cb02ec3ff9e793f58 .claude/skills/idea-critic/evals/heldout.json d8098c5ced63e8b8608867dd144091049563a0553cf62d27ce05b9d52ac54266 .claude/skills/idea-critic/evals/triggers.json diff --git a/changelog.d/app-pentest-openapi.md b/changelog.d/app-pentest-openapi.md deleted file mode 100644 index 4ec1e61cb..000000000 --- a/changelog.d/app-pentest-openapi.md +++ /dev/null @@ -1,3 +0,0 @@ -### Added -- `scripts/app_pentest.sh --openapi SPEC URL`: optional schemathesis API fuzzing of the validated target. SPEC is a local file or an http(s) URL. A URL must pass the same allowlist, userinfo, port and resolution rules as the target (and, with `--active`, the listed-and-private rule); it is fetched once, without redirects or env proxies (10 s, 10 MB), and schemathesis gets the local copy. A spec that is unreadable, over 10 MB, or has an absolute or scheme-relative `$ref` or `externalValue` in its text is refused (an early text match, not the guard). The binary is `schemathesis` on PATH, else `~/.local/bin/schemathesis`; no other name is run. Passive runs send only GET and HEAD; `--active` has no method filter and keeps the loopback/private-only rule. Both modes are rate-limited (`--rate-limit 5/s -n 10 -w 1`), pinned to the target origin with `--origin`, and run with an empty pinned config file. The run has an egress block: every proxy variable is a closed loopback port and `no_proxy` is the target host alone, so a remote `$ref` or `externalValue` that gets past the text match (a JSON unicode escape, a YAML tag) cannot be fetched and the caller's own proxy cannot divert target traffic. Residual, documented: the same host on another port, its subdomains, and a fetcher that ignores proxy variables. The JSON gains `tools.schemathesis` (`skipped`, `ran` or `error`; `ran` needs a complete report that tested at least one operation) and `openapi` (null without the flag, else `spec`, `ips` and the report's `tested` and `errored` operation counts, so `errored` above 0 is partial coverage). Each failure is a `{"tool":"schemathesis",...,"verified":false}` lead: medium for `ST101`, `ST102`, `ST401` and a `server_unavailable` crash, low for every other code. `references/app-pentest.md` documents the flags, the safety rules and the severity map. Test: `scripts/test_app_pentest.sh` (a fake schemathesis, a toy spec server, and, when the real binary is installed, wire-level runs that prove the passive and active methods, the egress block against hidden remote references and a user proxy, and the spec read limits). -- size-budget-raise: .claude/skills/deep-code-review/references/app-pentest.md 3033→6344 the --openapi safety rules, the egress block, the schemathesis flags with their reasons, the status semantics and the severity map diff --git a/changelog.d/ci-liveness-before-ci-only-fix.md b/changelog.d/ci-liveness-before-ci-only-fix.md deleted file mode 100644 index 5004ddecb..000000000 --- a/changelog.d/ci-liveness-before-ci-only-fix.md +++ /dev/null @@ -1,3 +0,0 @@ -### Added -- `references/domain-k.md`: a workflow file is not CI coverage until it runs. Before claiming CI coverage or recommending a CI-only fix, a review confirms workflows fire (`gh api repos///actions/permissions --jq .enabled`, `gh run list -L 5`). If Actions is off or unknown it says so and aims the fix at the gate that does run, or flags re-enabling Actions as an owner decision. The red-flag line gains "a CI-only fix where Actions is off or no run exists". Eval: `ci-only-fix-where-actions-is-disabled-confirm-workflows-run`. -- size-budget-raise: .claude/skills/deep-code-review/references/domain-k.md 5188→5674 the liveness rule (one checklist bullet plus one red-flag clause) diff --git a/changelog.d/committed-personal-data-check.md b/changelog.d/committed-personal-data-check.md deleted file mode 100644 index d2ae5b98f..000000000 --- a/changelog.d/committed-personal-data-check.md +++ /dev/null @@ -1,8 +0,0 @@ -### Added -- Committed personal-data check. New built-in `committed_personal_data.py` (stdlib only) counts the distinct email-shaped strings in every tracked file, plain binary data (sqlite, pickles) included, and reports a file with 10 or more as one `email-addresses` row: path and count only, never an address, a domain or a line. gitleaks, trufflehog and semgrep look for secrets, so a committed export of real people's addresses passed all of them clean. Not counted: the reserved example and test names (example.com, .net and .org with their subdomains, `.test`, `.example`, `.invalid`, `.localhost`), noreply forms, the ssh account `git@` of git remotes (`git@host:org/repo.git`, `ssh://git@host/org/repo`) and image asset-scale names (`icon@2x.png`, `icon@1.5x.png`, `icon@2x-dark.png`, for png, jpg, jpeg, gif, webp, avif, svg, pdf, ico, bmp, tif, tiff, heic, heif and apng). `--include-reserved` counts them so tests can use fictional example.com fixtures. Flags: `--threshold N` and `--repo-threshold N` (each 1 to 100000), `--strict` (exit 1 when a file or the repository total is flagged; the default is advisory, exit 0), `--stdin` (scan the listed paths instead of every tracked file), `--max-bytes N` (1 to 1 GiB, default 32 MiB; a larger file is scanned over its first bytes and a not-checked row says so). It exits 2 when `git ls-files` fails or a flag is out of range, never a silent pass and never a traceback. -- Addresses spread thin are summed. A leak of one address per file never reaches the per-file threshold, so the distinct addresses of all the scanned files that stay below it are also counted together, and a total of 50 or more (`--repo-threshold`) gives one `email-addresses` row with the path `.` ("69 distinct email addresses across 75 file(s) ..."). A file already reported is left out of the total, an address repeated across files counts once, and in diff mode only the changed files are counted. `--threshold 1` lists the files behind the row. The default 50 is five times the file threshold. The audit behind this check found about 850 addresses; this repository holds 69 distinct ones in 75 files that each stay below 10 (60 of them under docs/bench), so its own `--full` run shows the row as a lead. A default of 100 would stay quiet here but would miss a spread of 50 to 99 addresses. -- Compressed and columnar files do not pass silently, for exactly these formats. A file that stays under the threshold gets a `not-checked` row (path and format, never a value) when it is parquet (`PAR1` at the start and the end, or `PARE` at both for an encrypted footer), an Arrow IPC file (`ARROW1` at both ends), ORC (`ORC` at the start and before the last byte), zip (also docx, xlsx, jar), gzip, zstd, xz, 7z, lz4 frame, bzip2, Avro, PDF, or a zlib stream (a valid header whose data decodes). Parquet, Arrow and ORC need their footer, so a text file that merely begins with "PAR1" or "ORC" gets no row, and neither does a file too short to hold both magics (a bare "PAR1") or text whose first two bytes pass the zlib header check by chance. Every other format (brotli, snappy, rar, Arrow IPC streams, raw deflate, ORC 0.11 files, which lack the end magic) is read as plain bytes and can pass clean, as can UTF-16 text and obfuscated forms (`[at]`, `%40`); names are not detected. A file at or above the threshold is reported as usual, as a lower bound. -- Wired into `review_checks.sh` as the built-in `committed-data` step, right after `invisible-chars`: it scans the changed files in diff mode and every tracked file under `--full`, skips the paths every step skips (node_modules, vendor, build dirs) plus third_party/, Pods/, bower_components/ and *.min.js, as `invisible-chars` does, and is listed under "analyzers ran". A missing script gives one not-checked row. Rows are not tagged `likely-fixture`, because a real address in a fixture is still a leak. The `method-situational.md` analyzer list names the step with its `.` and `not-checked` rows. -- Threshold 10, measured rather than guessed: 5 x 8 MiB of seeded random bytes held 2 email-shaped strings (0.05 per MB); across this repository's 93,711 tracked files at this commit the most distinct non-reserved addresses in one file is 6 (75 files hold any, none is a recognised container), so 10 flags nothing here while a threshold of 5 would flag 3 files (two bench outputs and this check's own unit test); a whole-tree run took 3 to 7 s. A data export holds dozens to thousands. Contributor lists (AUTHORS, .mailmap) reach 10 by design and show up as leads for the reviewer. `--threshold` tunes it. -- New tests `scripts/test_committed_personal_data.py` (31 unit tests, one skipped on macOS: they cover the container rows for every format above, real gzip, bzip2, xz, zlib and PDF data, footerless and overlapping magics and zlib lookalikes, the `git@` and asset-scale names, the repository total with its edges, the flag ranges and the regex memory bound on a hostile dotted run) and `scripts/test-review-checks-committed-data.sh` (12 checks, with vendor/ and node_modules/ files planted and left unflagged, and the repository-total row passing through the hub), registered in `scripts/ci-tests.d/committed-personal-data.sh`. -- size-budget-raise: .claude/skills/deep-code-review/references/method-situational.md 43864→44018 the analyzer list names the new committed-data step and its rows diff --git a/changelog.d/supply-chain-scorecard-local.md b/changelog.d/supply-chain-scorecard-local.md deleted file mode 100644 index 419ebd471..000000000 --- a/changelog.d/supply-chain-scorecard-local.md +++ /dev/null @@ -1,3 +0,0 @@ -### Added -- `supply_chain_check.sh` scans a git DIR with OpenSSF Scorecard offline when `--scorecard owner/repo` is not given; before, that run printed `SKIPPED scorecard`. It exports the repo root's HEAD (`git archive HEAD`, tracked files only) and runs `scorecard --local` on the export, so an untracked `node_modules` cannot score Binary-Artifacts 0. The export is removed afterwards, also when the run is stopped by SIGTERM, SIGHUP or SIGINT. Only the checks that work offline run: Binary-Artifacts, Dangerous-Workflow, Dependency-Update-Tool, License, Packaging, Pinned-Dependencies, SAST, Security-Policy and Token-Permissions. Vulnerabilities is never included because it reported a false 10 ("0 existing vulnerabilities") while api.osv.dev was unreachable, and Fuzzing errors offline. Scores stay informational with no pass/fail threshold, as in the remote mode, and the `ok scorecard` line says the offline view is partial and not comparable with a public Scorecard score. A subdirectory DIR scores the whole repo root, and the same line names the root and says so. `git archive` runs with `GIT_NO_LAZY_FETCH=1`, so a partial clone that is missing blobs is SKIPPED with a reason naming the partial clone instead of fetching over the network. A non-git DIR or a repo with no commit is also SKIPPED with the reason (exit 3 unless `--allow-skip`), and `--scorecard owner/repo` keeps the remote scan. The `supply-chain-check.md` table row and the script header say so. Test: `scripts/test-supply-chain-check.sh`, with a fake scorecard that records its arguments and the files it can see, and can hang so a test can signal the script. -- size-budget-raise: .claude/skills/deep-code-review/references/supply-chain-check.md 3421→3646 the scorecard table row now states the offline git-DIR mode, its whole-repo scope and its partial view diff --git a/changelog.d/w7a-comms-lint-guard-wait.md b/changelog.d/w7a-comms-lint-guard-wait.md deleted file mode 100644 index d4074abe8..000000000 --- a/changelog.d/w7a-comms-lint-guard-wait.md +++ /dev/null @@ -1,5 +0,0 @@ -### Fixed -- `comms_lint.py` `audit-pr`: the budget (1200 words and 160 lines, a failure above 1.5 times either) now counts everything outside `## Changes by area`, so a body written to the template with many areas no longer fails for its area text (eight areas came to 2024 words and failed). Inside that section each `### ` area fails above 200 words and the whole body fails above 3000 words, with no slack. The per-area cap covers `### ` areas only: bullet areas, text before the first `### ` heading and a `#### ` section that follows no `### ` heading are bound by the 3000-word cap alone. The `words` and `lines` the lint reports, and its score line, are the counted ones. `artifact-templates.md` states the scope and both caps. Test: `scripts/test_audit_pr_budget.py`, plus the eval `audit-pr-multi-area-body-budget`. -- `comms_lint.py` placeholder rule: a run of capital X inside a single-backtick code span on one line (a `mktemp` template such as `drain.XXXXXX`) is no longer read as a placeholder; the same run in a double-backtick span or a span split across lines still is. TBD, TODO and a lone XXX in prose still fail, also on a line that carries a template, and so does `` (also `` and ``), which the rule did not catch before. Test: `scripts/test_comms_lint_placeholder_template.py`. -- `scripts/test_destructive_guard_w21.py`: its waits on the guard process (20 s for the wall-clock case, 10 s or 20 s elsewhere) are one `WAIT = 60` ceiling, so a loaded host (load average near 13) no longer fails the wall-clock case while a real hang still fails, at 60 s. Test: `scripts/test_destructive_guard_w21_wait.py`, which also pins the guard's own `TIMEOUT` at 20 s or less so raising its watchdog still fails a test. -- size-budget-raise: .claude/skills/communication-structure/references/artifact-templates.md 9234→9443 the audit-pr paragraph states the budget scope and the two area and body caps diff --git a/gemini-extension.json b/gemini-extension.json index 1c8b68228..60789b5f7 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,6 +1,6 @@ { "name": "perun", - "version": "1.623.0", + "version": "1.624.0", "description": "Perun: a skill suite for AI-agent teams: deep code review, token and cost tools, gated delivery, an idea critic and product specialists.", "contextFileName": "AGENTS.md" }