@windyroad/itil 0.32.1 → 0.32.2

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "wr-itil",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "description": "ITIL-aligned IT service management for Claude Code"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "description": "ITIL-aligned IT service management for Claude Code (problem, and future incident/change skills)",
5
5
  "bin": {
6
6
  "windyroad-itil": "./bin/install.mjs"
@@ -36,10 +36,16 @@
36
36
  #
37
37
  # @problem P118
38
38
  # @problem P170 (RFC-002 — dual-tolerant migration window)
39
+ # @problem P252 (Inbound Upstream Reports rows mis-attributed to VQ)
39
40
  # @adr ADR-014 (Reconciliation as preflight robustness layer)
40
41
  # @adr ADR-022 (Verification Pending lifecycle excludes from WSJF Rankings)
41
42
  # @adr ADR-031 (Per-state subdir is post-migration authoritative state signal)
42
43
  # @adr ADR-038 (Progressive disclosure — per-row byte budget)
44
+ # @adr ADR-062 (Inbound upstream-report discovery + assessment pipeline)
45
+ # @rfc RFC-004 (P079 inbound-upstream-report discovery)
46
+ # @jtbd JTBD-006 (Progress the Backlog While I'm Away)
47
+ # @jtbd JTBD-001 (Enforce Governance Without Slowing Down)
48
+ # @jtbd JTBD-302 (Trust That the README Describes the Plugin I Just Installed)
43
49
 
44
50
  set -uo pipefail
45
51
 
@@ -108,13 +114,21 @@ shopt -u nullglob
108
114
 
109
115
  WSJF_START=$(grep -n '^## WSJF Rankings' "$README" | head -1 | cut -d: -f1)
110
116
  VQ_START=$(grep -n '^## Verification Queue' "$README" | head -1 | cut -d: -f1)
117
+ # Inbound Upstream Reports (ADR-062 / RFC-004) sits between Verification
118
+ # Queue and Closed when populated. Its `Matched local ticket` column
119
+ # carries `| P<NNN> |` cross-refs that look like VQ rows but live in a
120
+ # distinct section. The VQ slice MUST terminate at this header when
121
+ # present, otherwise its rows get miscounted as VQ entries (P252).
122
+ INBOUND_START=$(grep -n '^## Inbound Upstream Reports' "$README" | head -1 | cut -d: -f1)
111
123
  CLOSED_START=$(grep -n '^## Closed' "$README" | head -1 | cut -d: -f1)
112
124
  PARKED_START=$(grep -n '^## Parked' "$README" | head -1 | cut -d: -f1)
113
125
  END_LINE=$(wc -l < "$README")
114
126
 
115
- # Sentinel each end with the next section start (or EOF).
116
- WSJF_END=${VQ_START:-${CLOSED_START:-${PARKED_START:-$END_LINE}}}
117
- VQ_END=${CLOSED_START:-${PARKED_START:-$END_LINE}}
127
+ # Sentinel each end with the next section start (or EOF). The VQ_END
128
+ # cascade prefers INBOUND_START when present so Inbound rows are
129
+ # excluded from VQ extraction (P252).
130
+ WSJF_END=${VQ_START:-${INBOUND_START:-${CLOSED_START:-${PARKED_START:-$END_LINE}}}}
131
+ VQ_END=${INBOUND_START:-${CLOSED_START:-${PARKED_START:-$END_LINE}}}
118
132
  CLOSED_END=${PARKED_START:-$END_LINE}
119
133
  PARKED_END=$END_LINE
120
134
 
@@ -860,3 +860,148 @@ EOF
860
860
  run "$SCRIPT" "$FIXTURE_DIR"
861
861
  [ "$status" -eq 0 ]
862
862
  }
863
+
864
+ # ── P252: Inbound Upstream Reports section excluded from VQ slice ───────────
865
+ #
866
+ # Contract: when `docs/problems/README.md` carries a `## Inbound Upstream
867
+ # Reports` section (per ADR-062 + RFC-004), its rows MUST NOT be counted as
868
+ # Verification Queue rows. Pre-fix the script slices VQ as
869
+ # `[VQ_START, CLOSED_START)`, which swallows the entire Inbound table and
870
+ # mis-attributes its `| P<NNN> |` cross-ref cells to the VQ. Post-fix the
871
+ # VQ slice terminates at `## Inbound Upstream Reports` when present.
872
+ #
873
+ # Behavioural assertion shape: reconcile against a README that has VQ
874
+ # matching filesystem ground truth AND an Inbound Upstream Reports table
875
+ # carrying cross-refs to Open tickets → exit 0. Pre-fix this would emit
876
+ # 31-ish STALE entries. Post-fix it emits none.
877
+ #
878
+ # @problem P252
879
+ # @adr ADR-062 (Inbound upstream-report discovery + assessment pipeline)
880
+ # @rfc RFC-004 (P079 inbound-upstream-report discovery)
881
+ # @jtbd JTBD-006 (Progress the Backlog While I'm Away)
882
+ # @jtbd JTBD-302 (Trust That the README Describes the Plugin I Just Installed)
883
+
884
+ @test "reconcile-readme P252: Inbound Upstream Reports cross-refs are NOT counted as Verification Queue rows" {
885
+ # Two Open tickets cross-referenced as inbound-mirror entries. Pre-fix
886
+ # the script slices VQ as [VQ_START, CLOSED_START), which swallows the
887
+ # Inbound table — both P-IDs get reported as STALE verification-queue.
888
+ # Post-fix the VQ slice terminates at Inbound; both IDs are correctly
889
+ # recognised as Open (their .open.md files exist) and no drift fires.
890
+ cat > "$FIXTURE_DIR/198-foo.open.md" <<EOF
891
+ **Status**: Open
892
+ EOF
893
+ cat > "$FIXTURE_DIR/199-bar.open.md" <<EOF
894
+ **Status**: Open
895
+ EOF
896
+ cat > "$FIXTURE_DIR/README.md" <<'EOF'
897
+ ## WSJF Rankings
898
+
899
+ | WSJF | ID | Title | Severity | Status | Effort |
900
+ |------|-----|-------|----------|--------|--------|
901
+ | 5.0 | P198 | Foo | 12 High | Open | M |
902
+ | 4.0 | P199 | Bar | 12 High | Open | M |
903
+
904
+ ## Verification Queue
905
+
906
+ | ID | Title | Released | Likely verified? |
907
+ |----|-------|----------|------------------|
908
+
909
+ ## Inbound Upstream Reports
910
+
911
+ Inbound mirrors discovered via the discovery pipeline (ADR-062 / RFC-004).
912
+ The `Matched local ticket` column carries cross-refs that look like VQ rows
913
+ but live in a different section and MUST NOT be parsed as VQ entries.
914
+
915
+ | # | Inbound | Matched local ticket |
916
+ |---|---------|----------------------|
917
+ | 1 | upstream/issues/42 | P198 |
918
+ | 2 | upstream/issues/43 | P199 |
919
+
920
+ ## Closed
921
+
922
+ | ID | Title | Closed via |
923
+ |----|-------|-----------|
924
+ EOF
925
+ run "$SCRIPT" "$FIXTURE_DIR"
926
+ # Clean state — both P-IDs are correctly Open on disk AND in WSJF
927
+ # Rankings; the Inbound section's cross-refs are scoped out.
928
+ [ "$status" -eq 0 ]
929
+ # Defensive: even if exit 0 lands by accident, output must not contain
930
+ # any false-positive STALE entries citing Inbound IDs.
931
+ ! echo "$output" | grep -q "P198 verification-queue"
932
+ ! echo "$output" | grep -q "P199 verification-queue"
933
+ }
934
+
935
+ @test "reconcile-readme P252: real VQ drift surfaces, Inbound cross-refs stay silent" {
936
+ # Mixed fixture: one genuine STALE VQ row (P056 — README lists in VQ,
937
+ # disk has it Closed) + an Inbound section with cross-refs to Open
938
+ # tickets. Drift output MUST cite P056 only; Inbound IDs MUST NOT
939
+ # appear in any verification-queue drift line.
940
+ cat > "$FIXTURE_DIR/056-qux.closed.md" <<EOF
941
+ **Status**: Closed
942
+ EOF
943
+ cat > "$FIXTURE_DIR/198-foo.open.md" <<EOF
944
+ **Status**: Open
945
+ EOF
946
+ cat > "$FIXTURE_DIR/README.md" <<'EOF'
947
+ ## WSJF Rankings
948
+
949
+ | WSJF | ID | Title | Severity | Status | Effort |
950
+ |------|-----|-------|----------|--------|--------|
951
+ | 5.0 | P198 | Foo | 12 High | Open | M |
952
+
953
+ ## Verification Queue
954
+
955
+ | ID | Title | Released | Likely verified? |
956
+ |----|-------|----------|------------------|
957
+ | P056 | Qux | 2026-01-01 | yes |
958
+
959
+ ## Inbound Upstream Reports
960
+
961
+ | # | Inbound | Matched local ticket |
962
+ |---|---------|----------------------|
963
+ | 1 | upstream/issues/42 | P198 |
964
+
965
+ ## Closed
966
+
967
+ | ID | Title | Closed via |
968
+ |----|-------|-----------|
969
+ EOF
970
+ run "$SCRIPT" "$FIXTURE_DIR"
971
+ [ "$status" -eq 1 ]
972
+ # P056 is the genuine drift (in VQ, file is Closed).
973
+ echo "$output" | grep -q "P056"
974
+ # P198 must NOT appear in any verification-queue drift line — it's an
975
+ # Inbound cross-ref, not a VQ row.
976
+ ! echo "$output" | grep -q "P198 verification-queue"
977
+ }
978
+
979
+ @test "reconcile-readme P252: missing Inbound section preserves existing behaviour (back-compat)" {
980
+ # A README that does NOT have an Inbound Upstream Reports section must
981
+ # continue to slice VQ as [VQ_START, CLOSED_START) without regression.
982
+ # This guards against the fix accidentally requiring the Inbound header
983
+ # to exist.
984
+ cat > "$FIXTURE_DIR/056-qux.closed.md" <<EOF
985
+ **Status**: Closed
986
+ EOF
987
+ cat > "$FIXTURE_DIR/README.md" <<'EOF'
988
+ ## WSJF Rankings
989
+
990
+ | WSJF | ID | Title | Severity | Status | Effort |
991
+ |------|-----|-------|----------|--------|--------|
992
+
993
+ ## Verification Queue
994
+
995
+ | ID | Title | Released | Likely verified? |
996
+ |----|-------|----------|------------------|
997
+ | P056 | Qux | 2026-01-01 | yes |
998
+
999
+ ## Closed
1000
+
1001
+ | ID | Title | Closed via |
1002
+ |----|-------|-----------|
1003
+ EOF
1004
+ run "$SCRIPT" "$FIXTURE_DIR"
1005
+ [ "$status" -eq 1 ]
1006
+ echo "$output" | grep -q "P056"
1007
+ }