Skip to content

chore: sync standards components - #3771

Merged
melodic-standards-sync[bot] merged 1 commit into
mainfrom
chore/standards-sync
Sep 5, 2026
Merged

chore: sync standards components#3771
melodic-standards-sync[bot] merged 1 commit into
mainfrom
chore/standards-sync

Conversation

@melodic-standards-sync

Copy link
Copy Markdown
Contributor

Summary

Automated exact-file synchronization from melodic-software/standards@d98e1f53a3376fe1c7415f861b9cfcd34d7b684b (requested ref main).

No linked issue — recurring automated distribution, not tied to a tracked change.

Fix

Managed component mappings applied to melodic-software/claude-code-plugins:

  • actionlint: .github/actionlint.yaml.github/actionlint.yaml (mode 100644)
  • editorconfig-checker: .editorconfig-checker.json.editorconfig-checker.json (mode 100644)
  • gitleaks: .gitleaks.toml.gitleaks.toml (mode 100644)
  • lychee: lychee.tomllychee.toml (mode 100644)
  • managed-files-guard-caller: components/managed-files-guard/managed-files-guard.yml.github/workflows/managed-files-guard.yml (mode 100644)
  • markdownlint: .markdownlint-cli2.jsonc.markdownlint-cli2.jsonc (mode 100644)
  • node-runtime: .node-version.node-version (mode 100644)
  • path-detection-guardrails: components/path-detection/machine-path-patterns.shplugins/guardrails/lib/path-detection/machine-path-patterns.sh (mode 100644)
  • pr-body-contract-rule: .claude/rules/pr-body-contract.md.claude/rules/pr-body-contract.md (mode 100644)
  • pyright: components/pyright/pyrightconfig.json.github/standards/pyright/pyrightconfig.json (mode 100644)
  • repository-text: .editorconfig.editorconfig (mode 100644)
  • repository-text: .gitattributes.gitattributes (mode 100644)
  • review-instructions: REVIEW.mdREVIEW.md (mode 100644)
  • runner-policy: components/runner-policy/package-lock.json.github/standards/runner-policy/package-lock.json (mode 100644)
  • runner-policy: components/runner-policy/package.json.github/standards/runner-policy/package.json (mode 100644)
  • runner-policy: components/runner-policy/policy.json.github/standards/runner-policy/policy.json (mode 100644)
  • runner-policy: components/runner-policy/policy.schema.json.github/standards/runner-policy/policy.schema.json (mode 100644)
  • runner-policy: components/runner-policy/repository-policy.schema.json.github/standards/runner-policy/repository-policy.schema.json (mode 100644)
  • runner-policy: components/runner-policy/runner-policy.mjs.github/standards/runner-policy/runner-policy.mjs (mode 100755)
  • shellcheck: .shellcheckrc.shellcheckrc (mode 100644)
  • typos: _typos.toml_typos.toml (mode 100644)

Verification

Review and merge this PR normally. Do not hand-edit these managed files downstream; change their standards source instead.

Related

@melodic-standards-sync melodic-standards-sync Bot added the standards-sync Standards-config sync PRs from the standards-sync workflow. label Sep 5, 2026
@melodic-standards-sync
melodic-standards-sync Bot enabled auto-merge (squash) September 5, 2026 19:07
@melodic-standards-sync
melodic-standards-sync Bot merged commit 73eb4d9 into main Sep 5, 2026
20 of 21 checks passed
@melodic-standards-sync
melodic-standards-sync Bot deleted the chore/standards-sync branch September 5, 2026 19:13
kyle-sexton pushed a commit that referenced this pull request Sep 5, 2026
…e sibling bumps

Main's fleet-wide prompt audit (#3770) rewrote descriptions and changelog text
across the plugins this branch also touches, and #3771/#3773 moved their
versions past the numbers taken here.

Every conflict was in a version line, a changelog heading, or prose the audit
rewrote. The manifests take main's descriptions whole, since this branch
changed only their version fields, and each version is renumbered strictly
above main's: claude-ops 0.42.10, mutation-testing 0.3.16, verification 0.6.5.
The changelogs keep both sides in order, with this branch's entry under its new
number.

The testing plugin's change is dropped rather than merged. Its one edit added a
presence gate and a code-metrics fallback to the marketplace-skill section in
`write/context/organize.md`, and the audit deleted that whole section as naming
plugins no installed marketplace carries. There is nothing left to gate, so
testing returns to main's content, version, and changelog exactly. The
cross-plugin pointer this branch adds now lives only in `verification:measure`
and `mutation-testing:principles`.

The `verification:measure` metrics bullet takes both sides: main's worktree-safe
`git show` sequence stays as the manual fallback, under this branch's
`/code-metrics:audit-<measure>` pointer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DVwWe4VVyx57dQ1AL7m35p
kyle-sexton pushed a commit that referenced this pull request Sep 7, 2026
…acement (#3871)

Closes #3509

## Summary

`pr-body-linkage-gate.sh` timed out on **every** recorded run in the
measurement window — 423 timeouts against a 15 s ceiling, the worst
blocked count in the #3508 campaign. A gate that is killed before it
renders a verdict protects nothing, so its cost is part of its contract.
Its MCP-surface sibling `pr-linkage-mcp-gate.sh` and the validator both
share is fixed here too.

**The parent's stated cause does not hold, and this PR does not act on
it.** #3508 attributes the cost to per-field `jq` forks needing a new
shared helper in `lib/hook-utils.sh`. Shard #3520 (PR #3779) established
the real mechanism, and merged PR #3788 fixed 34 hook scripts across 17
plugins while touching `lib/hook-utils.sh` **zero times**. This PR
follows that precedent: **`lib/hook-utils.sh` and every
`plugins/*/hooks/hook-utils.sh` copy are untouched** (unmerged PRs #3740
and #3838 own that file). The fix is entirely in-file.

The mechanism, re-verified on this host before any edit was made:

| Form | clone-family | `execve` |
| --- | --- | --- |
| `V=$(jq . f)` | 1 | 1 |
| `V=$(jq . f 2>/dev/null)` | **2** | 1 |
| `{ V=$(jq . f); } 2>/dev/null` | **1** | 1 |
| `V=$(printf \| jq \| tr)` | 4 | 2 |
| `V=$(cat -- f)` | 1 | 1 |
| `V=$(<f)` | **0** | **0** |
| `V=$(shellfunc)` | 1 | 0 |
| out-variable call | **0** | 0 |
| `read < <(printf …)` | 1 | 0 |

Bash elides the extra fork and execs in the command substitution's own
subshell only when the command carries no redirection of its own. A
`2>/dev/null`, a `<<<`, or a pipeline inside the substitution defeats
that.

## Fix

Four shapes, all in-file:

1. **Per-field `jq` batched into one process.** Both gates read payload
fields through `printf '%s' "$INPUT" | jq -r … 2>/dev/null | tr -d
'\r'`, once per field — 4 clones and 2 execs each, **five times over**
on the MCP surface, all asking about one buffered string. One
`hook::jq_fields` call (the library's existing batched reader, called
not changed) answers every field, and CR-strips exactly as the `tr` did.
2. **Redirection hoisted onto the enclosing group.** `ORIGIN=$(git …
2>/dev/null || true)` became `{ ORIGIN=$(git …) || ORIGIN=""; }
2>/dev/null`. The group holds exactly one command, so nothing beyond
that git call is silenced.
3. **The shared validator's helpers write into a caller-named
variable.** `strip_html_comments`, `mask_markdown_code`,
`section_content` and `trim` were each read through `$(…)` over a `<
<(printf …)` line reader: **12 forks and zero extra `execve` per judged
body** — pure process-creation latency. They now use `printf -v` and an
in-shell line split. `linkage::chomp_to` reproduces the trailing-newline
strip that command substitution performed, which is the one thing a
naive out-variable conversion gets wrong.
4. **`$(<file)` for the `--body-file` read, `printf -v '%q'` for wrapper
re-quoting, `hook::json_str_object_to` for the telemetry envelope** —
replacing a `cat`, a `printf` substitution, and a `jq -n` that bash can
do itself. The `$(dirname …)` source line was already fixed in both
gates by #3771.

`pr-body-linkage-gate.sh` and `pr-linkage-validator.sh` now invoke **no
external command of their own at all**; every remaining spawn on their
path belongs to `lib/hook-utils.sh`. `pr-linkage-mcp-gate.sh` keeps
three, each justified: the origin-remote scope guard, the `jq -e`
defer-guard on a repo's own `settings.json` (behind a `[[ -f ]]` probe),
and a carriage-return fallback described below.

### Where the MCP reader's behaviour could change, and what it does
about each

`hook::jq_fields` CR-strips every value it returns; the validator only
strips a CR at end of line. A body with a **mid-line** CR would
therefore be judged against different text — and stripping is the
**permissive** direction: `## Sum<CR>mary` becomes a section that was
previously missing, turning a BLOCK into a silent ALLOW. The batch
therefore also reports whether the raw body holds a CR at all, and the
MCP body is re-read losslessly with its own `jq` only in that case,
which no real payload hits.

Review of the first revision found two field shapes where that batched
reader itself flipped a per-field DENY to ALLOW; both are fixed in the
second commit (`fe3fd235`), and the differential below was re-run over
them:

- **The CR probe was not type-safe.** `contains("\r")` errors on a
non-string body, one erroring filter fails the whole batch, and a failed
batch exited 0 — so a body of `5`, `true`, `{"a":1}` or `["x"]`, which
the per-field reader rendered as text and blocked, was allowed. The
probe now goes through `tostring` first, so it is total over every JSON
type. A batch that **still** fails (a `tool_input` or payload root that
is not an object) falls back to the per-field reads it replaced instead
of allowing outright, so a batch failure is now exactly as fail-closed
as the per-field reader was: a determinable bad body still blocks. That
fallback is unreachable for any object `tool_input`, so the fenced spawn
counts do not move.
- **Trailing newlines survived on the exact-match fields.** `$( )`
chomped them from every per-field read, so `"owner": "acme-corp\n"` or a
tool name with a trailing newline matched the guards and was gated;
`hook::jq_fields` keeps the newline and both slipped past. `TOOL`,
`HOOK_CWD`, `T_OWNER`, `T_REPO` and `BODY` are now chomped in-shell,
byte-identical to their `$(jq -r …)` form whenever they carry no CR.

**One accepted stricter change remains.** A CR *inside* `tool_name`,
`owner` or `repo` used to stay in the value, so it never matched and the
call was allowed; `hook::jq_fields` strips it, the value matches, and
the body is judged. Neither GitHub nor the MCP server produces such a
value; the stricter direction is kept and named in the CHANGELOG rather
than asserted away.

## Verification

**Behaviour, proven by differential rather than argued.**

- **End-to-end, 154 payloads (85 Bash, 69 MCP), exit code, stdout and
stderr compared byte-for-byte** between the merge-base gates and these,
after `fe3fd235`. Bash surface: every body-flag spelling (`--body`,
`-b`, `--body=`, `--body-file`, `-F`, attached forms), stdin and
substitution heredocs, multiple-heredoc and unterminated cases, missing
body files, `--repo`/`-R` and `cd` escapes, `env -S` / `env` / `sudo`
wrappers, `gh.exe` and `./gh`, CRLF and mid-line-CR bodies, NUL bytes,
NBSP/BOM/ZWSP/U+2028/U+2029, `--fill`/`--web`, absent and non-string
`command`, trailing-newline and CR `cwd`. MCP surface: the tool x owner
x body matrix, absent/empty/null body, non-string bodies (`5`, `true`,
`{"a":1}`, `["x"]`, `false`), absent and non-object `tool_input`, NUL
and CR in `owner`, trailing newline and CR in
`owner`/`repo`/`tool_name`/`cwd`. **Bash gate: 85/85 verdict-identical.
MCP gate: 67/69 verdict-identical; the remaining 2 are the CR-in-`owner`
and CR-in-`tool_name` cases above, ALLOW to DENY.** Before `fe3fd235`
the same run showed 7 DENY-to-ALLOW mismatches (the four non-string
bodies, `owner\n`, `repo\n`, `tool_name\n`); all 7 are gone and no new
one appeared. Bash's own "ignored null byte" warning line, whose text
carries the script path, is excluded from the byte comparison. Denies:
69 at the merge base, 71 here.
- **Validator differential, 425 bodies**, hand cases plus a seeded fuzz
corpus over heading/fence/comment/CR/NBSP/backtick-run tokens. **0
mismatches.**
- **Trailing-newline invariance** proved separately, because
`hook::jq_fields` preserves trailing newlines where `$(printf | jq)`
stripped them: 24 validator cases and 9 hook cases, 0 mismatches; the
MCP gate's own fields are now chomped as well.
- Contract suites: `pr-body-linkage-gate.test.sh` **146/146**,
`pr-linkage-mcp-gate.test.sh` **37/37** (nine new cases: the four
non-string bodies block, `owner\n` / `repo\n` / `tool_name\n` still
gate, CR-in-owner gates as the accepted stricter case, a string
`tool_input` takes the fallback and allows).

**Cost, measured with `strace -f -e
trace=clone,clone3,fork,vfork,execve`** — not an xtrace command count,
which reads source positions rather than kernel spawns (#3520 measured
xtrace at 2 against 8 real spawns on one script). Telemetry sink off;
re-measured after `fe3fd235`, unchanged:

| Path | clones before | clones after | `execve` before | `execve` after
|
| --- | --- | --- | --- | --- |
| Bash gate, a `gh` call with no `pr` | 8 | **7** | 3 | **2** |
| Bash gate, `gh pr create` with a body (ALLOW) | 28 | **11** | 6 |
**3** |
| Bash gate, `gh pr create` with a body (BLOCK) | 28 | **11** | 6 |
**3** |
| MCP gate, unrelated tool | 7 | **7** | 2 | **2** |
| MCP gate, create (ALLOW) | 37 | **11** | 9 | **4** |
| MCP gate, create (BLOCK) | 37 | **11** | 9 | **4** |

Two components: the validator refactor alone removes **12 clones with
`execve` unchanged** — that half is pure latency, no work removed. The
rest is genuinely duplicated work removed: six `jq` processes re-parsing
one buffered payload, plus two `tr` calls deleting a byte class bash
rewrites in place.

**No wall-clock figure is claimed.** This is a Linux host where a spawn
costs ~3-5 ms; the campaign's host measures 0.3-0.9 s and is bimodal at
501 concurrent processes. A timing here would say nothing about there,
so the process count is reported as the proxy, per #3508's own
correction.

**New gate: `hooks/pr-linkage-spawn-budget.test.sh`.** Ceilings are the
measured counts with **no headroom**, per `hook-budget.md` rule 2. It
refuses to report a pass it has not earned:

- a self-check first proves the harness can distinguish `$(cmd
2>/dev/null)` from `{ …; } 2>/dev/null` at the kernel level, and
**SKIPs** rather than passing if it cannot (no ptrace, no strace);
- three mutants must each raise the count above the ceiling or **the
suite fails itself**: a redirect moved back inside a substitution (11 ->
12 clones), one field split back out of the batch (11 -> 14 clones, 4 ->
5 execve), a validator helper re-forking (11 -> 13 clones);
- it asserts both gates still exit 2 on a failing body, so a budget of
zero spawns cannot pass as a no-op.

Pointed at HEAD's hooks the new suite reports **13 failures**; against
this branch, 23/23 pass.

**Gates run** (re-run after `fe3fd235`). `scripts/affected-tests.sh
--run`: 151 shell suites pass, 14 selected suites belong to ecosystems
the runner does not execute (reported NOT RUN, not skipped), one
pre-existing failure noted below; `scripts/check-changelog-parity.sh` in
all four modes (`--check`, `--check-order`, `--check-bump origin/main`,
`--check-preserved origin/main`) all rc=0; `shellcheck -x` and `shfmt
-d` clean on all five scripts; `markdownlint-cli2` and
`editorconfig-checker` clean on the changed files;
`check-shell-portability.sh`, `check-purged-em-dashes.sh`,
`check-silent-skips.sh`, `check-discriminating-test-skips.sh`,
`check-killswitch-hoist.sh`, `check-hook-exec-form.sh`,
`check-fixture-git-isolation.sh` all rc=0 on the first revision.
Manifest bumped 0.55.58 -> **0.55.60** with the matching CHANGELOG entry
(see Related for why not 0.55.59), and the README carries the measured
share per `hook-budget.md` rule 1.

**Pre-existing failures, not from this branch** (both reproduce on a
clean `HEAD` checkout, and this branch touches no file either reads):
`plugins/claude-ops/skills/plugins/scripts/cache-content-check.test.sh`
(2 cases, its own xtrace-based budget probe) and
`plugins/session-flow/scripts/tests/test_save_point.py::test_new_origin_falls_back_to_directory_name`.

### Acceptance criteria: two are not fully met, stated plainly

| # | Criterion | Status |
| --- | --- | --- |
| 1 | No more than 2 external spawns on the common path (own shell + at
most one `jq`) | **NOT MET — over by one.** The common path is now the
hook's own shell, one `jq -e .`, and one batched `jq`. The batched `jq`
is the criterion's allowance; the `jq -e .` is `hook::buffer_stdin`'s
payload validation inside `lib/hook-utils.sh`, which this PR is fenced
off from. Removing it is that library's change to make, in #3740/#3838.
|
| 2 | `grep`/`sed`/`cut`/`tr`/`basename`/`dirname` on the hot path
replaced with builtins | **MET.** None appears in any of the three
scripts; `cat` is gone too. |
| 3 | Matcher or early guard exits before any spawn for non-matching
invocations | **PARTIALLY MET.** The `if: Bash(*gh *)` filter
(pre-existing) removes the hook process entirely for non-`gh` calls, and
the two in-hook guards short-circuit before any repo I/O. They cannot
run before `hook::buffer_stdin`, whose `jq` is the same library spawn as
row 1 — the hook must read stdin before it can know what it is looking
at. |
| 4 | Existing behavioural tests still pass; the guard still blocks what
it blocked before | **MET, with one named exception.** 183 contract
cases plus the 154-payload differential: every merge-base DENY is
reproduced, and two merge-base ALLOWs (a CR inside `owner` or
`tool_name`) are now DENY, the stricter direction, recorded in the
CHANGELOG. |
| 5 | Under 2 s for a single run on a Windows host | **NOT VERIFIED.**
No Windows host available; the process-count proxy above is offered
instead, and no timing figure is invented. |

## Related

- Parent: #3508 (Windows process-creation tax). Its stated cause —
per-field `jq` needing a new shared `hook-utils.sh` helper — is not what
this PR acts on; see Summary.
- Precedent: #3520 / PR #3779 (established redirection placement as the
real mechanism) and merged PR #3788 (34 scripts, 17 plugins,
`lib/hook-utils.sh` untouched).
- **Version and overlap with #3838.** `main` is at `source-control`
0.55.58. Open, ready PR #3838 (`cursor/shell-script-perf-phase1-bb5b`)
bumps this plugin to **0.55.59** with its own `## [0.55.59]` heading,
and edits **both gate files this PR touches**
(`pr-body-linkage-gate.sh`, `pr-linkage-mcp-gate.sh`, introducing
`hook::buffer_stdin_to`) plus the three worktree gates and the synced
`hook-utils.sh`. This PR therefore takes **0.55.60**, verified against
`main`, #3838, #3774 (0.55.58, no bump) and #3740 (tops at 0.55.54).
Whichever of #3838 and this PR lands second needs a **rebase of the two
gate files, not just a rebump**; that is the merge lane's call, flagged
here so it is not a surprise. Sibling #3510 is in flight against this
plugin and takes the next version after this one.
- Fenced off: unmerged PRs #3740 and #3838 own `lib/hook-utils.sh`;
#3510 owns `worktree-add-containment-gate.sh`,
`worktree-add-claim-gate.sh` and `worktree-create-gate.sh`, which share
this plugin's `CHANGELOG.md` and manifest. Neither set is touched here.
- Prior art acknowledged: #1403 / PR #1385, whose revival bar (four
contract-test regressions, two failing open) is what the deny-preserving
differential above is aimed at.
- Budget authority: `docs/conventions/hook-budget/README.md` (#1809),
surfaced by `.claude/rules/hook-budget.md`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01ViPsHkL3ng9xWt2GjEQJob

---
_Generated by [Claude
Code](https://claude.ai/code/session_01ViPsHkL3ng9xWt2GjEQJob)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

standards-sync Standards-config sync PRs from the standards-sync workflow.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants