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
30 changes: 30 additions & 0 deletions .gitapex/ssot.json
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,36 @@
{"kind": "workflow-event", "ref": "retrospective-gate-drift.yml:schedule"},
{"kind": "workflow-event", "ref": "retrospective-gate-drift.yml:workflow_dispatch"}
]
},
{
"id": "unguarded-shell-pipe-in-docs",
"kind": "script",
"script": ".github/scripts/gitapex_gate_unguarded_shell_pipe_in_docs.py",
"rule": "No skills/*/SKILL.md, skills/*/references/*.md, or checker/gate script's own module docstring may carry an unguarded `cmd1 | cmd2`-shaped shell pipe example (a real command token, a single `|`, and one of a fixed shell-consumer vocabulary such as python3/bash/uv/jq) with no nearby `pipefail` disclosure -- either `pipefail` mentioned inside the same fenced block (Markdown) or the same module docstring (Python), or an explicit `<!-- gitapex-allow-unguarded-shell-pipe: <reason> -->` marker directly above the fence (Markdown) or the flagged line (Python docstring). A single-backtick or double-backtick-quoted inline example in a docstring is never in scope. Exits 2, never a silent pass, when no in-scope file is discovered in either category or a file cannot be read/decoded as UTF-8 or parsed as Python.",
"planes": ["ci", "local"],
"local_invocation": ["uv", "run", "--frozen", "python3", ".github/scripts/gitapex_gate_unguarded_shell_pipe_in_docs.py"],
"trigger": ".github/workflows/unguarded-shell-pipe-in-docs-gate.yml on pull_request and workflow_dispatch, plus tests/test_gitapex_gate_unguarded_shell_pipe_in_docs.py and tests/test_gitapex_gate_unguarded_shell_pipe_in_docs_properties.py inside the pytest step of .github/workflows/test.yml",
"policy_refs": [],
"cluster": "repo-hygiene",
"tracking_issue": [1531, 1567],
"status": "active",
"supersedes": null,
"bypass_review_status": "not-yet-reviewed",
"preconditions": {
"requires_python_packages": ["pydantic"]
},
"target": [
{"kind": "file-glob", "ref": "skills/*/SKILL.md"},
{"kind": "file-glob", "ref": "skills/*/references/*.md"},
{"kind": "file-glob", "ref": ".github/scripts/*.py"},
{"kind": "file-glob", "ref": "skills/*/scripts/*.py"},
{"kind": "file-glob", "ref": "evals/scripts/*.py"},
{"kind": "file-glob", "ref": "hooks/*.py"},
{"kind": "workflow-event", "ref": "unguarded-shell-pipe-in-docs-gate.yml:pull_request"},
{"kind": "workflow-event", "ref": "unguarded-shell-pipe-in-docs-gate.yml:workflow_dispatch"},
{"kind": "workflow-event", "ref": "test.yml:pull_request"},
{"kind": "workflow-event", "ref": "test.yml:push"}
]
}
],
"clusters": {
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/gitapex_detect_changed_gate_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@

git diff --name-status BASE...HEAD | uv run --frozen python3 gitapex_detect_changed_gate_scripts.py

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s own
exit code separately, if the caller must detect an upstream failure rather
than silently scanning whatever partial `--name-status` output reached
stdin.

Reads `--name-status` lines on stdin, writes the comma-joined selection to
stdout (empty line when nothing matched) and diagnostics to stderr, so the
machine-read channel carries only the payload (dimension 14).
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/gitapex_detect_touched_eval_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@
python3 .github/scripts/gitapex_detect_touched_eval_skills.py \\
evals/foo/tasks/x.yaml evals/bar/eval.yaml

A bare pipe in the first form masks `git diff`'s own exit status in a
non-`pipefail` shell (issue #1531): add `set -o pipefail` first, or check
`git diff`'s own exit code separately, if the caller must detect an
upstream failure rather than silently classifying whatever partial path
list reached stdin.

`--nul`/`-0` (matching the `-0` convention of `xargs -0`/`grep -z`) reads
raw NUL-delimited bytes from `sys.stdin.buffer`, decoding each piece with
`os.fsdecode` (POSIX-safe, round-trips non-UTF-8-but-valid path bytes via
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_extract_diff_added_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@

git diff -U<large> "$BASE_SHA...$HEAD_SHA" -- <pathspec...> \\
| python3 .github/scripts/gitapex_extract_diff_added_lines.py > added_lines.txt

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s
own exit code separately, if the caller must detect an upstream failure
rather than silently extracting whatever partial diff reached stdin.
"""

from __future__ import annotations
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_acm_issue_disclosure.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
uv run --frozen python3 .github/scripts/gitapex_gate_acm_issue_disclosure.py --check-only --body <path>
printf '%s' "$ISSUE_BODY" | uv run --frozen python3 .github/scripts/gitapex_gate_acm_issue_disclosure.py --check-only

A bare pipe here masks `printf`'s own exit status in a non-`pipefail` shell
(issue #1531) -- harmless for a literal `printf` producer, which cannot
itself fail in ordinary use, but add `set -o pipefail` first if this
recipe's producer is ever swapped for a command that can.

Usage (full run: check, then label/comment as needed)::

uv run --frozen python3 .github/scripts/gitapex_gate_acm_issue_disclosure.py \\
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/gitapex_gate_design_doc_pattern_dryrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ class the module-level Residual risk paragraph above already commits
--diff-added added_lines.txt [--diff-added ...] [--body PR_BODY.txt] \\
[--repo-root .] [--corpus-glob 'skills/**/*.md']

A bare pipe in the first line masks `git diff`'s own exit status in a
non-`pipefail` shell (issue #1531): add `set -o pipefail` first, or check
`git diff`'s own exit code separately, if the caller must detect an
upstream failure rather than silently extracting whatever partial diff
reached stdin.

Exit codes:
0 No stated literal-text-search pattern found, every stated pattern
has at least one live corpus match, or a disclosure marker is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@
"$MERGE_BASE" "$HEAD_SHA" -- '*.py' \\
| uv run --frozen python3 .github/scripts/gitapex_gate_detection_logic_property_coverage.py

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s
own exit code separately, if the caller must detect an upstream failure
rather than silently grading whatever partial diff reached stdin.

Both flags are load-bearing for the same reason they are in
``gitapex_gate_exception_handler_gaps.py``: rename detection would hide a file
newly promoted into a graded directory behind a zero-added-line header, and
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_exception_handler_gaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ class this gate was measured against: a read from `sys.stdin`, a write-mode
"$MERGE_BASE" "$HEAD_SHA" -- '*.py' \\
| uv run --frozen python3 .github/scripts/gitapex_gate_exception_handler_gaps.py

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s
own exit code separately, if the caller must detect an upstream failure
rather than silently grading whatever partial diff reached stdin.

Both flags are load-bearing, not tidiness: rename detection hides a file
promoted into a graded directory behind a zero-added-line header, and
`core.quotePath` renders a non-ASCII path as an escaped string this gate
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_function_body_test_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@
"$MERGE_BASE" "$HEAD_SHA" -- '*.py' \\
| uv run --frozen python3 .github/scripts/gitapex_gate_function_body_test_coverage.py

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s
own exit code separately, if the caller must detect an upstream failure
rather than silently grading whatever partial diff reached stdin.

Both flags are load-bearing for the same reason they are in both sibling
gates: rename detection would hide a file newly promoted into a graded
directory behind a zero-added-line header, and ``core.quotePath`` renders a
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_independent_review_pending.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
--body PR_BODY.txt --head-sha <sha>
printf '%s' "$PR_BODY" | python3 .github/scripts/gitapex_gate_independent_review_pending.py --head-sha <sha>

A bare pipe here masks `printf`'s own exit status in a non-`pipefail` shell
(issue #1531) -- harmless for a literal `printf` producer, which cannot
itself fail in ordinary use, but add `set -o pipefail` first if this
recipe's producer is ever swapped for a command that can.

Exit codes:
0 A Verdict: CLEAN verdict naming the given head SHA is present.
1 No verdict section, an incomplete one, a non-CLEAN verdict, a
Expand Down
11 changes: 6 additions & 5 deletions .github/scripts/gitapex_gate_local_preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
(issue #890), which closes the "configured here but never actually
installed" half; nothing closes the ``--no-verify`` half. CI remains the
authoritative merge gate for every gate carrying a ``ci`` plane -- true
for 42 of the 44 wired gates. ``behind-base`` (issue #985) and
for 43 of the 45 wired gates. ``behind-base`` (issue #985) and
``real-checkout-git-write`` (issue #991) are the two exceptions: each
carries only ``local``, so for those two gates specifically this
pre-push hook -- bypassable the same way as any other -- is the *only*
Expand All @@ -119,7 +119,7 @@
``jsonschema`` -- a real, non-stdlib dependency, contrary to an earlier
revision of this paragraph's own "the runner itself needs no
dependencies" claim. A bare system ``python3`` with no ``jsonschema``
installed crashed the whole runner on import before any of the 44 wired
installed crashed the whole runner on import before any of the 45 wired
gates got a chance to run individually, so CONTRIBUTING.md's standalone
example and the pre-push hook's own ``entry`` both now invoke it as ``uv
run --frozen python3`` too, the same pin every wired gate's own argv
Expand Down Expand Up @@ -207,9 +207,10 @@
# own _GROUP_TIMEOUT_SECONDS = 600 -- so that one gate's own theoretical
# worst case is ~4200 s, not 600 s. A ceiling matching that would be useless
# as a hang guard (80 minutes of a silent pre-push), so this is a judgment
# call in the other direction. For scale: a warm run of all 44 wired gates
# combined measures roughly 15 s end to end (the
# prior 43-gate set measured roughly 15 s, the 42-gate set before that
# call in the other direction. For scale: a warm run of all 45 wired gates
# combined measures roughly 14 s end to end (the
# prior 44-gate set measured roughly 15 s, the 43-gate set before that
# measured roughly 15 s, the 42-gate set before that
# measured roughly 18 s, the 41-gate set before that
# measured roughly 18 s, the 40-gate set before that
# measured roughly 17 s, the 39-gate set before that
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_pr_title_convention.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
printf '%s' "$PR_TITLE" | uv run --frozen python3 \\
.github/scripts/gitapex_gate_pr_title_convention.py

A bare pipe here masks `printf`'s own exit status in a non-`pipefail` shell
(issue #1531) -- harmless for a literal `printf` producer, which cannot
itself fail in ordinary use, but add `set -o pipefail` first if this
recipe's producer is ever swapped for a command that can.

Exit codes: 0 the title matches; 1 it does not.
"""

Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_provenance_disclosure.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@
--body PR_BODY.txt --diff-added ADDED_LINES.txt [--diff-added ...]
printf '%s' "$PR_BODY" | python3 .github/scripts/gitapex_gate_provenance_disclosure.py

A bare pipe here masks `printf`'s own exit status in a non-`pipefail` shell
(issue #1531) -- harmless for a literal `printf` producer, which cannot
itself fail in ordinary use, but add `set -o pipefail` first if this
recipe's producer is ever swapped for a command that can.

Exit codes:
0 No offending paragraph in the combined corpus, or a disclosure
marker is present.
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/gitapex_gate_stdlib_only_claim_drift.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
git diff -U0 "$BASE_SHA...$HEAD_SHA" -- '.github/scripts/*.py' 'evals/scripts/*.py' \\
| uv run --frozen python3 .github/scripts/gitapex_gate_stdlib_only_claim_drift.py

A bare pipe here masks `git diff`'s own exit status in a non-`pipefail`
shell (issue #1531): add `set -o pipefail` first, or check `git diff`'s
own exit code separately, if the caller must detect an upstream failure
rather than silently grading whatever partial diff reached stdin.

Exit codes: 0 clean (including "no file in this diff gained a third-party
import" -- a legitimate pass, not an error), 1 stale claim(s) found, 2 the
scan could not be trusted (a malformed diff, an unreadable file, or a
Expand Down
Loading
Loading