@windyroad/itil 0.56.0-preview.877 → 0.56.0-preview.879

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.
@@ -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-stories-ratified.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-stories-ratified.sh" "$@"
@@ -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/detect-unratified-stories-maps.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/detect-unratified-stories-maps.sh" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.56.0-preview.877",
3
+ "version": "0.56.0-preview.879",
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,57 @@
1
+ #!/usr/bin/env bash
2
+ # check-rfc-stories-ratified.sh — ADR-090 (an RFC may reference only RATIFIED stories).
3
+ #
4
+ # For each STORY-NNN in the RFC's `stories:` frontmatter, resolve the story file
5
+ # under <stories-root>/*/STORY-NNN-*.md and verify it carries
6
+ # `human-oversight: confirmed`. Exit non-zero (with a stderr directive naming the
7
+ # offending stories) if any listed story is unratified, unconfirmed, or missing.
8
+ #
9
+ # Composes with check-rfc-has-stories.sh (ADR-089): has-stories checks >=1 story
10
+ # exists; this checks each listed story is ratified. An empty `stories:` passes
11
+ # vacuously here (the has-stories gate owns the emptiness rejection).
12
+ #
13
+ # Usage: check-rfc-stories-ratified.sh <rfc-file> [stories-root=docs/stories]
14
+ # Exit: 0 = all listed stories ratified (or none listed); 1 = >=1 unratified/missing;
15
+ # 2 = usage / file error.
16
+ #
17
+ # Authority: ADR-090. Driver: P404 Phase 2. Test: check-rfc-stories-ratified.bats.
18
+ set -euo pipefail
19
+
20
+ rfc="${1:-}"
21
+ stories_root="${2:-docs/stories}"
22
+ if [ -z "$rfc" ]; then
23
+ echo "check-rfc-stories-ratified: usage: check-rfc-stories-ratified.sh <rfc-file> [stories-root]" >&2
24
+ exit 2
25
+ fi
26
+ if [ ! -f "$rfc" ]; then
27
+ echo "check-rfc-stories-ratified: file not found: $rfc" >&2
28
+ exit 2
29
+ fi
30
+
31
+ # Collect STORY-NNN ids from the stories: frontmatter (inline list OR block list).
32
+ stories_line="$(awk '/^stories:/{print; exit}' "$rfc")"
33
+ ids="$(printf '%s' "$stories_line" | grep -oE 'STORY-[0-9]+' || true)"
34
+ if [ -z "$ids" ]; then
35
+ ids="$(awk '/^stories:/{f=1;next} f&&/^[[:space:]]*-/{print} f&&/^[^[:space:]-]/{exit}' "$rfc" | grep -oE 'STORY-[0-9]+' || true)"
36
+ fi
37
+
38
+ # No stories listed → vacuously ratified (has-stories gate owns emptiness).
39
+ [ -z "$ids" ] && exit 0
40
+
41
+ unratified=""
42
+ for id in $ids; do
43
+ f="$(ls "$stories_root"/*/"$id"-*.md 2>/dev/null | head -1 || true)"
44
+ if [ -z "$f" ]; then
45
+ unratified="$unratified $id(missing)"
46
+ continue
47
+ fi
48
+ if ! grep -qE '^human-oversight:[[:space:]]*confirmed([[:space:]]|$)' "$f"; then
49
+ unratified="$unratified $id(unratified)"
50
+ fi
51
+ done
52
+
53
+ if [ -n "$unratified" ]; then
54
+ echo "check-rfc-stories-ratified: $rfc references unratified stories:$unratified — ADR-090: an RFC may reference only ratified (human-oversight: confirmed) stories. Ratify them first." >&2
55
+ exit 1
56
+ fi
57
+ exit 0
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ # detect-unratified-stories-maps.sh — ADR-090 detector.
3
+ #
4
+ # Token-cheap detection of story maps + stories lacking the ratified
5
+ # `human-oversight: confirmed` marker. Mirrors wr-architect detect-unoversighted.sh
6
+ # but spans two artefact types with different marker encodings:
7
+ # - stories: markdown YAML frontmatter `human-oversight: confirmed`
8
+ # - story-maps: HTML meta tag `<meta name="human-oversight" content="confirmed">`
9
+ #
10
+ # ADR-090's marker is DRIFT-INVALIDATED (unlike ADR-066 write-once): an edit
11
+ # re-opens it to `unconfirmed`, so this detector surfaces both the never-ratified
12
+ # (no marker) and the drift-reopened (`unconfirmed`) cases as the drain queue.
13
+ #
14
+ # Usage: detect-unratified-stories-maps.sh [STORIES_DIR=docs/stories] [MAPS_DIR=docs/story-maps]
15
+ # Output: one unratified artefact path per line, sorted. Empty = all confirmed.
16
+ # Always exits 0 (detector, not a gate). Consumed by the work-problems Step 2.4
17
+ # oversight-unconfirmed drain (mirror of the architect/jtbd detectors).
18
+ set -euo pipefail
19
+
20
+ STORIES_DIR="${1:-docs/stories}"
21
+ MAPS_DIR="${2:-docs/story-maps}"
22
+
23
+ shopt -s nullglob
24
+
25
+ {
26
+ # Stories — markdown YAML frontmatter.
27
+ if [ -d "$STORIES_DIR" ]; then
28
+ for f in "$STORIES_DIR"/*.md "$STORIES_DIR"/*/*.md; do
29
+ [ "$(basename "$f")" = "README.md" ] && continue
30
+ fm="$(awk 'NR==1 && $0 != "---" { exit } NR==1 { next } /^---[[:space:]]*$/ { exit } { print }' "$f")"
31
+ printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$' && continue
32
+ echo "$f"
33
+ done
34
+ fi
35
+ # Story maps — HTML meta tag.
36
+ if [ -d "$MAPS_DIR" ]; then
37
+ for f in "$MAPS_DIR"/*.html "$MAPS_DIR"/*/*.html; do
38
+ grep -qiE '<meta[^>]*name="human-oversight"[^>]*content="confirmed"' "$f" && continue
39
+ echo "$f"
40
+ done
41
+ fi
42
+ } | sort
43
+
44
+ exit 0
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural test for check-rfc-stories-ratified.sh (ADR-090 — an RFC may
3
+ # reference only RATIFIED stories). For each STORY-NNN in the RFC's stories:
4
+ # frontmatter, the predicate resolves the story file and verifies
5
+ # human-oversight: confirmed. Composes with check-rfc-has-stories (ADR-089):
6
+ # has-stories checks >=1 exists; this checks each listed one is ratified.
7
+ #
8
+ # @adr ADR-090 (story maps and stories carry a drift-invalidated human-oversight marker)
9
+ # @problem P404 (implement ADR-089 + ADR-090) — Phase 2
10
+
11
+ setup() {
12
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
13
+ SCRIPT="${REPO_ROOT}/packages/itil/scripts/check-rfc-stories-ratified.sh"
14
+ TMPD="$(mktemp -d)"
15
+ mkdir -p "$TMPD/stories/accepted"
16
+ }
17
+ teardown() { rm -rf "$TMPD"; }
18
+
19
+ mkstory() { # $1=id $2=oversight-value(or "none")
20
+ local f="$TMPD/stories/accepted/$1-x.md"
21
+ { echo "---"; echo "status: accepted"; echo "story-id: x"
22
+ [ "$2" != "none" ] && echo "human-oversight: $2"
23
+ echo "---"; echo "# $1"; } > "$f"
24
+ }
25
+ mkrfc() { printf -- '---\nstatus: proposed\nstories: [%s]\n---\n# rfc\n' "$1" > "$TMPD/RFC.proposed.md"; }
26
+
27
+ @test "check-rfc-stories-ratified: empty stories: [] passes vacuously (exit 0)" {
28
+ printf -- '---\nstatus: proposed\nstories: []\n---\n# rfc\n' > "$TMPD/RFC.proposed.md"
29
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
30
+ [ "$status" -eq 0 ]
31
+ }
32
+
33
+ @test "check-rfc-stories-ratified: a confirmed story is ACCEPTED (exit 0)" {
34
+ mkstory STORY-020 confirmed
35
+ mkrfc STORY-020
36
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
37
+ [ "$status" -eq 0 ]
38
+ }
39
+
40
+ @test "check-rfc-stories-ratified: an unconfirmed story is REJECTED (exit non-zero)" {
41
+ mkstory STORY-020 unconfirmed
42
+ mkrfc STORY-020
43
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
44
+ [ "$status" -ne 0 ]
45
+ [[ "$output" == *"STORY-020"* ]]
46
+ }
47
+
48
+ @test "check-rfc-stories-ratified: a story with NO oversight marker is REJECTED" {
49
+ mkstory STORY-020 none
50
+ mkrfc STORY-020
51
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
52
+ [ "$status" -ne 0 ]
53
+ [[ "$output" == *"STORY-020"* ]]
54
+ }
55
+
56
+ @test "check-rfc-stories-ratified: a missing story file is REJECTED" {
57
+ mkrfc STORY-099
58
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
59
+ [ "$status" -ne 0 ]
60
+ [[ "$output" == *"STORY-099"* ]]
61
+ }
62
+
63
+ @test "check-rfc-stories-ratified: mixed — one confirmed one unconfirmed is REJECTED" {
64
+ mkstory STORY-020 confirmed
65
+ mkstory STORY-021 unconfirmed
66
+ mkrfc "STORY-020, STORY-021"
67
+ run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
68
+ [ "$status" -ne 0 ]
69
+ [[ "$output" == *"STORY-021"* ]]
70
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural test for detect-unratified-stories-maps.sh (ADR-090 detector).
3
+ # Mirrors wr-architect detect-unoversighted.sh, but spans TWO artefact types
4
+ # with DIFFERENT marker encodings:
5
+ # - stories: markdown YAML frontmatter `human-oversight: confirmed`
6
+ # - story-maps: HTML `<meta name="human-oversight" content="confirmed">`
7
+ # Lists every artefact whose marker is missing or not `confirmed`. Always exit 0.
8
+ #
9
+ # @adr ADR-090 (drift-invalidated story-map/story human-oversight marker)
10
+ # @problem P404 (implement ADR-089 + ADR-090) — Phase 2
11
+
12
+ setup() {
13
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
14
+ SCRIPT="${REPO_ROOT}/packages/itil/scripts/detect-unratified-stories-maps.sh"
15
+ TMPD="$(mktemp -d)"
16
+ mkdir -p "$TMPD/stories/accepted" "$TMPD/story-maps/draft"
17
+ # stories (markdown frontmatter)
18
+ printf -- '---\nstatus: accepted\nhuman-oversight: confirmed\n---\n# ok\n' > "$TMPD/stories/accepted/STORY-1-ok.md"
19
+ printf -- '---\nstatus: accepted\nhuman-oversight: unconfirmed\n---\n# no\n' > "$TMPD/stories/accepted/STORY-2-unconf.md"
20
+ printf -- '---\nstatus: accepted\n---\n# nomarker\n' > "$TMPD/stories/accepted/STORY-3-none.md"
21
+ # story-maps (HTML meta)
22
+ printf -- '<meta name="human-oversight" content="confirmed">\n' > "$TMPD/story-maps/draft/STORY-MAP-1-ok.html"
23
+ printf -- '<meta name="human-oversight" content="unconfirmed">\n' > "$TMPD/story-maps/draft/STORY-MAP-2-unconf.html"
24
+ printf -- '<h1>no meta</h1>\n' > "$TMPD/story-maps/draft/STORY-MAP-3-none.html"
25
+ }
26
+ teardown() { rm -rf "$TMPD"; }
27
+
28
+ @test "detect: lists unconfirmed + unmarked, omits confirmed; exits 0" {
29
+ run bash "$SCRIPT" "$TMPD/stories" "$TMPD/story-maps"
30
+ [ "$status" -eq 0 ]
31
+ [[ "$output" == *"STORY-2-unconf.md"* ]]
32
+ [[ "$output" == *"STORY-3-none.md"* ]]
33
+ [[ "$output" == *"STORY-MAP-2-unconf.html"* ]]
34
+ [[ "$output" == *"STORY-MAP-3-none.html"* ]]
35
+ [[ "$output" != *"STORY-1-ok.md"* ]]
36
+ [[ "$output" != *"STORY-MAP-1-ok.html"* ]]
37
+ }
38
+
39
+ @test "detect: all-confirmed set yields empty output, exit 0" {
40
+ rm "$TMPD/stories/accepted/STORY-2-unconf.md" "$TMPD/stories/accepted/STORY-3-none.md" \
41
+ "$TMPD/story-maps/draft/STORY-MAP-2-unconf.html" "$TMPD/story-maps/draft/STORY-MAP-3-none.html"
42
+ run bash "$SCRIPT" "$TMPD/stories" "$TMPD/story-maps"
43
+ [ "$status" -eq 0 ]
44
+ [ -z "$output" ]
45
+ }
46
+
47
+ @test "detect: missing dirs are tolerated (exit 0, no crash)" {
48
+ run bash "$SCRIPT" "$TMPD/nope-stories" "$TMPD/nope-maps"
49
+ [ "$status" -eq 0 ]
50
+ }
@@ -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). **`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`. |
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`. **Every listed story ratified — run `wr-itil-check-rfc-stories-ratified <rfc-file>`; hard-block on non-zero (any listed story carries `human-oversight: unconfirmed`, has no marker, or is missing).** Per **ADR-090** an RFC may reference only ratified stories; composes with the has-stories gate (has-stories checks ≥1 exists, ratified checks each is confirmed). |
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. |