@windyroad/itil 0.47.3 → 0.47.4-preview.539
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/package.json
CHANGED
|
@@ -75,23 +75,78 @@ if [ -z "$DRIFT_IDS" ]; then
|
|
|
75
75
|
exit 2
|
|
76
76
|
fi
|
|
77
77
|
|
|
78
|
-
# ── Build set of IDs covered by
|
|
79
|
-
# `git status --porcelain` v1 emits rename
|
|
80
|
-
#
|
|
81
|
-
# RM
|
|
78
|
+
# ── Build set of IDs covered by in-flight ticket renames in the working tree
|
|
79
|
+
# `git status --porcelain` v1 emits same-session rename evidence in two shapes:
|
|
80
|
+
#
|
|
81
|
+
# 1. R / RM rename entries (git's rename-detection matched the old + new path)
|
|
82
|
+
# R <old-path> -> <new-path>
|
|
83
|
+
# RM <old-path> -> <new-path> (rename + unstaged modification)
|
|
84
|
+
#
|
|
85
|
+
# 2. Same-ID D + (A or ??) pair (substantial-body edit defeated rename-
|
|
86
|
+
# detection so the move renders as delete + add for the same ticket ID
|
|
87
|
+
# — P306). We treat such a pair as equivalent to an R entry for drift-
|
|
88
|
+
# coverage purposes: the in-flow P094/P062 refresh will reconcile the
|
|
89
|
+
# README in the upcoming commit per ADR-014.
|
|
90
|
+
#
|
|
82
91
|
# We match the destination path's ticket ID — the post-rename status is what
|
|
83
|
-
# the in-flow
|
|
92
|
+
# the in-flow refresh will reconcile.
|
|
84
93
|
RENAMED_IDS=""
|
|
85
94
|
if git rev-parse --git-dir >/dev/null 2>&1; then
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
# `-u` (= `--untracked-files=all`) expands untracked directories so the
|
|
96
|
+
# `??` side of a D + `??` rename pair lists individual files, not the
|
|
97
|
+
# collapsed parent directory.
|
|
98
|
+
STATUS_LINES="$(git status --porcelain -u "$PROBLEMS_DIR" 2>/dev/null)"
|
|
99
|
+
|
|
100
|
+
# Shape 1: R / RM rename entries — extract destination-path ticket ID.
|
|
101
|
+
R_IDS="$(
|
|
102
|
+
printf '%s\n' "$STATUS_LINES" \
|
|
88
103
|
| awk '/^R/' \
|
|
89
104
|
| sed 's|.*-> ||' \
|
|
90
105
|
| sed "s|^${PROBLEMS_DIR}/||" \
|
|
91
106
|
| grep -oE '^[0-9]{3}' \
|
|
107
|
+
| awk '{ printf "P%s\n", $0 }'
|
|
108
|
+
)"
|
|
109
|
+
|
|
110
|
+
# Shape 2: same-ID D + (A or ??) pair — intersect deleted-IDs with added-
|
|
111
|
+
# IDs. `git status --porcelain` emits one entry per file:
|
|
112
|
+
# ` D <path>` staged delete (note: column 1 is space, column 2 is `D`)
|
|
113
|
+
# `D <path>` staged delete (alternate form when also touched in index)
|
|
114
|
+
# `A <path>` staged add
|
|
115
|
+
# `?? <path>` untracked add
|
|
116
|
+
# We treat any 3-char prefix line whose 1st-or-2nd column is `D`/`A`/`?`
|
|
117
|
+
# as candidate, then extract the leading `<NNN>` from the basename.
|
|
118
|
+
DELETED_IDS="$(
|
|
119
|
+
printf '%s\n' "$STATUS_LINES" \
|
|
120
|
+
| awk 'substr($0,1,2) ~ /D / || substr($0,1,2) ~ /^ D/' \
|
|
121
|
+
| sed 's|^...||' \
|
|
122
|
+
| sed "s|^${PROBLEMS_DIR}/||" \
|
|
123
|
+
| grep -oE '^[0-9]{3}' \
|
|
92
124
|
| awk '{ printf "P%s\n", $0 }' \
|
|
93
125
|
| sort -u
|
|
94
126
|
)"
|
|
127
|
+
ADDED_IDS="$(
|
|
128
|
+
printf '%s\n' "$STATUS_LINES" \
|
|
129
|
+
| awk 'substr($0,1,2) ~ /A / || substr($0,1,2) == "??"' \
|
|
130
|
+
| sed 's|^...||' \
|
|
131
|
+
| sed "s|^${PROBLEMS_DIR}/||" \
|
|
132
|
+
| grep -oE '^[0-9]{3}' \
|
|
133
|
+
| awk '{ printf "P%s\n", $0 }' \
|
|
134
|
+
| sort -u
|
|
135
|
+
)"
|
|
136
|
+
DA_PAIR_IDS=""
|
|
137
|
+
if [ -n "$DELETED_IDS" ] && [ -n "$ADDED_IDS" ]; then
|
|
138
|
+
DA_PAIR_IDS="$(
|
|
139
|
+
comm -12 \
|
|
140
|
+
<(printf '%s\n' "$DELETED_IDS") \
|
|
141
|
+
<(printf '%s\n' "$ADDED_IDS")
|
|
142
|
+
)"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
RENAMED_IDS="$(
|
|
146
|
+
{ printf '%s\n' "$R_IDS"; printf '%s\n' "$DA_PAIR_IDS"; } \
|
|
147
|
+
| grep -v '^$' \
|
|
148
|
+
| sort -u
|
|
149
|
+
)"
|
|
95
150
|
fi
|
|
96
151
|
|
|
97
152
|
# ── Cross-reference each drift ID against the renamed set ───────────────────
|
|
@@ -132,6 +132,136 @@ EOF
|
|
|
132
132
|
echo "$output" | grep -q "covered=1"
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
# ── P306: same-ID D+A pair coverage (substantial-body in-flight rename) ─────
|
|
136
|
+
# When `git mv` is followed by a substantial body edit, git's rename-detection
|
|
137
|
+
# may not match the old/new paths and emits a delete+add pair instead of an R
|
|
138
|
+
# entry. The classifier must recognise a same-ID D+A pair as same-session
|
|
139
|
+
# coverage — equivalent to an R/RM entry — and return INLINE_REFRESH.
|
|
140
|
+
|
|
141
|
+
@test "classify-readme-drift: same-ID D+A pair (substantial-body rename) → INLINE_REFRESH" {
|
|
142
|
+
# Seed an open ticket, commit, then `git mv` + substantial-body edit. With
|
|
143
|
+
# rename-detection thresholds, git may emit `D ` + `A ` (or `??`) rather
|
|
144
|
+
# than `R `. We force the D+A shape by writing wholly different content to
|
|
145
|
+
# the destination path.
|
|
146
|
+
cat > docs/problems/306-foo.open.md <<'EOF'
|
|
147
|
+
# Problem 306: Foo
|
|
148
|
+
|
|
149
|
+
**Status**: Open
|
|
150
|
+
|
|
151
|
+
Original body — short.
|
|
152
|
+
EOF
|
|
153
|
+
git add docs/problems/306-foo.open.md
|
|
154
|
+
git commit -q -m "init"
|
|
155
|
+
|
|
156
|
+
# Create the new file at the verifying path with substantially different
|
|
157
|
+
# body BEFORE removing the old path (avoid empty-dir prune). git's
|
|
158
|
+
# rename-detection then sees delete + add as distinct entries rather than
|
|
159
|
+
# an R-rename, because the bodies are wholly different.
|
|
160
|
+
cat > docs/problems/306-foo.verifying.md <<'EOF'
|
|
161
|
+
# Problem 306: Foo
|
|
162
|
+
|
|
163
|
+
**Status**: Verification Pending
|
|
164
|
+
|
|
165
|
+
Wholly rewritten body so git rename-detection does not match the source.
|
|
166
|
+
This is a multi-paragraph substantive rewrite that exercises the D+A path.
|
|
167
|
+
|
|
168
|
+
## Fix Released
|
|
169
|
+
|
|
170
|
+
Deployed in vX.Y.Z. Adds a behavioural fixture that exercises the
|
|
171
|
+
classifier across the D+A coverage gap that P306 captured.
|
|
172
|
+
EOF
|
|
173
|
+
git rm -q docs/problems/306-foo.open.md
|
|
174
|
+
git add docs/problems/306-foo.verifying.md
|
|
175
|
+
|
|
176
|
+
cat > drift.txt <<'EOF'
|
|
177
|
+
DRIFT P306 wsjf-rankings: claims=open actual=verifying
|
|
178
|
+
MISSING P306 verification-queue: actual=verifying
|
|
179
|
+
EOF
|
|
180
|
+
|
|
181
|
+
run "$SCRIPT" drift.txt docs/problems
|
|
182
|
+
[ "$status" -eq 0 ]
|
|
183
|
+
echo "$output" | grep -q "INLINE_REFRESH"
|
|
184
|
+
echo "$output" | grep -q "covered=1"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@test "classify-readme-drift: same-ID D+A pair with untracked add (D + ??) → INLINE_REFRESH" {
|
|
188
|
+
# Variant: the new path is untracked (not yet `git add`-ed). git status
|
|
189
|
+
# emits `D ` for the old path and `??` for the new path. The classifier
|
|
190
|
+
# must still recognise the same-ID pair as same-session coverage.
|
|
191
|
+
cat > docs/problems/307-bar.open.md <<'EOF'
|
|
192
|
+
# Problem 307: Bar
|
|
193
|
+
**Status**: Open
|
|
194
|
+
EOF
|
|
195
|
+
git add docs/problems/307-bar.open.md
|
|
196
|
+
git commit -q -m "init"
|
|
197
|
+
|
|
198
|
+
cat > docs/problems/307-bar.verifying.md <<'EOF'
|
|
199
|
+
# Problem 307: Bar
|
|
200
|
+
|
|
201
|
+
**Status**: Verification Pending
|
|
202
|
+
|
|
203
|
+
Untracked add side of the D+?? pair.
|
|
204
|
+
EOF
|
|
205
|
+
git rm -q docs/problems/307-bar.open.md
|
|
206
|
+
|
|
207
|
+
cat > drift.txt <<'EOF'
|
|
208
|
+
DRIFT P307 wsjf-rankings: claims=open actual=verifying
|
|
209
|
+
EOF
|
|
210
|
+
|
|
211
|
+
run "$SCRIPT" drift.txt docs/problems
|
|
212
|
+
[ "$status" -eq 0 ]
|
|
213
|
+
echo "$output" | grep -q "INLINE_REFRESH"
|
|
214
|
+
echo "$output" | grep -q "covered=1"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@test "classify-readme-drift: D-only (no matching A for same ID) → HALT_ROUTE_RECONCILE" {
|
|
218
|
+
# Negative case: a delete without a corresponding add for the same ID is
|
|
219
|
+
# NOT a rename — it is a genuine deletion. Must HALT.
|
|
220
|
+
cat > docs/problems/308-baz.open.md <<'EOF'
|
|
221
|
+
# Problem 308: Baz
|
|
222
|
+
**Status**: Open
|
|
223
|
+
EOF
|
|
224
|
+
git add docs/problems/308-baz.open.md
|
|
225
|
+
git commit -q -m "init"
|
|
226
|
+
|
|
227
|
+
git rm -q docs/problems/308-baz.open.md
|
|
228
|
+
|
|
229
|
+
cat > drift.txt <<'EOF'
|
|
230
|
+
DRIFT P308 wsjf-rankings: claims=open actual=verifying
|
|
231
|
+
EOF
|
|
232
|
+
|
|
233
|
+
run "$SCRIPT" drift.txt docs/problems
|
|
234
|
+
[ "$status" -eq 1 ]
|
|
235
|
+
echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
|
|
236
|
+
echo "$output" | grep -q "uncovered=1"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@test "classify-readme-drift: mismatched D + A (different IDs) → HALT_ROUTE_RECONCILE" {
|
|
240
|
+
# Negative case: a delete for one ID + an add for a different ID is NOT a
|
|
241
|
+
# rename of either ticket — both are uncovered.
|
|
242
|
+
cat > docs/problems/309-alpha.open.md <<'EOF'
|
|
243
|
+
# Problem 309: Alpha
|
|
244
|
+
**Status**: Open
|
|
245
|
+
EOF
|
|
246
|
+
git add docs/problems/309-alpha.open.md
|
|
247
|
+
git commit -q -m "init"
|
|
248
|
+
|
|
249
|
+
cat > docs/problems/310-beta.open.md <<'EOF'
|
|
250
|
+
# Problem 310: Beta
|
|
251
|
+
**Status**: Open
|
|
252
|
+
EOF
|
|
253
|
+
git rm -q docs/problems/309-alpha.open.md
|
|
254
|
+
git add docs/problems/310-beta.open.md
|
|
255
|
+
|
|
256
|
+
cat > drift.txt <<'EOF'
|
|
257
|
+
DRIFT P309 wsjf-rankings: claims=open actual=verifying
|
|
258
|
+
EOF
|
|
259
|
+
|
|
260
|
+
run "$SCRIPT" drift.txt docs/problems
|
|
261
|
+
[ "$status" -eq 1 ]
|
|
262
|
+
echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
|
|
263
|
+
}
|
|
264
|
+
|
|
135
265
|
# ── Exit 1 (HALT_ROUTE_RECONCILE): committed cross-session drift ────────────
|
|
136
266
|
|
|
137
267
|
@test "classify-readme-drift: single drift ID not covered by any rename → HALT_ROUTE_RECONCILE" {
|