@soleur/opencode 1.0.0 → 3.354.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/commands/go.md +6 -5
  2. package/commands/sync.md +2 -2
  3. package/install.js +6 -85
  4. package/lib/harness.ts +1 -1
  5. package/lib/heartbeat-manifest.ts +19 -0
  6. package/lib/pr-merge-poll.ts +2 -2
  7. package/lib/workflow-fidelity.ts +2 -2
  8. package/opencode/INSTRUCTIONS.md +19 -0
  9. package/opencode/plugin.ts +343 -18
  10. package/package.json +1 -1
  11. package/scripts/admin-merge-ready.sh +1 -1
  12. package/scripts/admin-merge-ready.test.sh +0 -0
  13. package/scripts/affected-tests.sh +0 -0
  14. package/scripts/alpha-metrics.sh +0 -0
  15. package/scripts/alpha-metrics.test.sh +0 -0
  16. package/scripts/check-red-on-main.sh +0 -0
  17. package/scripts/ci-head-verdict.sh +54 -6
  18. package/scripts/cloud-detect.sh +0 -0
  19. package/scripts/delegation-verify.sh +0 -0
  20. package/scripts/delegation-verify.test.sh +0 -0
  21. package/scripts/deploy-arm.sh +0 -0
  22. package/scripts/domain-model-drift.sh +0 -0
  23. package/scripts/emit-decision.sh +0 -0
  24. package/scripts/emit-decision.test.sh +0 -0
  25. package/scripts/ext-filter-divergence.sh +0 -0
  26. package/scripts/grok-fidelity-gate.sh +0 -0
  27. package/scripts/grok-pre-push-gate.sh +0 -0
  28. package/scripts/install-opencode-adapter.ts +118 -129
  29. package/scripts/lib/affected-tests/common.sh +0 -0
  30. package/scripts/lib/affected-tests/go.sh +0 -0
  31. package/scripts/lib/affected-tests/js.sh +0 -0
  32. package/scripts/lib/affected-tests/nx-turbo.sh +0 -0
  33. package/scripts/lib/affected-tests/python.sh +0 -0
  34. package/scripts/lib/session-state.sh +0 -0
  35. package/scripts/lib/tmp-classify.sh +0 -0
  36. package/scripts/monitor-pr-checks.sh +1 -1
  37. package/scripts/pipeline-tally.sh +0 -0
  38. package/scripts/plugin-version.sh +0 -0
  39. package/scripts/precommit-guard.sh +3 -1
  40. package/scripts/precommit-guard.test.sh +0 -0
  41. package/scripts/render-c4-model.sh +0 -0
  42. package/scripts/resolve-regenerable-conflicts.sh +0 -0
  43. package/scripts/run-in-pid-namespace.sh +0 -0
  44. package/scripts/setup-ralph-loop.sh +0 -0
  45. package/scripts/sync-npm-package.ts +74 -87
  46. package/scripts/sync-opencode-adapter.ts +19 -0
  47. package/scripts/sync-pr-behind.sh +183 -70
  48. package/scripts/taste-profile-update.sh +0 -0
  49. package/scripts/taste-profile-update.test.sh +0 -0
  50. package/scripts/write-budget-marker.sh +0 -0
  51. package/skills/deepen-plan/SKILL.md +1 -1
  52. package/skills/deploy/references/hetzner-setup.md +5 -3
  53. package/skills/drain-prs/SKILL.md +3 -2
  54. package/skills/fix-issue/SKILL.md +8 -8
  55. package/skills/fix-issue/references/agent-authored-exclusion.md +4 -2
  56. package/skills/fix-issue/references/exclude-label-jq-snippet.md +4 -3
  57. package/skills/merge-pr/SKILL.md +5 -3
  58. package/skills/model-launch-review/SKILL.md +4 -4
  59. package/skills/one-shot/SKILL.md +1 -1
  60. package/skills/plan/SKILL.md +1 -1
  61. package/skills/plan/references/plan-sharp-edges.md +7 -1
  62. package/skills/postmerge/SKILL.md +2 -2
  63. package/skills/postmerge/references/deploy-status-debugging.md +3 -0
  64. package/skills/review/SKILL.md +14 -15
  65. package/skills/review/references/review-evidence-comment.md +36 -0
  66. package/skills/review/references/risk-tier-and-fix-rounds.md +2 -0
  67. package/skills/ship/SKILL.md +14 -13
  68. package/skills/ship/references/cleanup-merged-archival.md +39 -0
  69. package/skills/ship/references/delegated-act-authoring-form.md +7 -0
  70. package/skills/ship/references/epic-close-sweep.md +62 -0
  71. package/skills/ship/references/merge-queue-dequeue.md +1 -1
  72. package/skills/ship/references/operator-holds.md +30 -0
  73. package/skills/ship/references/queue-mode.md +1 -1
  74. package/skills/ship/references/ready-run-wait.md +3 -3
  75. package/skills/test-fix-loop/SKILL.md +1 -1
  76. package/skills/verification-scaffold/SKILL.md +73 -0
  77. package/skills/verification-scaffold/references/gitleaks.toml.template +13 -0
  78. package/skills/verification-scaffold/references/guardrail-audit.md +156 -0
  79. package/skills/verification-scaffold/references/guards/block-stash-hook.sh.template +72 -0
  80. package/skills/verification-scaffold/references/guards/commit-on-main-hook.sh.template +82 -0
  81. package/skills/verification-scaffold/references/guards/orphan-test-check.sh.template +102 -0
  82. package/skills/verification-scaffold/references/guards/precommit-guard.sh.template +115 -0
  83. package/skills/verification-scaffold/references/guards/secret-scan-hook.sh.template +176 -0
  84. package/skills/verification-scaffold/references/guards/verification-gates.yml.template +49 -0
  85. package/skills/verification-scaffold/references/guards/wrapper-boundary.sh.template +47 -0
  86. package/skills/verification-scaffold/references/guards.tsv +21 -0
  87. package/skills/verification-scaffold/references/verification-readme.template +55 -0
  88. package/skills/verification-scaffold/scripts/verification-scaffold.sh +785 -0
  89. package/skills/verification-scaffold/test/bite-proof.test.sh +159 -0
  90. package/skills/verification-scaffold/test/generator.test.sh +319 -0
  91. package/skills/verification-scaffold/test/manifest.test.sh +144 -0
  92. package/skills/work/SKILL.md +2 -1
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # precommit-guard.sh — refuse `git commit` on main/master, without hook execution.
3
+ #
4
+ # Canonical source for the commit-on-main check (Soleur Cloud Mode, FR5).
5
+ # Plugin command hooks are documented cloud-capable for most events, but a
6
+ # skill cannot rely on hook execution in a not-local session — and user repos
7
+ # consuming via requiredPlugins have no .claude/hooks at all. work/ship/one-shot
8
+ # invoke this script directly so the refusal is structural in both worlds.
9
+ #
10
+ # Scope: commit-on-main ONLY. The DONE-marker stop-gate is not extractable —
11
+ # it reads hook-stdin transcript data a standalone script cannot see; it stays
12
+ # prose + the plugin Stop hook.
13
+ #
14
+ # Self-contained by design: no jq, no .claude/hooks/lib vendoring (those paths
15
+ # resolve relative to the repo, which is wrong when this script runs from the
16
+ # plugin install cache), no emit_incident — callers log their own incidents.
17
+ # ONE sibling dependency: scripts/lib/wrapper-boundary.sh ships beside this
18
+ # script in the plugin cache, so $(dirname)/lib/… resolves in both contexts
19
+ # (#10248 — the canonical wrapper-prefix grammar the COMMIT_RE below consumes).
20
+ #
21
+ # Usage:
22
+ # precommit-guard.sh "COMMAND" check a shell command string
23
+ # precommit-guard.sh --cwd DIR also check DIR's branch (a `cd DIR &&`
24
+ # or `git -C DIR` inside COMMAND wins)
25
+ #
26
+ # Exit 0 = allowed (not a commit, or not on main/master).
27
+ # Exit 1 = refused — reason on stderr.
28
+ # Exit 2 = usage error.
29
+
30
+ set -euo pipefail
31
+
32
+ COMMAND=""
33
+ CWD=""
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --cwd) CWD="${2:-}"; shift 2 ;;
37
+ -*) echo "precommit-guard.sh: unknown flag: $1" >&2; exit 2 ;;
38
+ *) COMMAND="$1"; shift ;;
39
+ esac
40
+ done
41
+
42
+ # Detection is per pipeline segment: a `git commit` can hide anywhere in a
43
+ # chain — `git add && git commit`, `cd repo; git commit`, `printf msg |
44
+ # git commit -F -`, `LEFTHOOK=0 git commit`. Split COMMAND on the chain
45
+ # operators (&&, ||, ;, |, newline), then judge each segment.
46
+ #
47
+ # Known lexical limits (documented, not silently missed): subshell/eval forms
48
+ # like `bash -c 'git commit'`, `$(git commit)`, `( git commit )`, quoted
49
+ # `-C`/`cd` paths containing spaces, `git -c <cfg> commit` side effects beyond
50
+ # detection, and commits created without `git commit` (merge/pull/cherry-pick/
51
+ # rebase/continue) are out of this guard's declared scope — commit-on-main ONLY.
52
+ # Opaque string payloads (`rtk run -c '…'`, `rtk summary --shell '…'`) join that
53
+ # same declared class — the boundary fragment deliberately excludes the
54
+ # -c/--command/--shell atoms so they cannot match here.
55
+ _PC_GUARD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
56
+ unset VERIFICATION_ENV_ASN_RE VERIFICATION_LAUNCHER_RE VERIFICATION_RTK_RE VERIFICATION_BOUNDARY_PFX VERIFICATION_BOUNDARY_PFX_LOADED
57
+ if [[ -f "$_PC_GUARD_DIR/lib/wrapper-boundary.sh" ]]; then
58
+ # shellcheck source=/dev/null
59
+ source "$_PC_GUARD_DIR/lib/wrapper-boundary.sh" 2>/dev/null || true
60
+ fi
61
+ if [[ "${VERIFICATION_BOUNDARY_PFX_LOADED:-0}" != 1 ]]; then
62
+ unset VERIFICATION_ENV_ASN_RE VERIFICATION_LAUNCHER_RE VERIFICATION_RTK_RE VERIFICATION_BOUNDARY_PFX
63
+ echo "precommit-guard.sh: wrapper-boundary.sh unavailable at $_PC_GUARD_DIR/lib — commit gate runs pre-#10248 matching" >&2
64
+ fi
65
+ unset _PC_GUARD_DIR
66
+ # #10248: the shared fragment replaces the inline env-asn/launcher alternation —
67
+ # one canonical atom set for every boundary-anchored gate. The :- fallback is
68
+ # the PRE-FIX atom set (env + launcher) — a superset of this file's prior
69
+ # env*/launcher?/env* sandwich, which is the safe direction for a deny gate.
70
+ COMMIT_RE='^[[:space:]]*('"${VERIFICATION_BOUNDARY_PFX:-([A-Za-z_][A-Za-z_0-9]*=[^[:space:]]+|sudo|command|nice|env|xargs)}"'[[:space:]]+)*git([[:space:]]+(-C[[:space:]]+[^[:space:]]+|-c[[:space:]]+[^[:space:]]+|--git-dir=[^[:space:]]+|--git-dir[[:space:]]+[^[:space:]]+|-[A-Za-z]))*[[:space:]]+commit([[:space:]]|$)'
71
+
72
+ last_cd=""
73
+ while IFS= read -r seg; do
74
+ # Track the dir the chain has cd'd into — it applies to later segments.
75
+ cd_hit="$(grep -oE '(^|[[:space:]])cd[[:space:]]+[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/.*cd[[:space:]]+//' || true)"
76
+ [[ -n "$cd_hit" ]] && last_cd="$cd_hit"
77
+
78
+ grep -qE "$COMMIT_RE" <<<"$seg" || continue
79
+
80
+ # Resolve the repo THIS segment commits into. Priority: the -C / --git-dir
81
+ # attached to the commit's own git invocation, then a GIT_DIR env-assignment
82
+ # prefix (LEFTHOOK=0-style prefixes are the reason env-assignments are scanned
83
+ # at all — dropping GIT_DIR would resolve the wrong repo); then the chain's
84
+ # most recent cd; then --cwd; then ambient $PWD.
85
+ target="" git_dir=""
86
+ if grep -qoE -- '--git-dir[=[:space:]][^[:space:]]+' <<<"$seg"; then
87
+ git_dir="$(grep -oE -- '--git-dir[=[:space:]][^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/^--git-dir[=[:space:]]+//' || true)"
88
+ elif grep -qoE -- '(^|[[:space:]])GIT_DIR=[^[:space:]]+' <<<"$seg"; then
89
+ git_dir="$(grep -oE -- '(^|[[:space:]])GIT_DIR=[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/.*GIT_DIR=//' || true)"
90
+ else
91
+ git_c="$(grep -oE -- '-C[[:space:]]+[^[:space:]]+' <<<"$seg" | tail -n 1 | sed -E 's/^-C[[:space:]]+//' || true)"
92
+ [[ -n "$git_c" ]] && target="$git_c"
93
+ fi
94
+ if [[ -z "$target" && -z "$git_dir" ]]; then
95
+ for cand in "$last_cd" "$CWD" "$PWD"; do
96
+ [[ -n "$cand" && -d "$cand" ]] && target="$cand" && break
97
+ done
98
+ fi
99
+
100
+ if [[ -n "$git_dir" ]]; then
101
+ BRANCH="$(git --git-dir="$git_dir" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
102
+ elif [[ -n "$target" && -d "$target" ]]; then
103
+ BRANCH="$(git -C "$target" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
104
+ else
105
+ continue # nowhere to resolve — nothing to refuse on
106
+ fi
107
+ # The alternation is the stamped default-branch set — soleur:verification-scaffold
108
+ # emits this file with `@DEFAULT_BRANCHES@` substituted for the target's resolved default.
109
+ if [[ "$BRANCH" =~ ^(@DEFAULT_BRANCHES@)$ ]]; then
110
+ printf 'BLOCKED: Committing directly to %s is not allowed. Create a feature branch first.\n' "$BRANCH" >&2
111
+ exit 1
112
+ fi
113
+ done < <(printf '%s\n' "$COMMAND" | sed -E 's/&&|\|\||[;|]/\n/g')
114
+
115
+ exit 0
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env bash
2
+ # secret-scan.sh — PreToolUse hook emitted by soleur:verification-scaffold.
3
+ # Scans the staged index for secret-shaped strings (PEM bodies, vendor tokens,
4
+ # cloud keys) via gitleaks and reports before a `git commit` tool call lands.
5
+ #
6
+ # MODE=@MODE@ — stamped at emit time:
7
+ # advisory (the installed default): real findings surface as `ask`; tool
8
+ # errors (missing/unrunnable gitleaks, absent ruleset, empty report) warn
9
+ # and allow — loudly, never silently.
10
+ # enforce: findings deny; tool errors deny with the install remediation.
11
+ # Promote by changing MODE to "enforce" — a one-line flip. The predicates are
12
+ # identical in both modes; only the terminal decision changes.
13
+ #
14
+ # Hook stdin: JSON payload with tool_input. Hook stdout: the PreToolUse JSON
15
+ # decision envelope. Exit code is always 0 — the envelope carries the verdict.
16
+ #
17
+ # Portability floor: bash + POSIX tools only (stock macOS included — the
18
+ # timeout->gtimeout->bare-fallback below is deliberate). No network egress.
19
+ set -euo pipefail
20
+
21
+ MODE="@MODE@"
22
+
23
+ # --- decision envelopes -------------------------------------------------------
24
+ # _vs_decide <allow|ask|deny> [reason]: one JSON envelope on stdout, exit 0.
25
+ # Reasons are escaped (backslash, quote, newline) before embedding.
26
+ _vs_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' '; }
27
+ _vs_decide() {
28
+ local d="$1" r="${2:-}"
29
+ if [ -n "$r" ]; then
30
+ printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"%s","permissionDecisionReason":"%s"}}\n' \
31
+ "$d" "$(_vs_escape "$r")"
32
+ else
33
+ printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"%s"}}\n' "$d"
34
+ fi
35
+ exit 0
36
+ }
37
+ _vs_allow() { _vs_decide allow; }
38
+ # _vs_tool_error <msg>: a scanner/tool problem, never a finding. Advisory warns
39
+ # and allows; enforce denies with remediation. `ask` can never fire on this path.
40
+ _vs_tool_error() {
41
+ local msg="secret-scan: $1"
42
+ printf '[secret-scan] WARN: %s\n' "$msg" >&2
43
+ if [ "$MODE" = "enforce" ]; then
44
+ _vs_decide deny "BLOCKED: $msg"
45
+ else
46
+ _vs_decide allow
47
+ fi
48
+ }
49
+ # _vs_finding <msg>: a REAL finding — the only path that may `ask`.
50
+ _vs_finding() {
51
+ if [ "$MODE" = "enforce" ]; then
52
+ _vs_decide deny "BLOCKED: $1"
53
+ else
54
+ _vs_decide ask "$1"
55
+ fi
56
+ }
57
+
58
+ # --- stdin command extraction (jq -> node -> sed fallback) --------------------
59
+ _vs_payload="$(cat)"
60
+ _vs_cmd=""
61
+ if command -v jq >/dev/null 2>&1; then
62
+ _vs_cmd="$(printf '%s' "$_vs_payload" | jq -r '.tool_input.command // ""' 2>/dev/null || true)"
63
+ elif command -v node >/dev/null 2>&1; then
64
+ _vs_cmd="$(printf '%s' "$_vs_payload" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const c=JSON.parse(s).tool_input.command;process.stdout.write(typeof c==="string"?c:"")}catch(e){}})' 2>/dev/null || true)"
65
+ else
66
+ # Last-resort extraction: handles the common single-quoted-string shape.
67
+ # Documented limit: escaped quotes inside the command truncate the value,
68
+ # which only ever DE-escalates this hook (the CI job still scans the push).
69
+ _vs_cmd="$(printf '%s' "$_vs_payload" | sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)"
70
+ fi
71
+ [ -n "$_vs_cmd" ] || _vs_allow
72
+
73
+ # `git commit` at a command boundary: start, or after &&, ||, ;, |, $(.
74
+ # Trailing space-or-end rejects commit-tree/commit-graph.
75
+ if ! grep -qE '(^|[[:space:]]|&&|\|\||;|\$\()[[:space:]]*git[[:space:]]+commit([[:space:]]|$)' <<<"$_vs_cmd"; then
76
+ _vs_allow
77
+ fi
78
+
79
+ # --- environment guards -------------------------------------------------------
80
+ GL_REMEDIATION="install gitleaks 8.24.x (e.g. 'mise use gitleaks@8.24.2' or 'brew install gitleaks') — the CI job scans every PR regardless."
81
+
82
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
83
+ _vs_allow
84
+ fi
85
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
86
+ if [ -z "$repo_root" ] || [ ! -f "$repo_root/.gitleaks.toml" ]; then
87
+ _vs_tool_error ".gitleaks.toml not found at the repo root — the staged scan was skipped. $GL_REMEDIATION"
88
+ fi
89
+ if ! command -v gitleaks >/dev/null 2>&1; then
90
+ _vs_tool_error "gitleaks is not on PATH — the staged scan was skipped. $GL_REMEDIATION"
91
+ fi
92
+
93
+ # Resolvable is not runnable: probe the binary, bounded when a bound exists
94
+ # (stock macOS has neither timeout nor gtimeout — a missing bound must never
95
+ # read as a broken scanner).
96
+ gl_to=()
97
+ if command -v timeout >/dev/null 2>&1; then gl_to=(timeout 10)
98
+ elif command -v gtimeout >/dev/null 2>&1; then gl_to=(gtimeout 10); fi
99
+ gl_rc=0
100
+ { ${gl_to[@]+"${gl_to[@]}"} gitleaks version >/dev/null; } 2>/dev/null || gl_rc=$?
101
+ if [ "$gl_rc" -ne 0 ]; then
102
+ _vs_tool_error "gitleaks resolves on PATH but cannot run (rc=$gl_rc — an unpinned version-manager shim?). $GL_REMEDIATION"
103
+ fi
104
+
105
+ # --- staged scan ---------------------------------------------------------------
106
+ # Colour is pinned off on BOTH config surfaces: gitleaks runs git itself, and a
107
+ # user-side color.ui/color.diff=always wraps patch lines in ANSI, which makes
108
+ # the scan report ZERO findings on a staged key (measured). GIT_CONFIG_COUNT
109
+ # appends so a caller's own -c settings survive; GIT_CONFIG_PARAMETERS is a
110
+ # separate source read AFTER the count list, so it is pinned too.
111
+ report_file="$(mktemp -t verification-scan-XXXXXX.json)"
112
+ nodiff_dir="$(mktemp -d -t verification-nodiff-XXXXXX)"
113
+ nodiff_report="$(mktemp -t verification-nodiff-XXXXXX.json)"
114
+ trap 'rm -f "$report_file" "$nodiff_report"; rm -rf "$nodiff_dir"' EXIT INT TERM
115
+
116
+ gl_cfg_n="${GIT_CONFIG_COUNT:-0}"
117
+ case "$gl_cfg_n" in ''|*[!0-9]*) gl_cfg_n=0 ;; esac
118
+ scan_rc=0
119
+ (
120
+ cd "$repo_root" || exit 1
121
+ env "GIT_CONFIG_COUNT=$((gl_cfg_n + 2))" \
122
+ "GIT_CONFIG_KEY_${gl_cfg_n}=color.ui" "GIT_CONFIG_VALUE_${gl_cfg_n}=never" \
123
+ "GIT_CONFIG_KEY_$((gl_cfg_n + 1))=color.diff" "GIT_CONFIG_VALUE_$((gl_cfg_n + 1))=never" \
124
+ "GIT_CONFIG_PARAMETERS=${GIT_CONFIG_PARAMETERS:+${GIT_CONFIG_PARAMETERS} }'color.ui'='never' 'color.diff'='never'" \
125
+ gitleaks git --pre-commit --staged --redact --no-banner --exit-code 1 \
126
+ --report-format json --report-path "$report_file" >/dev/null 2>&1
127
+ ) || scan_rc=$?
128
+
129
+ # -diff gitattribute bypass: a path marked -diff renders "Binary files …
130
+ # differ" with no `+` lines, so the staged scan sees nothing in it. Re-scan
131
+ # those paths from their STAGED blobs with `detect --no-git`, which never
132
+ # consults git's diff machinery.
133
+ gl_nodiff=()
134
+ while IFS= read -r -d '' _p; do
135
+ [ -n "$_p" ] || continue
136
+ if [ "$(git -C "$repo_root" check-attr diff -- "$_p" 2>/dev/null | sed 's/.*: //')" = "unset" ]; then
137
+ gl_nodiff+=("$_p")
138
+ fi
139
+ done < <(git -C "$repo_root" diff --cached --name-only -z --diff-filter=ACMR 2>/dev/null)
140
+
141
+ if (( ${#gl_nodiff[@]} > 0 )); then
142
+ for _p in "${gl_nodiff[@]}"; do
143
+ mkdir -p "$nodiff_dir/$(dirname "$_p")" 2>/dev/null || true
144
+ git -C "$repo_root" show ":$_p" > "$nodiff_dir/$_p" 2>/dev/null || true
145
+ done
146
+ nodiff_rc=0
147
+ gitleaks detect --no-git --source "$nodiff_dir" --redact --no-banner --exit-code 1 \
148
+ --report-format json --report-path "$nodiff_report" >/dev/null 2>&1 || nodiff_rc=$?
149
+ if [ "$nodiff_rc" -eq 1 ] && [ -s "$nodiff_report" ]; then
150
+ nd_count="$(grep -c '"RuleID"' "$nodiff_report" 2>/dev/null || echo 0)"
151
+ nd_files="$(printf '%s, ' "${gl_nodiff[@]}")"
152
+ _vs_finding "gitleaks found ${nd_count} secret-shaped string(s) in staged file(s) carrying a \`-diff\` gitattribute (${nd_files%, }), which hides them from the staged scan — they were re-scanned from their staged blobs. Scrub the secret bytes; if the attribute is not deliberate, remove it."
153
+ elif [ "$nodiff_rc" -ne 0 ] && [ "$nodiff_rc" -ne 1 ]; then
154
+ _vs_tool_error "the -diff re-scan exited $nodiff_rc without a findings report; those paths were NOT verified."
155
+ fi
156
+ fi
157
+
158
+ if [ "$scan_rc" -eq 0 ]; then
159
+ _vs_allow
160
+ fi
161
+
162
+ # Non-zero scan rc with an empty/absent report is a scanner failure, not a
163
+ # finding — it takes the tool-error path in both modes.
164
+ if [ ! -s "$report_file" ] || ! grep -q '"RuleID"' "$report_file" 2>/dev/null; then
165
+ _vs_tool_error "gitleaks exited $scan_rc but produced no findings report; the staged content was NOT scanned. Run 'gitleaks git --pre-commit --staged' locally to diagnose. $GL_REMEDIATION"
166
+ fi
167
+
168
+ count="$(grep -c '"RuleID"' "$report_file" 2>/dev/null || echo 0)"
169
+ if command -v jq >/dev/null 2>&1; then
170
+ summary="$(jq -r '[.[] | "\(.File):\(.StartLine) (\(.RuleID))"] | .[0:5] | join("; ")' "$report_file" 2>/dev/null || echo "")"
171
+ else
172
+ summary="$(grep -o '"File": *"[^"]*"' "$report_file" 2>/dev/null | head -n 5 | sed 's/"File": *"//; s/"$//' | tr '\n' '; ' || true)"
173
+ fi
174
+ reason="gitleaks found ${count} secret-shaped string(s) in the staged index. Locations: ${summary:-<redacted>}. Scrub the secrets — never commit-then-rotate. Unstage with 'git restore --staged <path>', redact in place, re-stage and retry. To mark a known-safe line, annotate it '# gitleaks:allow issue:#NNN <reason>'." # waiver-doc string (issue:#10404 teaches trailer form)
175
+
176
+ _vs_finding "$reason"
@@ -0,0 +1,49 @@
1
+ # verification-gates.yml — emitted by soleur:verification-scaffold at the
2
+ # REPOSITORY ROOT (.github/workflows only runs from the root — a nested copy is
3
+ # dormant). Jobs below run advisory-first: `continue-on-error` is the chokepoint
4
+ # the promotion step flips (see VERIFICATION.md).
5
+ #
6
+ # Marker convention for the emitter — a job block between
7
+ # `# verification-scaffold:begin:<guard>` and `# verification-scaffold:end:<guard>`
8
+ # is kept only when that guard is selected. Do not remove the markers.
9
+ name: verification-gates
10
+
11
+ on:
12
+ pull_request:
13
+ workflow_dispatch:
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ # verification-scaffold:begin:secret-scan
20
+ secret-scan:
21
+ name: secret-scan (gitleaks)
22
+ runs-on: ubuntu-latest
23
+ continue-on-error: @CI_CONTINUE_ON_ERROR@
24
+ steps:
25
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
26
+ - name: gitleaks staged+tree scan
27
+ # Pinned by commit SHA (supply-chain posture — never a mutable tag).
28
+ # Resolves gitleaks-action v2.3.9.
29
+ uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7
30
+ env:
31
+ GITLEAKS_ENABLE_COMMENTS: "false"
32
+ GITLEAKS_ENABLE_SUMMARY: "true"
33
+ # Honour the emitted ruleset when the repo carries one.
34
+ GITLEAKS_CONFIG: ".gitleaks.toml"
35
+ # verification-scaffold:end:secret-scan
36
+ # verification-scaffold:begin:orphan-test
37
+ orphan-test:
38
+ name: orphan-test registration check
39
+ runs-on: ubuntu-latest
40
+ continue-on-error: @CI_CONTINUE_ON_ERROR@
41
+ env:
42
+ # Declare the files that RUN your suites; the check searches them for each
43
+ # tracked *.test.sh basename. Glob runner files are fine.
44
+ RUNNER_GLOBS: "scripts/test-all.sh scripts/run-tests* Makefile makefile package.json"
45
+ steps:
46
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
47
+ - name: orphan-test check
48
+ run: bash scripts/verification/orphan-test-check.sh
49
+ # verification-scaffold:end:orphan-test
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # wrapper-boundary.sh — canonical inter-boundary prefix grammar (#10248).
3
+ #
4
+ # Boundary-anchored gates grep for a gated command anchored at a shell chain
5
+ # boundary (`(^|&&|\|\||;|\s--\s)` and siblings). That anchor is blind to
6
+ # command wrappers: `rtk gh pr merge`, `sudo git commit`, `X=1 gh pr merge`
7
+ # all sit between the boundary and the gated command. ONE shared fragment —
8
+ # interpolated by every site — gives ~20 boundary-anchored gates a common
9
+ # wrapper-prefix grammar (only ~4 sites had any prefix list before; the rest
10
+ # had none — that absence was the defect) so the wrapper set cannot silently
11
+ # drift (#10248; same argv-prefix class as PR #3689's `with_lock` bypass, learnings
12
+ # 2026-05-12-cross-session-lock-lease-bash-primitives.md SE1).
13
+ #
14
+ # Consumption idiom — insert between the boundary alternation and the gated
15
+ # command:
16
+ # (<boundary-alternation>)[[:space:]]*(${VERIFICATION_BOUNDARY_PFX:-}[[:space:]]+)*<gated-cmd>
17
+ # The `:-` default is load-bearing: several hooks run `set -u`, and an unset
18
+ # fragment degrades to pre-fix matching (status quo), never a hard error and
19
+ # never a new hole. The required trailing `[[:space:]]+` after each atom is the
20
+ # right-edge word boundary: `rtk-foo`, `artk`, `sudox`, `FOO` (no `=`) can never
21
+ # partially consume — the wrapper word must be a whole word between the boundary
22
+ # and the gated command.
23
+ #
24
+ # Restatement pin: the rtk atom below restates exec-subcommand and global-flag
25
+ # rows of lib/ext-filter-grammar.tsv (the argv[0]-unwrap class's canonical
26
+ # grammar, #10230); test/rtk-grammar-parity.test.sh asserts set equality by
27
+ # extraction — edit the TSV and this atom together. The atom admits ONLY inert
28
+ # grammar words (counted-verbose globals, --skip-env, the six exec subcommands);
29
+ # a string-payload flag (-c|--command|--shell) is NOT an atom, so
30
+ # `rtk run -c 'gh pr merge'` stops the prefix at `run` and cannot match — the
31
+ # opaque-payload class joins the pre-existing `bash -c '…'` lexical limit every
32
+ # boundary gate already declares (#10248 CTO ruling, decision-challenges row 2).
33
+
34
+ VERIFICATION_ENV_ASN_RE='[A-Za-z_][A-Za-z_0-9]*=[^[:space:]]+'
35
+ VERIFICATION_LAUNCHER_RE='(sudo|command|nice|env|xargs)'
36
+ # Globals: -v/-vv/-vvv/--verbose (counted) and --skip-env (takes an optional
37
+ # =value — the argv[0]-unwrap SUT accepts both spellings). Exec subcommands:
38
+ # run|err|test|proxy|bypass|summary (canonical TSV, class=exec). `--` is the
39
+ # clap end-of-options marker — inert anywhere in the wrapper span, so
40
+ # `rtk -- run gh` and `rtk run -- gh` both execute `gh` upstream and both
41
+ # must reach the gated literal here (canonical TSV flag-global row).
42
+ VERIFICATION_RTK_RE='rtk([[:space:]]+(-v|-vv|-vvv|--verbose|--skip-env(=[^[:space:]]+)?|--|run|err|test|proxy|bypass|summary))*'
43
+ VERIFICATION_BOUNDARY_PFX="(${VERIFICATION_ENV_ASN_RE}|${VERIFICATION_LAUNCHER_RE}|${VERIFICATION_RTK_RE})"
44
+
45
+ # Load sentinel — suites assert this, never `[[ -n "$VERIFICATION_BOUNDARY_PFX" ]]`,
46
+ # which an inherited environment value would satisfy even after a failed source.
47
+ VERIFICATION_BOUNDARY_PFX_LOADED=1
@@ -0,0 +1,21 @@
1
+ # verification-scaffold guard manifest (#10404) — the machine form of guardrail-audit.md.
2
+ # One row per installable guard. Columns:
3
+ # name guard id the CLI accepts (--guards/--uninstall/--list)
4
+ # verdict audit verdict (references/guardrail-audit.md) — every v1 row is
5
+ # generic-after-extraction by construction
6
+ # surfaces hook | ci | hook+ci — which emitted surfaces the guard owns
7
+ # templates `;`-separated template paths under references/ this row emits
8
+ # default_mode advisory — every v1 guard installs advisory-first (flip MODE= / drop
9
+ # continue-on-error to promote; emitted VERIFICATION.md names the step)
10
+ # deps external deps the emitted guard needs at the target (NOT satisfied by
11
+ # the emit; checked and named, never silently assumed)
12
+ # source_files `;`-separated repo sources the templates were extracted from (provenance;
13
+ # the near-verbatim pair is byte-pinned by test/manifest.test.sh)
14
+ # emits `;`-separated target-relative artifact paths this guard emits
15
+ # tier v1 | v2 — v2 rows are candidates only; the generator refuses to emit them
16
+ name verdict surfaces templates default_mode deps source_files emits tier
17
+ secret-scan generic-after-extraction hook+ci references/guards/secret-scan-hook.sh.template;references/gitleaks.toml.template;references/guards/verification-gates.yml.template advisory gitleaks 8.24.x on PATH (hook) or gitleaks-action (CI);jq or node for the settings merge .claude/hooks/git-commit-secret-scan.sh .claude/hooks/secret-scan.sh;.gitleaks.toml;.github/workflows/verification-gates.yml v1
18
+ commit-on-main generic-after-extraction hook references/guards/commit-on-main-hook.sh.template;references/guards/precommit-guard.sh.template;references/guards/wrapper-boundary.sh.template advisory git plugins/soleur/scripts/precommit-guard.sh;plugins/soleur/scripts/lib/wrapper-boundary.sh;.claude/hooks/guardrails.sh .claude/hooks/commit-on-main.sh;scripts/verification/precommit-guard.sh;scripts/verification/lib/wrapper-boundary.sh v1
19
+ block-stash generic-after-extraction hook references/guards/block-stash-hook.sh.template advisory git .claude/hooks/guardrails.sh .claude/hooks/block-stash.sh v1
20
+ orphan-test generic-after-extraction ci references/guards/orphan-test-check.sh.template;references/guards/verification-gates.yml.template advisory git scripts/lint-orphan-test-suites.sh scripts/verification/orphan-test-check.sh;.github/workflows/verification-gates.yml;.github/verification-known-orphans.txt v1
21
+ block-rm-rf generic-after-extraction hook - advisory git .claude/hooks/guardrails.sh .claude/hooks/block-rm-rf.sh v2
@@ -0,0 +1,55 @@
1
+ <!-- verification-scaffold:readme:start -->
2
+ ## Verification gates (installed by soleur:verification-scaffold)
3
+
4
+ Installed mode: **@MODE@** · installed guards: **@GUARD_LIST@** · generated @DATE@.
5
+
6
+ | Guard | Artifacts | Decides |
7
+ |---|---|---|
8
+ @GUARD_TABLE@
9
+
10
+ ### What advisory means
11
+
12
+ Every guard was installed **advisory-first**: hooks warn (or ask, on a real
13
+ secret-scan finding) instead of denying, and each CI job carries
14
+ `continue-on-error: true`, so nothing blocks a commit or a PR yet. Advisory is
15
+ a decision-verb difference only — the detection predicates are identical to
16
+ enforcement mode.
17
+
18
+ ### Promoting a guard to enforcement (one line per surface)
19
+
20
+ Promote only after **at least one observed clean advisory run** — a gate that
21
+ has never run green is a wall, not a floor.
22
+
23
+ - Hooks (`.claude/hooks/<guard>.sh`): change `MODE="advisory"` to `MODE="enforce"`.
24
+ - CI (`.github/workflows/verification-gates.yml`): set the job's
25
+ `continue-on-error` to `false` (or remove the line), then add the check to
26
+ the branch's required status checks. Note `commit-on-main`/`block-stash` are
27
+ hook-only — branch protection on the remote is the enforcement floor for the
28
+ *default branch itself*.
29
+ - Re-running the scaffold NEVER overwrites your edits: re-install refuses on
30
+ any existing artifact (there is no `--force`).
31
+
32
+ ### Uninstall
33
+
34
+ ```bash
35
+ bash <plugin>/skills/verification-scaffold/scripts/verification-scaffold.sh --target . --uninstall <guard>
36
+ ```
37
+
38
+ removes only that guard's emitted files plus its `.claude/settings.json` hook
39
+ entry; shared artifacts (the workflow, `.gitleaks.toml` when another guard
40
+ still needs it) stay — the workflow is only edited when this guard's job block
41
+ is actually present. `--uninstall` on a never-installed guard is a no-op that
42
+ lists what is installed, and once no guard artifact remains this marker block
43
+ is pruned (a file that held only this block is removed).
44
+
45
+ ### Requirements
46
+
47
+ - bash + POSIX tools (stock macOS included). Emitted hooks resolve `gitleaks`
48
+ via `command -v` at exec time; the CI job uses the SHA-pinned
49
+ `gitleaks/gitleaks-action`.
50
+ - The secret-scan hook needs `gitleaks` on PATH to run locally (8.24.x
51
+ recommended); without it the hook warns and allows (advisory) — the CI job
52
+ still scans every PR.
53
+ - `.claude/settings.json` hook entries serve Claude-Code-compatible agent
54
+ sessions; the workflow is the floor for everyone else.
55
+ <!-- verification-scaffold:readme:end -->