@windyroad/itil 0.55.5 → 0.56.0-preview.876

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.
@@ -497,5 +497,5 @@
497
497
  }
498
498
  },
499
499
  "name": "wr-itil",
500
- "version": "0.55.5"
500
+ "version": "0.56.0"
501
501
  }
@@ -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/check-rfc-has-stories.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/check-rfc-has-stories.sh" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.55.5",
3
+ "version": "0.56.0-preview.876",
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"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ # check-rfc-has-stories.sh — ADR-089 (every RFC has at least one story).
3
+ #
4
+ # The load-bearing detection half of the manage-rfc `proposed -> accepted` gate:
5
+ # an RFC must list >=1 story before it can be accepted. Exits non-zero (with a
6
+ # stderr directive) when the RFC's `stories:` frontmatter is empty (`stories: []`)
7
+ # or missing; exits 0 when >=1 `STORY-NNN` is listed (inline or block-list).
8
+ #
9
+ # This removes the pre-ADR-089 empty-stories fallback (the "atomic RFC ships with
10
+ # stories: [] / JTBD-101 friction guard" shape). An atomic fix is now an RFC with
11
+ # exactly one full story, never an empty list.
12
+ #
13
+ # Usage: check-rfc-has-stories.sh <rfc-file>
14
+ # Exit: 0 = >=1 story; 1 = empty/missing stories; 2 = usage / file error.
15
+ #
16
+ # Authority: ADR-089. Driver: P404. Behavioural test: check-rfc-has-stories.bats.
17
+ set -euo pipefail
18
+
19
+ rfc="${1:-}"
20
+ if [ -z "$rfc" ]; then
21
+ echo "check-rfc-has-stories: usage: check-rfc-has-stories.sh <rfc-file>" >&2
22
+ exit 2
23
+ fi
24
+ if [ ! -f "$rfc" ]; then
25
+ echo "check-rfc-has-stories: file not found: $rfc" >&2
26
+ exit 2
27
+ fi
28
+
29
+ # The frontmatter `stories:` line (first match; frontmatter is at the top).
30
+ stories_line="$(awk '/^stories:/{print; exit}' "$rfc")"
31
+
32
+ if [ -z "$stories_line" ]; then
33
+ echo "check-rfc-has-stories: $rfc has no 'stories:' field — an RFC must list >=1 story (ADR-089). Decompose the fix into >=1 story before accepting." >&2
34
+ exit 1
35
+ fi
36
+
37
+ # Reject an explicit empty inline list: `stories: []`.
38
+ if printf '%s' "$stories_line" | grep -qE '^stories:[[:space:]]*\[[[:space:]]*\][[:space:]]*$'; then
39
+ echo "check-rfc-has-stories: $rfc has empty 'stories: []' — an RFC must list >=1 story (ADR-089; the empty-stories fallback is removed). An atomic fix is an RFC with exactly one full story." >&2
40
+ exit 1
41
+ fi
42
+
43
+ # Accept if a STORY-id appears inline on the stories: line.
44
+ if printf '%s' "$stories_line" | grep -qE 'STORY-[0-9]'; then
45
+ exit 0
46
+ fi
47
+
48
+ # Accept if a YAML block-list under stories: carries >=1 STORY-id.
49
+ if awk '
50
+ /^stories:/ { inblock=1; next }
51
+ inblock && /^[[:space:]]*-[[:space:]]*STORY-[0-9]/ { found=1 }
52
+ inblock && /^[^[:space:]-]/ { exit }
53
+ END { exit !found }
54
+ ' "$rfc"; then
55
+ exit 0
56
+ fi
57
+
58
+ echo "check-rfc-has-stories: $rfc 'stories:' lists no STORY- ids — an RFC must list >=1 story (ADR-089)." >&2
59
+ exit 1
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural test for check-rfc-has-stories.sh (ADR-089 — every RFC has >=1 story).
3
+ # The predicate exits non-zero when an RFC's `stories:` frontmatter is empty or
4
+ # missing (the empty-stories fallback ADR-089 removes); exit 0 when >=1 story is
5
+ # listed. This is the load-bearing detection half of the proposed->accepted gate.
6
+ #
7
+ # @adr ADR-089 (every RFC has at least one story)
8
+ # @problem P404 (implement ADR-089 + ADR-090)
9
+
10
+ setup() {
11
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
12
+ SCRIPT="${REPO_ROOT}/packages/itil/scripts/check-rfc-has-stories.sh"
13
+ TMPD="$(mktemp -d)"
14
+ }
15
+ teardown() { rm -rf "$TMPD"; }
16
+
17
+ @test "check-rfc-has-stories: empty stories: [] is REJECTED (exit non-zero + directive)" {
18
+ cat > "$TMPD/RFC-901-empty.proposed.md" <<'EOF'
19
+ ---
20
+ status: proposed
21
+ problems: [P170]
22
+ stories: []
23
+ ---
24
+ # RFC-901: empty
25
+ EOF
26
+ run bash "$SCRIPT" "$TMPD/RFC-901-empty.proposed.md"
27
+ [ "$status" -ne 0 ]
28
+ [[ "$output" == *"stories"* ]]
29
+ }
30
+
31
+ @test "check-rfc-has-stories: missing stories: field is REJECTED" {
32
+ cat > "$TMPD/RFC-902-none.proposed.md" <<'EOF'
33
+ ---
34
+ status: proposed
35
+ problems: [P170]
36
+ ---
37
+ # RFC-902: no stories field
38
+ EOF
39
+ run bash "$SCRIPT" "$TMPD/RFC-902-none.proposed.md"
40
+ [ "$status" -ne 0 ]
41
+ [[ "$output" == *"stories"* ]]
42
+ }
43
+
44
+ @test "check-rfc-has-stories: inline >=1 story is ACCEPTED (exit 0)" {
45
+ cat > "$TMPD/RFC-903-ok.proposed.md" <<'EOF'
46
+ ---
47
+ status: proposed
48
+ problems: [P170]
49
+ stories: [STORY-020, STORY-021]
50
+ ---
51
+ # RFC-903: has stories
52
+ EOF
53
+ run bash "$SCRIPT" "$TMPD/RFC-903-ok.proposed.md"
54
+ [ "$status" -eq 0 ]
55
+ }
56
+
57
+ @test "check-rfc-has-stories: block-list >=1 story is ACCEPTED (exit 0)" {
58
+ cat > "$TMPD/RFC-904-block.proposed.md" <<'EOF'
59
+ ---
60
+ status: proposed
61
+ problems: [P170]
62
+ stories:
63
+ - STORY-020
64
+ ---
65
+ # RFC-904: block-list stories
66
+ EOF
67
+ run bash "$SCRIPT" "$TMPD/RFC-904-block.proposed.md"
68
+ [ "$status" -eq 0 ]
69
+ }
@@ -118,7 +118,7 @@ Each transition is a `git mv` + Edit + `git add` (P057 staging trap) + README re
118
118
 
119
119
  | Transition | Checks |
120
120
  |------------|--------|
121
- | `proposed → accepted` | All `problems:` entries resolve. Architect re-review PASS. JTBD re-review PASS. `## Scope` populated (not "deferred"). `## Tasks` decomposed (≥ 1 task; not just deferred placeholder). |
121
+ | `proposed → accepted` | All `problems:` entries resolve. Architect re-review PASS. JTBD re-review PASS. `## Scope` populated (not "deferred"). `## Tasks` decomposed (≥ 1 task; not just deferred placeholder). **`stories:` non-empty — run `wr-itil-check-rfc-has-stories <rfc-file>`; hard-block on non-zero exit (empty `stories: []` or missing).** Per **ADR-089** the empty-stories fallback is removed: an atomic fix is an RFC with exactly one full story; an empty `stories:` is permitted only on a `proposed`/draft RFC before the fix is scoped, never at `accepted`. |
122
122
  | `accepted → in-progress` | First commit referencing `Refs: RFC-<NNN>` exists OR is being authored in this same commit. I1 hard-block on missing/orphan trace. |
123
123
  | `in-progress → verifying` | All `## Tasks` checked. `## Verification` section drafted (release marker, user-side check, trace closure path). I1 hard-block on missing/orphan trace. |
124
124
  | `verifying → closed` | User explicitly confirms (or AFK Rule 6 fallback for evidence-based close per ADR-022 / ADR-044 framework-resolved silent dispatch). I1 advisory-with-escalation if driving problems Closed/Parked. |