@windyroad/risk-scorer 0.12.2 → 0.12.3-preview.546

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.
@@ -310,5 +310,5 @@
310
310
  }
311
311
  },
312
312
  "name": "wr-risk-scorer",
313
- "version": "0.12.2"
313
+ "version": "0.12.3"
314
314
  }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Generated by scripts/sync-shim-wrappers.sh from
3
+ # packages/shared/lib/shim-wrapper-template.sh. DO NOT EDIT individual
4
+ # shim files in packages/*/bin/wr-* directly; edit the template + run
5
+ # `npm run sync:shim-wrappers` to regenerate.
6
+ #
7
+ # Resolution (ADR-080):
8
+ # 1. If the wrapper's parent dir is semver-shaped, treat as installed-
9
+ # cache execution and resolve to the highest-version sibling's
10
+ # scripts/ entry below.
11
+ # 2. Otherwise (parent dir is e.g. `architect`), treat as source-
12
+ # monorepo execution and dispatch to own scripts/. The source-repo-
13
+ # guard `exec` is the anchor parsed by
14
+ # packages/retrospective/scripts/check-tarball-shipped-shims.sh.
15
+ # 3. If the cache parent contains zero semver-shaped siblings, exit
16
+ # 127 with a stderr message naming the cache parent (per SQ-080-2).
17
+ #
18
+ # @adr ADR-080 (highest-version-wins shim wrapper plugin scaffold)
19
+ # @adr ADR-049 (plugin-bundled scripts resolve via bin/ on $PATH — amended)
20
+ # @problem P343 (mid-session staleness window)
21
+
22
+ set -euo pipefail
23
+
24
+ SHIM_DIR="$(cd "$(dirname "$0")" && pwd)"
25
+ OWN_VERSION_DIR="$(dirname "$SHIM_DIR")"
26
+ OWN_VERSION_NAME="$(basename "$OWN_VERSION_DIR")"
27
+ CACHE_PARENT="$(dirname "$OWN_VERSION_DIR")"
28
+
29
+ SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'
30
+
31
+ # Source-repo guard: own parent dir is NOT semver → dispatch to own scripts/.
32
+ if ! [[ "$OWN_VERSION_NAME" =~ $SEMVER_RE ]]; then
33
+ exec "$SHIM_DIR/../scripts/restage-commit.sh" "$@"
34
+ fi
35
+
36
+ # Cache execution: pick the highest-semver sibling under CACHE_PARENT.
37
+ HIGHEST=""
38
+ while IFS= read -r dir; do
39
+ name="$(basename "$dir")"
40
+ [[ "$name" =~ $SEMVER_RE ]] || continue
41
+ if [[ -z "$HIGHEST" ]] || [[ "$(printf '%s\n%s\n' "$HIGHEST" "$name" | sort -V | tail -1)" == "$name" ]]; then
42
+ HIGHEST="$name"
43
+ fi
44
+ done < <(find "$CACHE_PARENT" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
45
+
46
+ if [[ -z "$HIGHEST" ]]; then
47
+ printf 'wr-shim: no cached versions in %s\n' "$CACHE_PARENT" >&2
48
+ exit 127
49
+ fi
50
+
51
+ exec "$CACHE_PARENT/$HIGHEST/scripts/restage-commit.sh" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/risk-scorer",
3
- "version": "0.12.2",
3
+ "version": "0.12.3-preview.546",
4
4
  "description": "Pipeline risk scoring, commit/push gates, and secret leak detection",
5
5
  "bin": {
6
6
  "windyroad-risk-scorer": "./bin/install.mjs"
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ # packages/risk-scorer/scripts/restage-commit.sh
3
+ #
4
+ # Atomic re-stage-and-commit helper for the ADR-014 commit-gate flow.
5
+ #
6
+ # After the caller delegates to `wr-risk-scorer:pipeline` via the Agent tool
7
+ # (or invokes `/wr-risk-scorer:assess-release`), the Agent-tool boundary
8
+ # sometimes clears the parent index — a previously-staged set comes back as
9
+ # `Changes not staged for commit` and the subsequent `git commit` fails with
10
+ # "no changes added to commit" (P326). The workaround is a re-`git add` before
11
+ # the commit; this helper bundles the re-add + commit into a single atomic call
12
+ # so the SKILL.md surface no longer documents the double-tap.
13
+ #
14
+ # Surface:
15
+ # wr-risk-scorer-restage-commit -m "<msg>" [-m "<trailer>"] -- <path1> [<path2>...]
16
+ #
17
+ # Behaviour:
18
+ # - Accumulates all -m flags (repeatable; supports RISK_BYPASS-style trailers).
19
+ # - Requires `--` separator before paths (P326 grammar — avoids the path-vs-flag
20
+ # ambiguity when a path begins with `-`).
21
+ # - Runs `git add -- <paths>` (propagates `git add` exit code on bad paths).
22
+ # - Asserts the cached diff is non-empty (`git diff --cached --name-only`); exits
23
+ # 1 if nothing got staged, so the caller sees the empty-stage condition rather
24
+ # than committing an empty-or-misordered set.
25
+ # - Runs `git commit "${msg_args[@]}"` with the accumulated -m flags.
26
+ #
27
+ # Exit codes:
28
+ # 0 — commit landed
29
+ # 1 — usage error (missing -m, missing --, no paths) OR empty staging
30
+ # non-zero — propagated from `git add` or `git commit`
31
+ #
32
+ # @adr ADR-014 (governance skills commit their own work)
33
+ # @adr ADR-049 (resolved via bin/wr-risk-scorer-restage-commit shim)
34
+ # @adr ADR-052 (behavioural-fixture coverage at scripts/test/restage-commit.bats)
35
+ # @problem P326 (staged index cleared after risk-scorer pipeline delegation)
36
+ # @problem P057 (git mv + Edit re-stage discipline — composes with this helper)
37
+
38
+ set -uo pipefail
39
+
40
+ usage() {
41
+ cat >&2 <<'USAGE'
42
+ Usage: wr-risk-scorer-restage-commit -m "<msg>" [-m "<trailer>"] -- <path1> [<path2>...]
43
+
44
+ Atomic re-stage-and-commit after a wr-risk-scorer:pipeline Agent-tool delegation.
45
+ At least one -m flag is required. The `--` separator is mandatory before paths.
46
+
47
+ Examples:
48
+ wr-risk-scorer-restage-commit -m "docs(problems): open P999 widget" -- docs/problems/open/999-widget.md docs/problems/README.md
49
+
50
+ wr-risk-scorer-restage-commit \
51
+ -m "docs(problems): capture P999 widget" \
52
+ -m "RISK_BYPASS: capture-deferred-readme" \
53
+ -- docs/problems/open/999-widget.md
54
+ USAGE
55
+ }
56
+
57
+ msg_args=()
58
+ saw_separator=0
59
+ paths=()
60
+
61
+ while [ "$#" -gt 0 ]; do
62
+ case "$1" in
63
+ -m)
64
+ if [ "$#" -lt 2 ]; then
65
+ echo "ERROR: -m requires a message argument" >&2
66
+ usage
67
+ exit 1
68
+ fi
69
+ msg_args+=( -m "$2" )
70
+ shift 2
71
+ ;;
72
+ --)
73
+ saw_separator=1
74
+ shift
75
+ while [ "$#" -gt 0 ]; do
76
+ paths+=( "$1" )
77
+ shift
78
+ done
79
+ ;;
80
+ -h|--help)
81
+ usage
82
+ exit 0
83
+ ;;
84
+ *)
85
+ echo "ERROR: unexpected argument '$1' — paths must follow a '--' separator" >&2
86
+ usage
87
+ exit 1
88
+ ;;
89
+ esac
90
+ done
91
+
92
+ if [ "${#msg_args[@]}" -eq 0 ]; then
93
+ echo "ERROR: at least one -m message flag is required" >&2
94
+ usage
95
+ exit 1
96
+ fi
97
+
98
+ if [ "$saw_separator" -eq 0 ]; then
99
+ echo "ERROR: missing '--' separator before paths" >&2
100
+ usage
101
+ exit 1
102
+ fi
103
+
104
+ if [ "${#paths[@]}" -eq 0 ]; then
105
+ echo "ERROR: no paths supplied after '--'" >&2
106
+ usage
107
+ exit 1
108
+ fi
109
+
110
+ git add -- "${paths[@]}"
111
+ ADD_STATUS=$?
112
+ if [ "$ADD_STATUS" -ne 0 ]; then
113
+ exit "$ADD_STATUS"
114
+ fi
115
+
116
+ if [ -z "$(git diff --cached --name-only)" ]; then
117
+ echo "ERROR: nothing staged after re-add — supplied paths produced an empty index diff" >&2
118
+ exit 1
119
+ fi
120
+
121
+ git commit "${msg_args[@]}"
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural-fixture coverage for packages/risk-scorer/scripts/restage-commit.sh
3
+ # per ADR-052 (behavioural tests default) and P326 (re-stage-after-scorer-delegation).
4
+ #
5
+ # The script is invoked AFTER the agent delegates to wr-risk-scorer:pipeline
6
+ # to land the commit atomically — re-stages paths the Agent-tool boundary cleared
7
+ # from the index, asserts staging is non-empty, then runs `git commit` with the
8
+ # caller's -m args. Eliminates the silent re-add round-trip P326 documented.
9
+ #
10
+ # Surface:
11
+ # wr-risk-scorer-restage-commit -m "<msg>" [-m "<trailer>"] -- <path1> [<path2>...]
12
+
13
+ setup() {
14
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
15
+ SCRIPT="$REPO_ROOT/packages/risk-scorer/scripts/restage-commit.sh"
16
+ SHIM="$REPO_ROOT/packages/risk-scorer/bin/wr-risk-scorer-restage-commit"
17
+ WORK_DIR="$(mktemp -d)"
18
+ cd "$WORK_DIR"
19
+ git init --quiet
20
+ git config user.email "restage-test@example.com"
21
+ git config user.name "Restage Test"
22
+ git config commit.gpgsign false
23
+ git commit --quiet --allow-empty -m "init"
24
+ }
25
+
26
+ teardown() {
27
+ cd /
28
+ rm -rf "$WORK_DIR"
29
+ }
30
+
31
+ @test "shim wrapper exists and is executable" {
32
+ [ -x "$SHIM" ]
33
+ }
34
+
35
+ @test "shim resolves canonical script (not exit 127)" {
36
+ echo "seed" > seed.txt
37
+ run "$SHIM" -m "test" -- seed.txt
38
+ [ "$status" -ne 127 ]
39
+ }
40
+
41
+ @test "single path: re-stages and commits (the P326 happy path)" {
42
+ echo "content" > file.txt
43
+ run bash "$SCRIPT" -m "test: commit file" -- file.txt
44
+ [ "$status" -eq 0 ]
45
+ run git log -1 --name-only --format=
46
+ echo "$output" | grep -q '^file\.txt$'
47
+ }
48
+
49
+ @test "multi-path: re-stages all paths and commits them in one commit" {
50
+ echo "a" > a.txt
51
+ echo "b" > b.txt
52
+ echo "c" > c.txt
53
+ run bash "$SCRIPT" -m "test: multi" -- a.txt b.txt c.txt
54
+ [ "$status" -eq 0 ]
55
+ run git log -1 --name-only --format=
56
+ echo "$output" | grep -q '^a\.txt$'
57
+ echo "$output" | grep -q '^b\.txt$'
58
+ echo "$output" | grep -q '^c\.txt$'
59
+ }
60
+
61
+ @test "multiple -m flags pass through (trailer like RISK_BYPASS)" {
62
+ echo "x" > x.txt
63
+ run bash "$SCRIPT" \
64
+ -m "docs(problems): capture P999 test" \
65
+ -m "RISK_BYPASS: capture-deferred-readme" \
66
+ -- x.txt
67
+ [ "$status" -eq 0 ]
68
+ run git log -1 --format=%B
69
+ echo "$output" | grep -q '^docs(problems): capture P999 test'
70
+ echo "$output" | grep -q 'RISK_BYPASS: capture-deferred-readme'
71
+ }
72
+
73
+ @test "missing -m flag → exit 1, no commit" {
74
+ echo "x" > x.txt
75
+ run bash "$SCRIPT" -- x.txt
76
+ [ "$status" -ne 0 ]
77
+ echo "$output" | grep -qi 'message\|usage'
78
+ run git log --oneline
79
+ [ "$(echo "$output" | wc -l | tr -d ' ')" = "1" ]
80
+ }
81
+
82
+ @test "missing -- separator → exit 1" {
83
+ echo "x" > x.txt
84
+ run bash "$SCRIPT" -m "test" x.txt
85
+ [ "$status" -ne 0 ]
86
+ echo "$output" | grep -qi 'separator\|usage'
87
+ }
88
+
89
+ @test "no paths after -- → exit 1, no commit" {
90
+ run bash "$SCRIPT" -m "test" --
91
+ [ "$status" -ne 0 ]
92
+ echo "$output" | grep -qi 'path\|usage'
93
+ run git log --oneline
94
+ [ "$(echo "$output" | wc -l | tr -d ' ')" = "1" ]
95
+ }
96
+
97
+ @test "nothing staged after re-add → exit 1, no commit (e.g. unchanged file)" {
98
+ echo "tracked" > tracked.txt
99
+ git add tracked.txt
100
+ git commit --quiet -m "seed tracked"
101
+ run bash "$SCRIPT" -m "test: should fail" -- tracked.txt
102
+ [ "$status" -ne 0 ]
103
+ echo "$output" | grep -qi 'nothing staged\|empty'
104
+ run git log --oneline
105
+ [ "$(echo "$output" | wc -l | tr -d ' ')" = "2" ]
106
+ }
107
+
108
+ @test "path that doesn't exist → exit non-zero (git add propagates)" {
109
+ run bash "$SCRIPT" -m "test" -- nonexistent.txt
110
+ [ "$status" -ne 0 ]
111
+ }
112
+
113
+ @test "rename via git mv survives the re-stage-and-commit (P057 + P326 compose)" {
114
+ echo "v1" > orig.md
115
+ git add orig.md
116
+ git commit --quiet -m "seed"
117
+ git mv orig.md renamed.md
118
+ echo "v2" >> renamed.md
119
+ run bash "$SCRIPT" -m "test: rename+edit" -- renamed.md
120
+ [ "$status" -eq 0 ]
121
+ run git log -1 --name-only --format=
122
+ echo "$output" | grep -q '^renamed\.md$'
123
+ [ ! -f orig.md ]
124
+ run cat renamed.md
125
+ echo "$output" | grep -q 'v2'
126
+ }
127
+
128
+ @test "doesn't touch unrelated unstaged changes" {
129
+ echo "subject" > subject.txt
130
+ echo "bystander" > bystander.txt
131
+ run bash "$SCRIPT" -m "test: subject only" -- subject.txt
132
+ [ "$status" -eq 0 ]
133
+ run git status --porcelain bystander.txt
134
+ echo "$output" | grep -q '^?? bystander\.txt'
135
+ run git log -1 --name-only --format=
136
+ ! echo "$output" | grep -q '^bystander\.txt$'
137
+ }