@windyroad/risk-scorer 0.17.4 → 0.18.0
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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +13 -2
- package/bin/install.mjs +42 -8
- package/hooks/codex-agent-completion.mjs +82 -0
- package/hooks/hooks.json +5 -2
- package/hooks/risk-scorer-dispatch.sh +3 -0
- package/hooks/risk-scorer-scaffold-nudge.sh +15 -4
- package/package.json +5 -2
- package/scripts/codex-agents.mjs +208 -0
- package/scripts/sync-codex-skills.mjs +8 -3
- package/skills/assess-external-comms/SKILL.md +5 -4
- package/skills/assess-inbound-report/SKILL.md +5 -4
- package/skills/assess-release/SKILL.md +5 -4
- package/skills/assess-wip/SKILL.md +5 -4
- package/skills/bootstrap-catalog/SKILL.md +5 -4
- package/skills/create-risk/SKILL.md +5 -4
- package/skills/external-comms/SKILL.md +5 -4
- package/skills/pipeline/SKILL.md +5 -4
- package/skills/update-policy/SKILL.md +6 -5
- package/skills/wip/SKILL.md +5 -4
- package/agents/test/inbound-report-contract.bats +0 -225
- package/agents/test/risk-scorer-above-appetite-stop.bats +0 -80
- package/agents/test/risk-scorer-catalog-consumption.bats +0 -138
- package/agents/test/risk-scorer-commit-verdict.bats +0 -66
- package/agents/test/risk-scorer-monitoring-not-a-control.bats +0 -76
- package/agents/test/risk-scorer-reducing-bypass-criteria.bats +0 -62
- package/agents/test/risk-scorer-register-hint.bats +0 -111
- package/agents/test/risk-scorer-structured-remediations.bats +0 -123
- package/agents/test/risk-scorer-user-stated-preconditions.bats +0 -89
- package/hooks/test/ci-status-gate.bats +0 -234
- package/hooks/test/external-comms-gate.bats +0 -528
- package/hooks/test/external-comms-key-substance.bats +0 -74
- package/hooks/test/git-push-gate.bats +0 -82
- package/hooks/test/pipeline-state-changeset-partition.bats +0 -181
- package/hooks/test/pipeline-state-hash.bats +0 -195
- package/hooks/test/plan-risk-guidance-once-per-session.bats +0 -95
- package/hooks/test/reducing-marker-persistence.bats +0 -236
- package/hooks/test/risk-gate.bats +0 -330
- package/hooks/test/risk-policy-project-root.bats +0 -20
- package/hooks/test/risk-score-commit-gate-adr-031-bypass.bats +0 -98
- package/hooks/test/risk-score-commit-gate-cadence-staleness.bats +0 -105
- package/hooks/test/risk-score-mark-external-comms-prompt-parse.bats +0 -94
- package/hooks/test/risk-score-mark-register-queue.bats +0 -253
- package/hooks/test/risk-score-mark.bats +0 -112
- package/hooks/test/risk-scorer-hook-dispatch.bats +0 -77
- package/hooks/test/risk-scorer-no-stop-hook.bats +0 -19
- package/hooks/test/risk-scorer-scaffold-nudge.bats +0 -132
- package/hooks/test/slide-marker-on-subprocess-return.bats +0 -118
- package/hooks/test/wip-risk-mark.bats +0 -64
- package/scripts/test/drain-register-queue.bats +0 -359
- package/scripts/test/evaluate-graduation.bats +0 -533
- package/scripts/test/extract-risks-from-reports.bats +0 -366
- package/scripts/test/restage-commit.bats +0 -137
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# extract-risks-from-reports.sh — behavioural fixture per ADR-052.
|
|
4
|
-
# Walks .risk-reports/, parses RISK_REGISTER_HINT bullets per ADR-056,
|
|
5
|
-
# dedupes by slug, writes per-slug entries + README to docs/risks/.
|
|
6
|
-
#
|
|
7
|
-
# Tests are behavioural — replay the script against tmp-dir mock corpora,
|
|
8
|
-
# assert on observable filesystem outcomes (file presence, content shape,
|
|
9
|
-
# README rows). NO structural grep on script source.
|
|
10
|
-
|
|
11
|
-
setup() {
|
|
12
|
-
ORIG_DIR="$PWD"
|
|
13
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
14
|
-
SCRIPT="$REPO_ROOT/packages/risk-scorer/scripts/extract-risks-from-reports.sh"
|
|
15
|
-
TEST_DIR=$(mktemp -d)
|
|
16
|
-
cd "$TEST_DIR"
|
|
17
|
-
# All tests need RISK-POLICY.md to satisfy pre-condition
|
|
18
|
-
echo "# Risk Policy" > RISK-POLICY.md
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
teardown() {
|
|
22
|
-
cd "$ORIG_DIR"
|
|
23
|
-
rm -rf "$TEST_DIR"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
27
|
-
# Pre-condition handling
|
|
28
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
@test "fails with exit 1 when RISK-POLICY.md absent" {
|
|
31
|
-
rm RISK-POLICY.md
|
|
32
|
-
mkdir -p .risk-reports
|
|
33
|
-
echo "report" > .risk-reports/r.md
|
|
34
|
-
run "$SCRIPT"
|
|
35
|
-
[ "$status" -eq 1 ]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@test "fails with exit 1 when .risk-reports/ absent" {
|
|
39
|
-
run "$SCRIPT"
|
|
40
|
-
[ "$status" -eq 1 ]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@test "fails with exit 1 when .risk-reports/ is empty" {
|
|
44
|
-
mkdir -p .risk-reports
|
|
45
|
-
run "$SCRIPT"
|
|
46
|
-
[ "$status" -eq 1 ]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
50
|
-
# Deterministic extraction (Phase 1)
|
|
51
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
@test "extracts a single 3-column hint and writes one entry" {
|
|
54
|
-
mkdir -p .risk-reports
|
|
55
|
-
cat > .risk-reports/r1.md <<'EOF'
|
|
56
|
-
# Report
|
|
57
|
-
|
|
58
|
-
RISK_REGISTER_HINT:
|
|
59
|
-
- above-appetite-residual | example-risk-class | A description of the risk class
|
|
60
|
-
EOF
|
|
61
|
-
run "$SCRIPT"
|
|
62
|
-
[ "$status" -eq 0 ]
|
|
63
|
-
[ -d docs/risks ]
|
|
64
|
-
[ -f docs/risks/README.md ]
|
|
65
|
-
ls docs/risks/R*-example-risk-class.active.md >/dev/null 2>&1
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@test "dedupes 3 reports with same slug to one entry citing all 3 sources" {
|
|
69
|
-
mkdir -p .risk-reports
|
|
70
|
-
for i in 1 2 3; do
|
|
71
|
-
cat > .risk-reports/r${i}.md <<EOF
|
|
72
|
-
RISK_REGISTER_HINT:
|
|
73
|
-
- above-appetite-residual | duplicated-class | Same risk surfaced ${i} times
|
|
74
|
-
EOF
|
|
75
|
-
done
|
|
76
|
-
run "$SCRIPT"
|
|
77
|
-
[ "$status" -eq 0 ]
|
|
78
|
-
# Exactly one entry file
|
|
79
|
-
count=$(ls docs/risks/R*-duplicated-class.active.md 2>/dev/null | wc -l | tr -d ' ')
|
|
80
|
-
[ "$count" = "1" ]
|
|
81
|
-
# Source Evidence block lists all 3 reports
|
|
82
|
-
entry=$(ls docs/risks/R*-duplicated-class.active.md)
|
|
83
|
-
grep -q ".risk-reports/r1.md" "$entry"
|
|
84
|
-
grep -q ".risk-reports/r2.md" "$entry"
|
|
85
|
-
grep -q ".risk-reports/r3.md" "$entry"
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
@test "infers infosec category from confidentiality-disclosure reason-tag" {
|
|
89
|
-
mkdir -p .risk-reports
|
|
90
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
91
|
-
RISK_REGISTER_HINT:
|
|
92
|
-
- confidentiality-disclosure | leak-shape | A confidentiality leak surfaced
|
|
93
|
-
EOF
|
|
94
|
-
run "$SCRIPT"
|
|
95
|
-
[ "$status" -eq 0 ]
|
|
96
|
-
entry=$(ls docs/risks/R*-leak-shape.active.md)
|
|
97
|
-
grep -q "^\*\*Category\*\*: infosec" "$entry"
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@test "infers operational category from above-appetite-residual reason-tag" {
|
|
101
|
-
mkdir -p .risk-reports
|
|
102
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
103
|
-
RISK_REGISTER_HINT:
|
|
104
|
-
- above-appetite-residual | ops-shape | An ops risk surfaced
|
|
105
|
-
EOF
|
|
106
|
-
run "$SCRIPT"
|
|
107
|
-
[ "$status" -eq 0 ]
|
|
108
|
-
entry=$(ls docs/risks/R*-ops-shape.active.md)
|
|
109
|
-
grep -q "^\*\*Category\*\*: operational" "$entry"
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@test "skips bullets with invalid reason-tag" {
|
|
113
|
-
mkdir -p .risk-reports
|
|
114
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
115
|
-
RISK_REGISTER_HINT:
|
|
116
|
-
- not-a-real-tag | bogus-slug | Bogus prose
|
|
117
|
-
- above-appetite-residual | valid-slug | Valid prose
|
|
118
|
-
EOF
|
|
119
|
-
run "$SCRIPT"
|
|
120
|
-
[ "$status" -eq 0 ]
|
|
121
|
-
! ls docs/risks/R*-bogus-slug.active.md 2>/dev/null
|
|
122
|
-
ls docs/risks/R*-valid-slug.active.md >/dev/null 2>&1
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
126
|
-
# Source Evidence block + ADR-026 sentinel
|
|
127
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
128
|
-
|
|
129
|
-
@test "entry includes Source Evidence block with originating reports" {
|
|
130
|
-
mkdir -p .risk-reports
|
|
131
|
-
cat > .risk-reports/abc.md <<'EOF'
|
|
132
|
-
RISK_REGISTER_HINT:
|
|
133
|
-
- above-appetite-residual | citing-class | Cited risk
|
|
134
|
-
EOF
|
|
135
|
-
run "$SCRIPT"
|
|
136
|
-
[ "$status" -eq 0 ]
|
|
137
|
-
entry=$(ls docs/risks/R*-citing-class.active.md)
|
|
138
|
-
grep -q "## Source Evidence" "$entry"
|
|
139
|
-
grep -q ".risk-reports/abc.md" "$entry"
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
@test "ungrounded scoring fields use ADR-026 sentinel" {
|
|
143
|
-
mkdir -p .risk-reports
|
|
144
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
145
|
-
RISK_REGISTER_HINT:
|
|
146
|
-
- above-appetite-residual | sentinel-test | Sentinel check
|
|
147
|
-
EOF
|
|
148
|
-
run "$SCRIPT"
|
|
149
|
-
[ "$status" -eq 0 ]
|
|
150
|
-
entry=$(ls docs/risks/R*-sentinel-test.active.md)
|
|
151
|
-
grep -q "not estimated — no prior data" "$entry"
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@test "Status field is auto-scaffolded pending review" {
|
|
155
|
-
mkdir -p .risk-reports
|
|
156
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
157
|
-
RISK_REGISTER_HINT:
|
|
158
|
-
- above-appetite-residual | status-test | Status check
|
|
159
|
-
EOF
|
|
160
|
-
run "$SCRIPT"
|
|
161
|
-
[ "$status" -eq 0 ]
|
|
162
|
-
entry=$(ls docs/risks/R*-status-test.active.md)
|
|
163
|
-
grep -q "Active (auto-scaffolded — pending review)" "$entry"
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
167
|
-
# Recogniser-shape skeleton emission (P169 — bootstrap stops the bleeding)
|
|
168
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
169
|
-
# Newly-scaffolded entries MUST carry a ## Recogniser section with the three
|
|
170
|
-
# sub-blocks awaiting curation, so new risks stop landing without the shape
|
|
171
|
-
# that the @windyroad/risk-scorer pipeline relies on for slug-token matching.
|
|
172
|
-
|
|
173
|
-
@test "scaffolded entry includes ## Recogniser section" {
|
|
174
|
-
mkdir -p .risk-reports
|
|
175
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
176
|
-
RISK_REGISTER_HINT:
|
|
177
|
-
- above-appetite-residual | recogniser-shape-test | Recogniser skeleton check
|
|
178
|
-
EOF
|
|
179
|
-
run "$SCRIPT"
|
|
180
|
-
[ "$status" -eq 0 ]
|
|
181
|
-
entry=$(ls docs/risks/R*-recogniser-shape-test.active.md)
|
|
182
|
-
grep -q "^## Recogniser" "$entry"
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
@test "scaffolded Recogniser block has Path-patterns / Diff-content / Anti-patterns sub-blocks" {
|
|
186
|
-
mkdir -p .risk-reports
|
|
187
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
188
|
-
RISK_REGISTER_HINT:
|
|
189
|
-
- above-appetite-residual | recogniser-subblock-test | Recogniser sub-block check
|
|
190
|
-
EOF
|
|
191
|
-
run "$SCRIPT"
|
|
192
|
-
[ "$status" -eq 0 ]
|
|
193
|
-
entry=$(ls docs/risks/R*-recogniser-subblock-test.active.md)
|
|
194
|
-
grep -q "Path patterns" "$entry"
|
|
195
|
-
grep -q "Diff-content keywords" "$entry"
|
|
196
|
-
grep -q "Anti-patterns" "$entry"
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
@test "scaffolded Recogniser sub-blocks carry pending-review placeholders" {
|
|
200
|
-
mkdir -p .risk-reports
|
|
201
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
202
|
-
RISK_REGISTER_HINT:
|
|
203
|
-
- above-appetite-residual | recogniser-pending-test | Recogniser pending check
|
|
204
|
-
EOF
|
|
205
|
-
run "$SCRIPT"
|
|
206
|
-
[ "$status" -eq 0 ]
|
|
207
|
-
entry=$(ls docs/risks/R*-recogniser-pending-test.active.md)
|
|
208
|
-
# The skeleton uses the same ADR-026-style "pending review" sentinel
|
|
209
|
-
# so curators can grep for unfinished sections. Three pending-review
|
|
210
|
-
# markers (one per sub-block: paths / keywords / anti-patterns).
|
|
211
|
-
recog_lines=$(sed -n '/^## Recogniser/,/^## Inherent/p' "$entry" | grep -c "pending review" || true)
|
|
212
|
-
[ "$recog_lines" -ge 3 ]
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
216
|
-
# Idempotency / slug collision append
|
|
217
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
218
|
-
|
|
219
|
-
@test "second run with same corpus appends to existing Source Evidence" {
|
|
220
|
-
mkdir -p .risk-reports
|
|
221
|
-
cat > .risk-reports/first.md <<'EOF'
|
|
222
|
-
RISK_REGISTER_HINT:
|
|
223
|
-
- above-appetite-residual | append-test | First sighting
|
|
224
|
-
EOF
|
|
225
|
-
run "$SCRIPT"
|
|
226
|
-
[ "$status" -eq 0 ]
|
|
227
|
-
entry=$(ls docs/risks/R*-append-test.active.md)
|
|
228
|
-
# Add a second report with the same slug
|
|
229
|
-
cat > .risk-reports/second.md <<'EOF'
|
|
230
|
-
RISK_REGISTER_HINT:
|
|
231
|
-
- above-appetite-residual | append-test | Second sighting
|
|
232
|
-
EOF
|
|
233
|
-
run "$SCRIPT"
|
|
234
|
-
[ "$status" -eq 0 ]
|
|
235
|
-
# Same entry file — no new R<NNN>
|
|
236
|
-
count=$(ls docs/risks/R*-append-test.active.md 2>/dev/null | wc -l | tr -d ' ')
|
|
237
|
-
[ "$count" = "1" ]
|
|
238
|
-
# Both reports cited
|
|
239
|
-
grep -q "first.md" "$entry"
|
|
240
|
-
grep -q "second.md" "$entry"
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
244
|
-
# README generation
|
|
245
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
246
|
-
|
|
247
|
-
@test "README is regenerated with Register table containing the new entry" {
|
|
248
|
-
mkdir -p .risk-reports
|
|
249
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
250
|
-
RISK_REGISTER_HINT:
|
|
251
|
-
- above-appetite-residual | readme-test | README inclusion check
|
|
252
|
-
EOF
|
|
253
|
-
run "$SCRIPT"
|
|
254
|
-
[ "$status" -eq 0 ]
|
|
255
|
-
[ -f docs/risks/README.md ]
|
|
256
|
-
grep -q "Risk Register" docs/risks/README.md
|
|
257
|
-
grep -q "readme-test" docs/risks/README.md
|
|
258
|
-
# README cites ADR-059
|
|
259
|
-
grep -q "ADR-059" docs/risks/README.md
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
@test "emitted README does NOT leak the publishing-suite brand name (P374 published-artefacts)" {
|
|
263
|
-
# The README heredoc must use project-neutral phrasing so an adopter's
|
|
264
|
-
# generated docs/risks/README.md never carries the source monorepo's
|
|
265
|
-
# brand into adopter-controlled prose. Witness substring from #273.
|
|
266
|
-
mkdir -p .risk-reports
|
|
267
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
268
|
-
RISK_REGISTER_HINT:
|
|
269
|
-
- above-appetite-residual | brand-leak-test | Brand-leak absence check
|
|
270
|
-
EOF
|
|
271
|
-
run "$SCRIPT"
|
|
272
|
-
[ "$status" -eq 0 ]
|
|
273
|
-
[ -f docs/risks/README.md ]
|
|
274
|
-
! grep -q "Windy Road Agent Plugins suite" docs/risks/README.md
|
|
275
|
-
# Positive assertion: the neutral phrasing is present
|
|
276
|
-
grep -q "persistent risk register\*\* for this project" docs/risks/README.md
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
@test "README does NOT cite TEMPLATE.md (per user direction 2026-05-04)" {
|
|
280
|
-
mkdir -p .risk-reports
|
|
281
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
282
|
-
RISK_REGISTER_HINT:
|
|
283
|
-
- above-appetite-residual | nt | No TEMPLATE check
|
|
284
|
-
EOF
|
|
285
|
-
run "$SCRIPT"
|
|
286
|
-
[ "$status" -eq 0 ]
|
|
287
|
-
! grep -q "TEMPLATE.md" docs/risks/README.md
|
|
288
|
-
! [ -f docs/risks/TEMPLATE.md ]
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
292
|
-
# Dry-run mode
|
|
293
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
294
|
-
|
|
295
|
-
@test "dry-run does not write any files" {
|
|
296
|
-
mkdir -p .risk-reports
|
|
297
|
-
cat > .risk-reports/r.md <<'EOF'
|
|
298
|
-
RISK_REGISTER_HINT:
|
|
299
|
-
- above-appetite-residual | dry-run-test | Dry-run check
|
|
300
|
-
EOF
|
|
301
|
-
run "$SCRIPT" --dry-run
|
|
302
|
-
[ "$status" -eq 0 ]
|
|
303
|
-
! [ -d docs/risks ]
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
@test "dry-run reports hinted vs unhinted counts" {
|
|
307
|
-
mkdir -p .risk-reports
|
|
308
|
-
cat > .risk-reports/hinted.md <<'EOF'
|
|
309
|
-
RISK_REGISTER_HINT:
|
|
310
|
-
- above-appetite-residual | hinted | Hinted
|
|
311
|
-
EOF
|
|
312
|
-
echo "no hint here" > .risk-reports/unhinted.md
|
|
313
|
-
run "$SCRIPT" --dry-run
|
|
314
|
-
[ "$status" -eq 0 ]
|
|
315
|
-
[[ "$output" == *"reports walked: 2"* ]]
|
|
316
|
-
[[ "$output" == *"hinted (deterministic): 1"* ]]
|
|
317
|
-
[[ "$output" == *"unhinted (Phase 2 todo): 1"* ]]
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
321
|
-
# --derived-slugs (Phase 2 input)
|
|
322
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
323
|
-
|
|
324
|
-
@test "--derived-slugs adds entries from Phase 2 LLM-walk output" {
|
|
325
|
-
mkdir -p .risk-reports
|
|
326
|
-
echo "no hint" > .risk-reports/unhinted.md
|
|
327
|
-
# Simulate Phase 2 LLM-walk producing a derived slug
|
|
328
|
-
printf 'derived-slug\t1\tabove-appetite-residual\tDerived from LLM walk of unhinted.md\t.risk-reports/unhinted.md\n' > /tmp/derived.tsv
|
|
329
|
-
run "$SCRIPT" --derived-slugs /tmp/derived.tsv
|
|
330
|
-
[ "$status" -eq 0 ]
|
|
331
|
-
ls docs/risks/R*-derived-slug.active.md >/dev/null 2>&1
|
|
332
|
-
rm -f /tmp/derived.tsv
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
336
|
-
# Octal-eval regression (P164 Phase 2 — #273 witness)
|
|
337
|
-
# ──────────────────────────────────────────────────────────────────────────────
|
|
338
|
-
# Bash arithmetic $(( ... )) parses a leading-zero operand as octal; 008/009 are
|
|
339
|
-
# invalid octal literals → `bash: 008: value too great for base`. The next-ID
|
|
340
|
-
# compute (LOCAL_MAX from zero-padded R<NNN> filenames) must force base-10 with
|
|
341
|
-
# 10#. Pre-seed an R008 entry so LOCAL_MAX="008" and assert the script allocates
|
|
342
|
-
# R009 cleanly without the octal error.
|
|
343
|
-
|
|
344
|
-
@test "allocates R009 cleanly when an R008 entry already exists (octal-eval regression, P164 Phase 2)" {
|
|
345
|
-
mkdir -p docs/risks
|
|
346
|
-
# Pre-existing zero-padded entry that drives LOCAL_MAX="008". Carries the
|
|
347
|
-
# full field shape the README generator reads (Category/Status/Curation) so
|
|
348
|
-
# the regression isolates the octal-eval boundary, not fixture malformation.
|
|
349
|
-
cat > docs/risks/R008-pre-existing.active.md <<'EOF'
|
|
350
|
-
# Risk R008: Pre Existing
|
|
351
|
-
**Status**: Active
|
|
352
|
-
**Category**: operational
|
|
353
|
-
**Curation**: (human-curated)
|
|
354
|
-
EOF
|
|
355
|
-
mkdir -p .risk-reports
|
|
356
|
-
cat > .risk-reports/octal.md <<'EOF'
|
|
357
|
-
RISK_REGISTER_HINT:
|
|
358
|
-
- above-appetite-residual | octal-boundary-slug | New risk past the 008 boundary
|
|
359
|
-
EOF
|
|
360
|
-
run "$SCRIPT"
|
|
361
|
-
[ "$status" -eq 0 ]
|
|
362
|
-
# No octal-base error in output
|
|
363
|
-
[[ "$output" != *"value too great for base"* ]]
|
|
364
|
-
# New entry allocated as R009, not a corrupted/duplicate ID
|
|
365
|
-
[ -f docs/risks/R009-octal-boundary-slug.active.md ]
|
|
366
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
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
|
-
}
|