Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/agentic-delivery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: >-
license: MIT
metadata:
author: deep-code-review contributors
version: "1.623.0"
version: "1.624.0"
---

# Agentic delivery
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/agentic-delivery/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.623.0
1.624.0
2 changes: 1 addition & 1 deletion .claude/skills/deep-code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: >-
license: MIT
metadata:
author: deep-code-review contributors
version: "1.623.0"
version: "1.624.0"
---

# Deep Code Review
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/deep-code-review/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.623.0
1.624.0
2 changes: 1 addition & 1 deletion .claude/skills/idea-critic/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/idea-critic/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.623.0
1.624.0
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<owner>/<repo>/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 `<FILL>` (also `<fill in>` and `<fill me>`), 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
Expand Down
14 changes: 7 additions & 7 deletions SHA256SUMS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading