@windyroad/risk-scorer 0.12.4 → 0.12.5
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/pipeline.md
CHANGED
|
@@ -39,9 +39,18 @@ You receive structured pipeline state context with these sections:
|
|
|
39
39
|
|
|
40
40
|
- **UNCOMMITTED CHANGES**: Diff stat, untracked files, and categories
|
|
41
41
|
- **UNPUSHED CHANGES**: Commits and cumulative diff between remote and HEAD
|
|
42
|
-
- **UNRELEASED CHANGES**:
|
|
42
|
+
- **UNRELEASED CHANGES**: Partitioned changeset counts (Pending vs Queued — see Layer 1 contract below) and cumulative diff
|
|
43
43
|
- **STALE FILES**: Modified files uncommitted for over 24h
|
|
44
44
|
|
|
45
|
+
### Layer 1 changeset partition (P202)
|
|
46
|
+
|
|
47
|
+
The UNRELEASED CHANGES section emits TWO distinct changeset counts:
|
|
48
|
+
|
|
49
|
+
- `Pending changesets (commits unpushed): N` — changesets whose introducing commit is in `origin/<base>..HEAD` (local) OR is untracked. These ARE pending consumer-facing changes at THIS commit's surface and count toward Layer 1 release risk as before.
|
|
50
|
+
- `Queued changesets (commits already on origin): N` — changesets whose introducing commit is already on `origin/<base>`. The underlying code has already been pushed (and in the maintainer pipeline, reviewed); only the release-PR merge to npm is pending. These contribute **zero** release-risk at THIS commit's surface and MUST NOT count as pending consumer-facing changes in Layer 1.
|
|
51
|
+
|
|
52
|
+
When computing Layer 1 release risk, score only the Pending count (plus any unreleased diff content). A Queued count > 0 with Pending = 0 and no other unreleased diff content is a within-appetite state — the queue is awaiting a release-PR merge, not a maintainer decision. Do NOT emit `RISK_REMEDIATIONS:` lines (such as `move-to-holding`) targeting queued-on-origin changesets; their commits have already shipped and `git mv`'ing them into `docs/changesets-holding/` would fragment the release without reducing actual risk.
|
|
53
|
+
|
|
45
54
|
## Catalog Consumption Protocol (ADR-059)
|
|
46
55
|
|
|
47
56
|
Before scoring, READ the standing-risk catalog at `docs/risks/` and filter to risks applicable to THIS action. The catalog is the persistent record of risk classes the project has surfaced; consuming it eliminates the wasted-effort cost of re-deriving risk classes on every assessment AND closes the missed-risk-class hazard (forgetting a class the agent surfaced before because it didn't think of it this time). Per `RISK-POLICY.md` `## Risk Catalog` section.
|
|
@@ -248,10 +248,45 @@ fi
|
|
|
248
248
|
if [ "$SHOW_UNRELEASED" = true ]; then
|
|
249
249
|
echo "=== UNRELEASED CHANGES ==="
|
|
250
250
|
|
|
251
|
-
#
|
|
251
|
+
# Partition changesets by introducing-commit provenance (P202):
|
|
252
|
+
# - Queued: introducing commit is already on origin/<base>; awaiting
|
|
253
|
+
# only the release-PR merge to npm. Zero release-risk at THIS
|
|
254
|
+
# commit's surface — the underlying code has already shipped to
|
|
255
|
+
# origin and (in the maintainer pipeline) been reviewed.
|
|
256
|
+
# - Pending: introducing commit is NOT on origin/<base> (in
|
|
257
|
+
# origin/<base>..HEAD), OR the file is untracked. Counts as a
|
|
258
|
+
# pending consumer-facing change.
|
|
259
|
+
# Detection: for each .changeset/<name>.md (excluding README.md),
|
|
260
|
+
# run `git log <DEFAULT_BRANCH>..HEAD -- <file>`. Non-empty output OR
|
|
261
|
+
# untracked status => Pending. Empty output AND tracked => Queued.
|
|
262
|
+
# When DEFAULT_BRANCH is unset (no origin/main and no origin/master),
|
|
263
|
+
# nothing is on origin so all changesets are Pending.
|
|
252
264
|
CHANGESET_COUNT=0
|
|
265
|
+
QUEUED_COUNT=0
|
|
266
|
+
PENDING_COUNT=0
|
|
253
267
|
if [ -d ".changeset" ]; then
|
|
254
|
-
|
|
268
|
+
CHANGESET_FILES=$(find .changeset -name '*.md' -not -name 'README.md' 2>/dev/null || true)
|
|
269
|
+
if [ -n "$CHANGESET_FILES" ]; then
|
|
270
|
+
CHANGESET_COUNT=$(echo "$CHANGESET_FILES" | wc -l | tr -d ' ')
|
|
271
|
+
while IFS= read -r CS_FILE; do
|
|
272
|
+
[ -z "$CS_FILE" ] && continue
|
|
273
|
+
CS_TRACKED=$(git ls-files --error-unmatch -- "$CS_FILE" 2>/dev/null || true)
|
|
274
|
+
if [ -z "$CS_TRACKED" ]; then
|
|
275
|
+
PENDING_COUNT=$((PENDING_COUNT + 1))
|
|
276
|
+
continue
|
|
277
|
+
fi
|
|
278
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
279
|
+
PENDING_COUNT=$((PENDING_COUNT + 1))
|
|
280
|
+
continue
|
|
281
|
+
fi
|
|
282
|
+
CS_UNPUSHED=$(git log "$DEFAULT_BRANCH"..HEAD --oneline -- "$CS_FILE" 2>/dev/null || true)
|
|
283
|
+
if [ -n "$CS_UNPUSHED" ]; then
|
|
284
|
+
PENDING_COUNT=$((PENDING_COUNT + 1))
|
|
285
|
+
else
|
|
286
|
+
QUEUED_COUNT=$((QUEUED_COUNT + 1))
|
|
287
|
+
fi
|
|
288
|
+
done <<< "$CHANGESET_FILES"
|
|
289
|
+
fi
|
|
255
290
|
fi
|
|
256
291
|
|
|
257
292
|
# Check if origin/publish exists
|
|
@@ -269,7 +304,8 @@ if [ "$SHOW_UNRELEASED" = true ]; then
|
|
|
269
304
|
echo "No unreleased changes."
|
|
270
305
|
else
|
|
271
306
|
if [ "$CHANGESET_COUNT" -gt 0 ]; then
|
|
272
|
-
echo "Pending changesets: ${
|
|
307
|
+
echo "Pending changesets (commits unpushed): ${PENDING_COUNT}"
|
|
308
|
+
echo "Queued changesets (commits already on origin): ${QUEUED_COUNT}"
|
|
273
309
|
fi
|
|
274
310
|
if [ -n "$UNRELEASED_STAT" ]; then
|
|
275
311
|
echo "Accumulated unreleased diff (origin/publish..$DEFAULT_BRANCH):"
|
|
@@ -296,7 +332,8 @@ if [ "$SHOW_UNRELEASED" = true ]; then
|
|
|
296
332
|
else
|
|
297
333
|
echo "No publish branch (origin/publish not found)."
|
|
298
334
|
if [ "$CHANGESET_COUNT" -gt 0 ]; then
|
|
299
|
-
echo "Pending changesets: ${
|
|
335
|
+
echo "Pending changesets (commits unpushed): ${PENDING_COUNT}"
|
|
336
|
+
echo "Queued changesets (commits already on origin): ${QUEUED_COUNT}"
|
|
300
337
|
fi
|
|
301
338
|
fi
|
|
302
339
|
echo ""
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Behavioural coverage for pipeline-state.sh --unreleased changeset partition
|
|
3
|
+
# per P202: a changeset whose introducing commit is already on origin/<base>
|
|
4
|
+
# is awaiting only the release-PR merge to npm and must NOT count as a
|
|
5
|
+
# "pending consumer-facing change" at this commit's surface.
|
|
6
|
+
#
|
|
7
|
+
# Per ADR-052 (behavioural tests default) — fixture seeds two changesets
|
|
8
|
+
# with distinct commit provenance and asserts pipeline-state.sh emits
|
|
9
|
+
# distinct counts so the pipeline.md Layer-1 scoring contract can rely
|
|
10
|
+
# on the partition without re-deriving it.
|
|
11
|
+
|
|
12
|
+
setup() {
|
|
13
|
+
HOOKS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
14
|
+
SCRIPT="$HOOKS_DIR/lib/pipeline-state.sh"
|
|
15
|
+
|
|
16
|
+
TEST_DIR="$(mktemp -d)"
|
|
17
|
+
REMOTE_DIR="$(mktemp -d)"
|
|
18
|
+
|
|
19
|
+
(cd "$REMOTE_DIR" && git init --bare --initial-branch=main >/dev/null 2>&1)
|
|
20
|
+
|
|
21
|
+
cd "$TEST_DIR"
|
|
22
|
+
git init --initial-branch=main >/dev/null 2>&1
|
|
23
|
+
git config user.email "test@example.com"
|
|
24
|
+
git config user.name "Test"
|
|
25
|
+
git remote add origin "$REMOTE_DIR"
|
|
26
|
+
|
|
27
|
+
echo "initial" > README.md
|
|
28
|
+
git add README.md
|
|
29
|
+
git commit -m "initial" >/dev/null 2>&1
|
|
30
|
+
git push -u origin main >/dev/null 2>&1
|
|
31
|
+
|
|
32
|
+
mkdir -p .changeset
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
teardown() {
|
|
36
|
+
cd /
|
|
37
|
+
rm -rf "$TEST_DIR" "$REMOTE_DIR"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Helper: seed a changeset, commit it, and push to origin (queued state).
|
|
41
|
+
seed_queued_changeset() {
|
|
42
|
+
local name="$1"
|
|
43
|
+
cat > ".changeset/${name}.md" <<EOF
|
|
44
|
+
---
|
|
45
|
+
"@windyroad/itil": patch
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
${name} changeset body.
|
|
49
|
+
EOF
|
|
50
|
+
git add ".changeset/${name}.md"
|
|
51
|
+
git commit -m "add changeset ${name}" >/dev/null 2>&1
|
|
52
|
+
git push origin main >/dev/null 2>&1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Helper: seed a changeset and commit it locally without pushing (pending state).
|
|
56
|
+
seed_pending_changeset() {
|
|
57
|
+
local name="$1"
|
|
58
|
+
cat > ".changeset/${name}.md" <<EOF
|
|
59
|
+
---
|
|
60
|
+
"@windyroad/itil": patch
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
${name} changeset body.
|
|
64
|
+
EOF
|
|
65
|
+
git add ".changeset/${name}.md"
|
|
66
|
+
git commit -m "add changeset ${name}" >/dev/null 2>&1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Helper: seed a changeset file but leave it untracked (uncommitted-pending state).
|
|
70
|
+
seed_untracked_changeset() {
|
|
71
|
+
local name="$1"
|
|
72
|
+
cat > ".changeset/${name}.md" <<EOF
|
|
73
|
+
---
|
|
74
|
+
"@windyroad/itil": patch
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
${name} changeset body.
|
|
78
|
+
EOF
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@test "emits queued+pending breakdown when changesets straddle origin" {
|
|
82
|
+
cd "$TEST_DIR"
|
|
83
|
+
seed_queued_changeset queued-one
|
|
84
|
+
seed_pending_changeset pending-one
|
|
85
|
+
|
|
86
|
+
run bash "$SCRIPT" --unreleased
|
|
87
|
+
[ "$status" -eq 0 ]
|
|
88
|
+
|
|
89
|
+
echo "$output" | grep -q "Queued changesets (commits already on origin): 1" || {
|
|
90
|
+
echo "Missing 'Queued changesets (commits already on origin): 1' line."
|
|
91
|
+
echo "Output was:"
|
|
92
|
+
echo "$output"
|
|
93
|
+
return 1
|
|
94
|
+
}
|
|
95
|
+
echo "$output" | grep -q "Pending changesets (commits unpushed): 1" || {
|
|
96
|
+
echo "Missing 'Pending changesets (commits unpushed): 1' line."
|
|
97
|
+
echo "Output was:"
|
|
98
|
+
echo "$output"
|
|
99
|
+
return 1
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@test "all-on-origin scenario: Queued > 0, Pending = 0" {
|
|
104
|
+
cd "$TEST_DIR"
|
|
105
|
+
seed_queued_changeset on-origin-a
|
|
106
|
+
seed_queued_changeset on-origin-b
|
|
107
|
+
|
|
108
|
+
run bash "$SCRIPT" --unreleased
|
|
109
|
+
[ "$status" -eq 0 ]
|
|
110
|
+
|
|
111
|
+
echo "$output" | grep -q "Queued changesets (commits already on origin): 2" || {
|
|
112
|
+
echo "Missing 'Queued changesets (commits already on origin): 2' line."
|
|
113
|
+
echo "Output was:"
|
|
114
|
+
echo "$output"
|
|
115
|
+
return 1
|
|
116
|
+
}
|
|
117
|
+
echo "$output" | grep -q "Pending changesets (commits unpushed): 0" || {
|
|
118
|
+
echo "Missing 'Pending changesets (commits unpushed): 0' line."
|
|
119
|
+
echo "Output was:"
|
|
120
|
+
echo "$output"
|
|
121
|
+
return 1
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@test "all-local scenario: Pending > 0, Queued = 0" {
|
|
126
|
+
cd "$TEST_DIR"
|
|
127
|
+
seed_pending_changeset local-a
|
|
128
|
+
seed_pending_changeset local-b
|
|
129
|
+
|
|
130
|
+
run bash "$SCRIPT" --unreleased
|
|
131
|
+
[ "$status" -eq 0 ]
|
|
132
|
+
|
|
133
|
+
echo "$output" | grep -q "Pending changesets (commits unpushed): 2" || {
|
|
134
|
+
echo "Missing 'Pending changesets (commits unpushed): 2' line."
|
|
135
|
+
echo "Output was:"
|
|
136
|
+
echo "$output"
|
|
137
|
+
return 1
|
|
138
|
+
}
|
|
139
|
+
echo "$output" | grep -q "Queued changesets (commits already on origin): 0" || {
|
|
140
|
+
echo "Missing 'Queued changesets (commits already on origin): 0' line."
|
|
141
|
+
echo "Output was:"
|
|
142
|
+
echo "$output"
|
|
143
|
+
return 1
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@test "untracked changeset counts as pending" {
|
|
148
|
+
cd "$TEST_DIR"
|
|
149
|
+
seed_untracked_changeset draft-one
|
|
150
|
+
|
|
151
|
+
run bash "$SCRIPT" --unreleased
|
|
152
|
+
[ "$status" -eq 0 ]
|
|
153
|
+
|
|
154
|
+
echo "$output" | grep -q "Pending changesets (commits unpushed): 1" || {
|
|
155
|
+
echo "Untracked changeset should count as pending."
|
|
156
|
+
echo "Output was:"
|
|
157
|
+
echo "$output"
|
|
158
|
+
return 1
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@test "no changesets emits no breakdown lines" {
|
|
163
|
+
cd "$TEST_DIR"
|
|
164
|
+
# .changeset dir exists but is empty (setup creates it).
|
|
165
|
+
|
|
166
|
+
run bash "$SCRIPT" --unreleased
|
|
167
|
+
[ "$status" -eq 0 ]
|
|
168
|
+
|
|
169
|
+
! echo "$output" | grep -q "Queued changesets" || {
|
|
170
|
+
echo "Should not emit Queued line when no changesets present."
|
|
171
|
+
echo "Output was:"
|
|
172
|
+
echo "$output"
|
|
173
|
+
return 1
|
|
174
|
+
}
|
|
175
|
+
! echo "$output" | grep -q "Pending changesets" || {
|
|
176
|
+
echo "Should not emit Pending line when no changesets present."
|
|
177
|
+
echo "Output was:"
|
|
178
|
+
echo "$output"
|
|
179
|
+
return 1
|
|
180
|
+
}
|
|
181
|
+
}
|