@windyroad/itil 0.20.0 → 0.21.0-preview.213
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/.claude-plugin/plugin.json +1 -1
- package/hooks/hooks.json +4 -0
- package/hooks/pre-publish-intake-gate.sh +130 -0
- package/hooks/test/pre-publish-intake-gate.bats +144 -0
- package/package.json +1 -1
- package/skills/manage-problem/SKILL.md +19 -0
- package/skills/manage-problem/test/manage-problem-first-run-intake-prompt.bats +43 -0
- package/skills/report-upstream/SKILL.md +6 -0
- package/skills/scaffold-intake/SKILL.md +196 -0
- package/skills/scaffold-intake/templates/CONTRIBUTING.md.tmpl +31 -0
- package/skills/scaffold-intake/templates/SECURITY.md.tmpl +39 -0
- package/skills/scaffold-intake/templates/SUPPORT.md.tmpl +32 -0
- package/skills/scaffold-intake/templates/config.yml.tmpl +8 -0
- package/skills/scaffold-intake/templates/problem-report.yml.tmpl +87 -0
- package/skills/scaffold-intake/test/scaffold-intake-contract.bats +126 -0
- package/skills/scaffold-intake/test/scaffold-intake-fixture.bats +161 -0
- package/skills/scaffold-intake/test/scaffold-intake-secrets-absent.bats +87 -0
- package/skills/work-problems/SKILL.md +18 -0
- package/skills/work-problems/test/work-problems-first-run-intake-prompt.bats +36 -0
package/hooks/hooks.json
CHANGED
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
{
|
|
16
16
|
"matcher": "Bash",
|
|
17
17
|
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/p057-staging-trap-detect.sh" }]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"matcher": "Bash",
|
|
21
|
+
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-publish-intake-gate.sh" }]
|
|
18
22
|
}
|
|
19
23
|
],
|
|
20
24
|
"Stop": [
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse:Bash hook (P065 / ADR-036 Trigger 2): blocks `npm publish`
|
|
3
|
+
# and `gh pr merge` of a `changeset-release/*` PR when the project is
|
|
4
|
+
# missing one or more of the five intake files.
|
|
5
|
+
#
|
|
6
|
+
# Required intake files (per ADR-036 Detection step 5):
|
|
7
|
+
# .github/ISSUE_TEMPLATE/config.yml
|
|
8
|
+
# .github/ISSUE_TEMPLATE/problem-report.yml
|
|
9
|
+
# SECURITY.md
|
|
10
|
+
# SUPPORT.md
|
|
11
|
+
# CONTRIBUTING.md
|
|
12
|
+
#
|
|
13
|
+
# Bypass / opt-out paths (in priority order, per architect direction):
|
|
14
|
+
# 1. INTAKE_BYPASS=1 in env -> short-circuit BEFORE existence check
|
|
15
|
+
# (consistent with RISK_BYPASS naming
|
|
16
|
+
# convention in external-comms-gate.sh)
|
|
17
|
+
# 2. .claude/.intake-scaffold-declined marker present -> permit
|
|
18
|
+
# (ADR-009 marker semantics; explicit
|
|
19
|
+
# decline by adopter, persistent until
|
|
20
|
+
# file deleted)
|
|
21
|
+
# 3. All five intake files present -> permit (idempotent default)
|
|
22
|
+
# 4. Otherwise -> deny + delegate to /wr-itil:scaffold-intake
|
|
23
|
+
#
|
|
24
|
+
# This hook composes with risk-scorer's git-push-gate.sh: both fire on
|
|
25
|
+
# Bash with `gh pr merge` matchers, and either may deny independently.
|
|
26
|
+
# In practice git-push-gate denies all `gh pr merge` and routes to
|
|
27
|
+
# `npm run release:watch` which subsequently runs `npm publish` — at
|
|
28
|
+
# which point this hook fires.
|
|
29
|
+
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
|
|
32
|
+
# ---------- 1. Bypass: check INTAKE_BYPASS BEFORE anything else ----------
|
|
33
|
+
if [ "${INTAKE_BYPASS:-0}" = "1" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
INPUT=$(cat)
|
|
38
|
+
|
|
39
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | python3 -c "
|
|
40
|
+
import sys, json
|
|
41
|
+
try:
|
|
42
|
+
print(json.load(sys.stdin).get('tool_name', ''))
|
|
43
|
+
except Exception:
|
|
44
|
+
print('')
|
|
45
|
+
" 2>/dev/null || echo "")
|
|
46
|
+
|
|
47
|
+
# Out of scope: only Bash invocations are gated.
|
|
48
|
+
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
|
49
|
+
|
|
50
|
+
COMMAND=$(printf '%s' "$INPUT" | python3 -c "
|
|
51
|
+
import sys, json
|
|
52
|
+
try:
|
|
53
|
+
print(json.load(sys.stdin).get('tool_input', {}).get('command', ''))
|
|
54
|
+
except Exception:
|
|
55
|
+
print('')
|
|
56
|
+
" 2>/dev/null || echo "")
|
|
57
|
+
|
|
58
|
+
# ---------- Surface detection ----------
|
|
59
|
+
IN_SCOPE=0
|
|
60
|
+
|
|
61
|
+
# Match `npm publish` (with or without flags).
|
|
62
|
+
if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*npm publish(\s|$)'; then
|
|
63
|
+
IN_SCOPE=1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Match `gh pr merge` against a changeset-release/* PR. The changesets
|
|
67
|
+
# release-PR pattern is the only `gh pr merge` shape that flips the
|
|
68
|
+
# publish boundary. A regular feature-branch merge does not.
|
|
69
|
+
if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*gh pr merge\b' \
|
|
70
|
+
&& echo "$COMMAND" | grep -qE 'changeset-release/'; then
|
|
71
|
+
IN_SCOPE=1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
[ "$IN_SCOPE" = "1" ] || exit 0
|
|
75
|
+
|
|
76
|
+
# ---------- 2. Decline marker (ADR-009 persistent marker) ----------
|
|
77
|
+
if [ -f ".claude/.intake-scaffold-declined" ]; then
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# ---------- 3. Intake-file existence check ----------
|
|
82
|
+
MISSING=()
|
|
83
|
+
for path in \
|
|
84
|
+
".github/ISSUE_TEMPLATE/config.yml" \
|
|
85
|
+
".github/ISSUE_TEMPLATE/problem-report.yml" \
|
|
86
|
+
"SECURITY.md" \
|
|
87
|
+
"SUPPORT.md" \
|
|
88
|
+
"CONTRIBUTING.md"; do
|
|
89
|
+
if [ ! -f "$path" ]; then
|
|
90
|
+
MISSING+=("$path")
|
|
91
|
+
fi
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
if [ "${#MISSING[@]}" -eq 0 ]; then
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# ---------- 4. Deny + delegate ----------
|
|
99
|
+
deny_reason() {
|
|
100
|
+
local missing_list
|
|
101
|
+
missing_list=$(printf ' - %s\n' "${MISSING[@]}")
|
|
102
|
+
cat <<EOF
|
|
103
|
+
BLOCKED (P065 / ADR-036 pre-publish intake gate): ${#MISSING[@]} of 5 intake files missing — downstream reporters would hit a blank issue form and have no declared security-disclosure channel.
|
|
104
|
+
|
|
105
|
+
Missing:
|
|
106
|
+
${missing_list}
|
|
107
|
+
|
|
108
|
+
Recovery, in priority order:
|
|
109
|
+
1. Run /wr-itil:scaffold-intake to scaffold the missing intake files (recommended for first-time adopters).
|
|
110
|
+
2. Set INTAKE_BYPASS=1 to override for documented exceptions:
|
|
111
|
+
INTAKE_BYPASS=1 npm publish
|
|
112
|
+
3. Decline scaffolding entirely (suppresses the gate indefinitely):
|
|
113
|
+
mkdir -p .claude && touch .claude/.intake-scaffold-declined
|
|
114
|
+
EOF
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
REASON=$(deny_reason)
|
|
118
|
+
|
|
119
|
+
python3 -c "
|
|
120
|
+
import json, sys
|
|
121
|
+
print(json.dumps({
|
|
122
|
+
'hookSpecificOutput': {
|
|
123
|
+
'hookEventName': 'PreToolUse',
|
|
124
|
+
'permissionDecision': 'deny',
|
|
125
|
+
'permissionDecisionReason': sys.argv[1]
|
|
126
|
+
}
|
|
127
|
+
}))
|
|
128
|
+
" "$REASON"
|
|
129
|
+
|
|
130
|
+
exit 0
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036: pre-publish-intake-gate.sh PreToolUse:Bash hook must deny
|
|
4
|
+
# `npm publish` (and changesets-release `gh pr merge`) when the four intake
|
|
5
|
+
# files are missing, unless the project has opted out via the decline
|
|
6
|
+
# marker (`.claude/.intake-scaffold-declined`) or the user sets the
|
|
7
|
+
# `INTAKE_BYPASS=1` env override.
|
|
8
|
+
#
|
|
9
|
+
# Required intake files (per ADR-036 Detection step 5):
|
|
10
|
+
# .github/ISSUE_TEMPLATE/config.yml
|
|
11
|
+
# .github/ISSUE_TEMPLATE/problem-report.yml
|
|
12
|
+
# SECURITY.md
|
|
13
|
+
# SUPPORT.md
|
|
14
|
+
# CONTRIBUTING.md
|
|
15
|
+
#
|
|
16
|
+
# Per feedback_behavioural_tests.md (P081): behavioural assertions —
|
|
17
|
+
# simulate the hook's payload on stdin and assert on emitted JSON
|
|
18
|
+
# permissionDecision and exit status. No source-grep on hook content.
|
|
19
|
+
|
|
20
|
+
setup() {
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
22
|
+
HOOK="$SCRIPT_DIR/pre-publish-intake-gate.sh"
|
|
23
|
+
ORIG_DIR="$PWD"
|
|
24
|
+
TEST_DIR=$(mktemp -d)
|
|
25
|
+
cd "$TEST_DIR"
|
|
26
|
+
unset INTAKE_BYPASS
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
teardown() {
|
|
30
|
+
cd "$ORIG_DIR"
|
|
31
|
+
rm -rf "$TEST_DIR"
|
|
32
|
+
unset INTAKE_BYPASS
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Helper: simulate the PreToolUse:Bash payload on stdin.
|
|
36
|
+
run_bash_hook() {
|
|
37
|
+
local cmd="$1"
|
|
38
|
+
local json
|
|
39
|
+
json=$(printf '{"tool_name":"Bash","tool_input":{"command":"%s"}}' "$cmd")
|
|
40
|
+
echo "$json" | bash "$HOOK"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Helper: scaffold all five intake files in the test directory.
|
|
44
|
+
scaffold_all_intake() {
|
|
45
|
+
mkdir -p .github/ISSUE_TEMPLATE
|
|
46
|
+
echo "blank_issues_enabled: false" > .github/ISSUE_TEMPLATE/config.yml
|
|
47
|
+
echo "name: Report a problem" > .github/ISSUE_TEMPLATE/problem-report.yml
|
|
48
|
+
echo "# Security Policy" > SECURITY.md
|
|
49
|
+
echo "# Getting Help" > SUPPORT.md
|
|
50
|
+
echo "# Contributing" > CONTRIBUTING.md
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Helper: set the decline marker.
|
|
54
|
+
decline() {
|
|
55
|
+
mkdir -p .claude
|
|
56
|
+
: > .claude/.intake-scaffold-declined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# --- Allow paths ---
|
|
60
|
+
|
|
61
|
+
@test "allow: npm publish proceeds when all five intake files are present" {
|
|
62
|
+
scaffold_all_intake
|
|
63
|
+
run run_bash_hook "npm publish"
|
|
64
|
+
[ "$status" -eq 0 ]
|
|
65
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@test "allow: missing intake but decline marker present permits publish" {
|
|
69
|
+
decline
|
|
70
|
+
run run_bash_hook "npm publish"
|
|
71
|
+
[ "$status" -eq 0 ]
|
|
72
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@test "allow: missing intake but INTAKE_BYPASS=1 permits publish (checked BEFORE existence)" {
|
|
76
|
+
# Architect direction: INTAKE_BYPASS must short-circuit before the
|
|
77
|
+
# existence check fires.
|
|
78
|
+
INTAKE_BYPASS=1 run run_bash_hook "npm publish"
|
|
79
|
+
[ "$status" -eq 0 ]
|
|
80
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@test "allow: non-publish bash commands are out of scope" {
|
|
84
|
+
run run_bash_hook "ls -la"
|
|
85
|
+
[ "$status" -eq 0 ]
|
|
86
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
87
|
+
run run_bash_hook "git status"
|
|
88
|
+
[ "$status" -eq 0 ]
|
|
89
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@test "allow: non-Bash tool calls are out of scope" {
|
|
93
|
+
json='{"tool_name":"Read","tool_input":{"file_path":"foo"}}'
|
|
94
|
+
run bash -c "echo '$json' | bash $HOOK"
|
|
95
|
+
[ "$status" -eq 0 ]
|
|
96
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# --- Deny paths ---
|
|
100
|
+
|
|
101
|
+
@test "deny: npm publish with no intake files, no marker, no bypass" {
|
|
102
|
+
run run_bash_hook "npm publish"
|
|
103
|
+
[ "$status" -eq 0 ]
|
|
104
|
+
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
105
|
+
[[ "$output" == *"intake"* ]]
|
|
106
|
+
[[ "$output" == *"scaffold-intake"* ]]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@test "deny: npm publish with partial intake (3 of 5) still denies" {
|
|
110
|
+
mkdir -p .github/ISSUE_TEMPLATE
|
|
111
|
+
echo "blank_issues_enabled: false" > .github/ISSUE_TEMPLATE/config.yml
|
|
112
|
+
echo "name: Report a problem" > .github/ISSUE_TEMPLATE/problem-report.yml
|
|
113
|
+
echo "# Security Policy" > SECURITY.md
|
|
114
|
+
# SUPPORT.md and CONTRIBUTING.md missing
|
|
115
|
+
run run_bash_hook "npm publish"
|
|
116
|
+
[ "$status" -eq 0 ]
|
|
117
|
+
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@test "deny message names the recovery affordances (skill, marker, bypass)" {
|
|
121
|
+
run run_bash_hook "npm publish"
|
|
122
|
+
[ "$status" -eq 0 ]
|
|
123
|
+
[[ "$output" == *"scaffold-intake"* ]]
|
|
124
|
+
[[ "$output" == *"INTAKE_BYPASS"* ]]
|
|
125
|
+
[[ "$output" == *".intake-scaffold-declined"* ]]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# --- gh pr merge on changeset-release/* ---
|
|
129
|
+
|
|
130
|
+
@test "deny: gh pr merge of a changeset-release/* PR with missing intake" {
|
|
131
|
+
# ADR-036 Trigger 2 also matches gh pr merge against changeset-release/*
|
|
132
|
+
# branches (the changesets release-PR pattern).
|
|
133
|
+
run run_bash_hook "gh pr merge 42 --repo windyroad/example --branch changeset-release/main"
|
|
134
|
+
[ "$status" -eq 0 ]
|
|
135
|
+
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@test "allow: gh pr merge of a non-changeset PR is out of scope" {
|
|
139
|
+
# Only the changeset-release/* branch pattern is in scope; a regular
|
|
140
|
+
# feature-branch merge does not flip the publish boundary.
|
|
141
|
+
run run_bash_hook "gh pr merge 99 --branch feature/foo"
|
|
142
|
+
[ "$status" -eq 0 ]
|
|
143
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
144
|
+
}
|
package/package.json
CHANGED
|
@@ -13,6 +13,25 @@ Create, update, or transition problem tickets following an ITIL-aligned problem
|
|
|
13
13
|
|
|
14
14
|
When referencing problem IDs, ADR IDs, or JTBD IDs in prose output, always include the human-readable title on first mention. Use the format `P029 (Edit gate overhead for governance docs)`, not bare `P029`. Tables with separate ID and Title columns are fine as-is.
|
|
15
15
|
|
|
16
|
+
## First-run intake-scaffold pointer (P065 / ADR-036)
|
|
17
|
+
|
|
18
|
+
This skill is one of the two host skills wired to surface the [`/wr-itil:scaffold-intake`](../scaffold-intake/SKILL.md) skill on first invocation in a project that has not yet adopted the OSS intake surface. The contract is documented in [ADR-036](../../../../docs/decisions/036-scaffold-downstream-oss-intake.proposed.md) (Scaffold downstream OSS intake — skill + layered triggers).
|
|
19
|
+
|
|
20
|
+
**Preamble check** (run before Step 0 of any operation):
|
|
21
|
+
|
|
22
|
+
1. Look for the four intake paths: `.github/ISSUE_TEMPLATE/config.yml`, `.github/ISSUE_TEMPLATE/problem-report.yml`, `SECURITY.md`, `SUPPORT.md`, `CONTRIBUTING.md`.
|
|
23
|
+
2. Look for `.claude/.intake-scaffold-declined` (explicit decline marker — never re-prompt).
|
|
24
|
+
3. Look for `.claude/.intake-scaffold-done` (done marker — already scaffolded).
|
|
25
|
+
|
|
26
|
+
If any intake file is missing AND both markers are absent, surface the scaffold-intake skill:
|
|
27
|
+
|
|
28
|
+
| Mode | Behaviour |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Foreground (interactive)** | Fire one-shot `AskUserQuestion` per ADR-013 Rule 1: header `"Scaffold OSS intake?"`, three options — **Scaffold now** (delegate to `/wr-itil:scaffold-intake`), **Not now (ask again next session)** (no marker; re-prompt next time), **Decline (never prompt in this project)** (write `.claude/.intake-scaffold-declined`). |
|
|
31
|
+
| **AFK orchestrator (Rule 6 fail-safe)** | Do **not** fire `AskUserQuestion`. Append a one-line `"pending intake scaffold"` note to the iteration's `ITERATION_SUMMARY` notes field. Do **not** auto-scaffold — JTBD-006 forbids the agent from making this judgement call. The user catches up on next interactive session. |
|
|
32
|
+
|
|
33
|
+
The preamble check is a one-shot; the `.intake-scaffold-done` and `.intake-scaffold-declined` markers (ADR-009 persistent-marker semantics) suppress re-prompts in subsequent sessions without TTL expiry.
|
|
34
|
+
|
|
16
35
|
## Operations
|
|
17
36
|
|
|
18
37
|
- **Create**: `problem <title or description>` — creates a new open problem
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036 Confirmation line 198: manage-problem SKILL.md must
|
|
4
|
+
# wire the first-run intake-scaffold prompt — citing ADR-036 + the AFK
|
|
5
|
+
# fail-safe + the marker contract.
|
|
6
|
+
#
|
|
7
|
+
# Doc-lint structural test (Permitted Exception per ADR-005). The wiring
|
|
8
|
+
# itself is a SKILL.md preamble pointer (architect direction 2026-04-26),
|
|
9
|
+
# so behavioural assertions live at the contract layer of scaffold-intake;
|
|
10
|
+
# this bats fixes the wiring point so future maintainers cannot silently
|
|
11
|
+
# remove the cross-reference.
|
|
12
|
+
|
|
13
|
+
setup() {
|
|
14
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
15
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/manage-problem/SKILL.md"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@test "manage-problem: SKILL.md cites ADR-036 (first-run-prompt contract)" {
|
|
19
|
+
run grep -F 'ADR-036' "$SKILL_MD"
|
|
20
|
+
[ "$status" -eq 0 ]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@test "manage-problem: SKILL.md cross-references the scaffold-intake skill" {
|
|
24
|
+
run grep -F 'wr-itil:scaffold-intake' "$SKILL_MD"
|
|
25
|
+
[ "$status" -eq 0 ]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@test "manage-problem: SKILL.md documents the first-run intake-scaffold detection clause" {
|
|
29
|
+
# Detection clause must reference at least one intake-file path AND the
|
|
30
|
+
# decline marker so future maintainers see how the trigger fires.
|
|
31
|
+
run grep -iE 'intake' "$SKILL_MD"
|
|
32
|
+
[ "$status" -eq 0 ]
|
|
33
|
+
run grep -F '.claude/.intake-scaffold-declined' "$SKILL_MD"
|
|
34
|
+
[ "$status" -eq 0 ]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@test "manage-problem: SKILL.md documents the AFK fail-safe (no AskUserQuestion in AFK; iteration-report note)" {
|
|
38
|
+
# Per ADR-013 Rule 6, the AFK orchestrator branch must not fire
|
|
39
|
+
# AskUserQuestion. The SKILL.md preamble pointer must call out this
|
|
40
|
+
# fail-safe so adopters reading the skill understand the divergence.
|
|
41
|
+
run grep -iE 'afk|rule 6' "$SKILL_MD"
|
|
42
|
+
[ "$status" -eq 0 ]
|
|
43
|
+
}
|
|
@@ -36,6 +36,12 @@ This skill **does NOT** implement ADR-027's Step-0 auto-delegation pattern. Per
|
|
|
36
36
|
|
|
37
37
|
**Trigger to revisit**: if a second skill that reads upstream repo content lands (per ADR-024's Reassessment Criteria), reconsider whether the cross-cutting "read upstream" pattern should move into a Step-0-delegated subagent that this skill calls.
|
|
38
38
|
|
|
39
|
+
## Reciprocal sibling: scaffold-intake (P065 / ADR-036)
|
|
40
|
+
|
|
41
|
+
This skill files reports against an upstream's intake surface. Its reciprocal — [`/wr-itil:scaffold-intake`](../scaffold-intake/SKILL.md) — scaffolds the intake surface on a downstream project so that downstream can in turn be a target of further `report-upstream` calls. Together they close the ecosystem-level intake-shape discipline P055 partial coverage left open. Adopters who run this skill against a target without an `.github/ISSUE_TEMPLATE/` directory should be encouraged to suggest the target adopt `/wr-itil:scaffold-intake` to gain the full problem-first intake set this skill prefers (per ADR-033 Step 3 classifier).
|
|
42
|
+
|
|
43
|
+
[ADR-036](../../../../docs/decisions/036-scaffold-downstream-oss-intake.proposed.md) is the design record for the reciprocal-scaffolding side; it establishes the layered triggers (first-run prompt + pre-publish gate + optional CI check) and the marker-suppressed lifecycle.
|
|
44
|
+
|
|
39
45
|
## Voice-tone gate interaction (ADR-028)
|
|
40
46
|
|
|
41
47
|
The skill's `gh issue create` (Step 5) and `gh api repos/.../security-advisories` (Step 6) calls are **on the gated surface list per [ADR-028](../../../docs/decisions/028-voice-tone-gate-external-comms.proposed.md)** (Voice-tone gate on external communications). Expected behaviour during these tool calls:
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-itil:scaffold-intake
|
|
3
|
+
description: Scaffold the four OSS intake surfaces (.github/ISSUE_TEMPLATE/, SECURITY.md, SUPPORT.md, CONTRIBUTING.md) for a downstream project that adopts @windyroad/itil. Idempotent, foreground-synchronous, and respects ADR-009 marker semantics. Implements the contract in ADR-036.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Scaffold Intake — Downstream OSS Intake Skill
|
|
8
|
+
|
|
9
|
+
Scaffold the five intake files every project in the Windy Road ecosystem needs to receive structured problem reports and route security disclosure properly:
|
|
10
|
+
|
|
11
|
+
- `.github/ISSUE_TEMPLATE/config.yml`
|
|
12
|
+
- `.github/ISSUE_TEMPLATE/problem-report.yml`
|
|
13
|
+
- `SECURITY.md`
|
|
14
|
+
- `SUPPORT.md`
|
|
15
|
+
- `CONTRIBUTING.md`
|
|
16
|
+
|
|
17
|
+
Templates are seeded from this repo's P066-corrected problem-first intake set and are substituted with per-project values for project name, repository URL, plugin list, and security-contact path.
|
|
18
|
+
|
|
19
|
+
This skill implements the contract in [ADR-036](../../../../docs/decisions/036-scaffold-downstream-oss-intake.proposed.md) (Scaffold downstream OSS intake — skill + layered triggers). It is the reciprocal of [`/wr-itil:report-upstream`](../report-upstream/SKILL.md) — that skill files reports against upstream intake; this skill creates the intake surface so a downstream project can be a target.
|
|
20
|
+
|
|
21
|
+
## Pattern
|
|
22
|
+
|
|
23
|
+
This skill is **foreground-synchronous** per [ADR-032](../../../../docs/decisions/032-governance-skill-invocation-patterns.proposed.md) (Governance skill invocation patterns). Scaffolding writes files into the project that the user normally wants to review before they commit; the wrapped-pattern subagent shape would defeat that review. The skill commits its own work per [ADR-014](../../../../docs/decisions/014-governance-skills-commit-their-own-work.proposed.md).
|
|
24
|
+
|
|
25
|
+
## Invocation
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
/wr-itil:scaffold-intake [--dry-run] [--force] [--project-name <name>] [--project-url <url>] [--security-contact <path>] [--ci]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Flag | Effect |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `--dry-run` | Preview the files that would be scaffolded; no writes. |
|
|
34
|
+
| `--force` | Overwrite present files after a diff-and-replace prompt. Off by default — present files are reported as "already present" and skipped. |
|
|
35
|
+
| `--project-name <name>` | Override auto-detected project name (default: `package.json` `name`, fallback to repo dirname). |
|
|
36
|
+
| `--project-url <url>` | Override auto-detected repository URL (default: `package.json` `repository.url`, fallback to `git remote get-url origin`). |
|
|
37
|
+
| `--security-contact <path>` | Override the security-disclosure path written into SECURITY.md (default: `Use GitHub Security Advisories`). |
|
|
38
|
+
| `--ci` | Also emit `.github/workflows/intake-check.yml` (Trigger 3, optional). |
|
|
39
|
+
|
|
40
|
+
## Steps
|
|
41
|
+
|
|
42
|
+
### 1. Detect project metadata
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
PROJECT_NAME=$(node -p "require('./package.json').name" 2>/dev/null || basename "$PWD")
|
|
46
|
+
PROJECT_URL=$(node -p "require('./package.json').repository?.url || ''" 2>/dev/null \
|
|
47
|
+
| sed -E 's|^git\+||; s|\.git$||' \
|
|
48
|
+
|| git remote get-url origin 2>/dev/null \
|
|
49
|
+
|| echo "")
|
|
50
|
+
YEAR=$(date +%Y)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`--project-name` and `--project-url` flags override the auto-detected values when supplied. `package.json` shapes vary across adopters; if detection produces empty values, surface a clear error suggesting the override flags rather than substituting an empty token.
|
|
54
|
+
|
|
55
|
+
Plugin list: enumerate installed `@windyroad/*` packages from `.claude-plugin/plugin.json` (if present) or from `package.json` dev-dependencies. Used for the SUPPORT.md "affected plugin or component" enumeration.
|
|
56
|
+
|
|
57
|
+
### 2. Enumerate target paths
|
|
58
|
+
|
|
59
|
+
Required intake files (per ADR-036 Detection step 5):
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
.github/ISSUE_TEMPLATE/config.yml
|
|
63
|
+
.github/ISSUE_TEMPLATE/problem-report.yml
|
|
64
|
+
SECURITY.md
|
|
65
|
+
SUPPORT.md
|
|
66
|
+
CONTRIBUTING.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For each path: classify as **missing**, **present-and-current** (template-substituted output matches existing file content), or **present-and-outdated** (existing differs from substituted template).
|
|
70
|
+
|
|
71
|
+
### 3. AskUserQuestion: which files to scaffold (foreground only)
|
|
72
|
+
|
|
73
|
+
In foreground (interactive) mode, fire `AskUserQuestion` per ADR-013 Rule 1 with options scoped to the missing set:
|
|
74
|
+
|
|
75
|
+
- **Scaffold all missing** — write every absent template, skip present files.
|
|
76
|
+
- **Scaffold with review** — preview each substituted template before writing.
|
|
77
|
+
- **Dry-run** — preview only, no writes.
|
|
78
|
+
- **Cancel** — exit without action.
|
|
79
|
+
|
|
80
|
+
If `--force` is set AND outdated-present files exist, the prompt offers a fifth option for diff-and-replace per file. Architect direction (2026-04-26): keep the prompt one-shot when missing list is small; per-file prompts only when the user explicitly opts into review.
|
|
81
|
+
|
|
82
|
+
### 4. Substitute templates and write files
|
|
83
|
+
|
|
84
|
+
Templates live in `templates/` adjacent to this SKILL.md. Substitution is mustache-style — no runtime dependency:
|
|
85
|
+
|
|
86
|
+
| Token | Value source |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `{{project_name}}` | Detected or `--project-name` flag. |
|
|
89
|
+
| `{{project_url}}` | Detected or `--project-url` flag. |
|
|
90
|
+
| `{{plugin_list}}` | Comma-separated installed `@windyroad/*` plugins. |
|
|
91
|
+
| `{{security_contact}}` | Detected from existing SECURITY.md (when only that file is present); else `--security-contact` flag; fallback `Use GitHub Security Advisories`. |
|
|
92
|
+
| `{{year}}` | `date +%Y` at scaffold time. |
|
|
93
|
+
|
|
94
|
+
`sed` substitution sketch (idempotent, no runtime tooling):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
sed \
|
|
98
|
+
-e "s|{{project_name}}|$PROJECT_NAME|g" \
|
|
99
|
+
-e "s|{{project_url}}|$PROJECT_URL|g" \
|
|
100
|
+
-e "s|{{plugin_list}}|$PLUGIN_LIST|g" \
|
|
101
|
+
-e "s|{{security_contact}}|$SECURITY_CONTACT|g" \
|
|
102
|
+
-e "s|{{year}}|$YEAR|g" \
|
|
103
|
+
templates/SECURITY.md.tmpl > SECURITY.md
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For each missing file: substitute, write to the target path, report the write + a short diff to stdout.
|
|
107
|
+
|
|
108
|
+
For each present-and-current file: report "already present — skipped" without modification.
|
|
109
|
+
|
|
110
|
+
For each present-and-outdated file: when `--force` is set, offer diff-and-replace; otherwise skip with a clear note that `--force` would update it.
|
|
111
|
+
|
|
112
|
+
### 5. Mark scaffold as done
|
|
113
|
+
|
|
114
|
+
After successful write:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mkdir -p .claude
|
|
118
|
+
: > .claude/.intake-scaffold-done
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The marker suppresses Trigger-1 first-run prompts in subsequent `/wr-itil:manage-problem` and `/wr-itil:work-problems` invocations. ADR-009 marker semantics — persistent (no TTL), file-presence is the policy signal, deletable to reset.
|
|
122
|
+
|
|
123
|
+
### 6. Commit per ADR-014
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git add .github/ISSUE_TEMPLATE/ SECURITY.md SUPPORT.md CONTRIBUTING.md .claude/.intake-scaffold-done
|
|
127
|
+
git -c commit.gpgsign=false commit -m "docs: scaffold OSS intake (ISSUE_TEMPLATE, SECURITY, SUPPORT, CONTRIBUTING)"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Follow the project's existing commit-gate flow: the commit triggers `wr-risk-scorer:pipeline` per ADR-014; remediation suggestions are applied per ADR-042 if residual exceeds appetite.
|
|
131
|
+
|
|
132
|
+
## Idempotency
|
|
133
|
+
|
|
134
|
+
The skill is idempotent by construction:
|
|
135
|
+
|
|
136
|
+
- Present files are skipped unless `--force`.
|
|
137
|
+
- Re-running the skill on a fully-scaffolded project produces no diff.
|
|
138
|
+
- The done marker prevents the host first-run prompt from re-firing.
|
|
139
|
+
|
|
140
|
+
## Rule 6 audit (per ADR-032)
|
|
141
|
+
|
|
142
|
+
Every `AskUserQuestion` branch this skill uses must enumerate its AFK fail-safe per ADR-013 Rule 6. The audit table below documents each branch:
|
|
143
|
+
|
|
144
|
+
| AskUserQuestion branch | Resolution |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Step 3: "Which files to scaffold?" (foreground invocation) | Foreground-synchronous — user is in-session; `AskUserQuestion` fires normally. |
|
|
147
|
+
| Step 4: "Overwrite outdated present file with updated template?" (with `--force`) | Foreground-synchronous; same as Step 3. |
|
|
148
|
+
| First-run prompt fired from `/wr-itil:manage-problem` or `/wr-itil:work-problems` (foreground invocation) | Foreground-synchronous per the hosting skill's pattern. |
|
|
149
|
+
| First-run prompt fired from an AFK orchestrator iteration | **Fail-safe (Rule 6)**: do NOT fire `AskUserQuestion` and do NOT auto-scaffold. Append a single one-line "pending intake scaffold" note to the orchestrator's iteration report; defer the prompt to the user's next interactive session. JTBD-006 forbids the agent from making this judgement call. |
|
|
150
|
+
|
|
151
|
+
## Trigger surfaces (layered)
|
|
152
|
+
|
|
153
|
+
The skill is reachable via three trigger surfaces, layered so a soft prompt fires weeks before the hard publish stop. ADR-036 specifies the exact contract.
|
|
154
|
+
|
|
155
|
+
### Trigger 1: First-run prompt from manage-problem / work-problems
|
|
156
|
+
|
|
157
|
+
When `/wr-itil:manage-problem` or `/wr-itil:work-problems` fires in a foreground session, the host skill's preamble checks:
|
|
158
|
+
|
|
159
|
+
- Is `.github/ISSUE_TEMPLATE/` missing OR are any of the four other intake files absent?
|
|
160
|
+
- Is `.claude/.intake-scaffold-declined` absent?
|
|
161
|
+
- Is `.claude/.intake-scaffold-done` absent?
|
|
162
|
+
|
|
163
|
+
When all three checks pass, the host emits a one-shot `AskUserQuestion` prompt with three options — **Scaffold now**, **Not now (ask again next session)**, **Decline (never prompt in this project)**. On "Decline", write `.claude/.intake-scaffold-declined` and never re-prompt unless the user deletes the marker.
|
|
164
|
+
|
|
165
|
+
**AFK fail-safe**: when the host is invoked from an AFK orchestrator, do NOT fire the prompt. Append a one-line "pending intake scaffold" note to the iteration report and continue. The user catches up on next interactive session.
|
|
166
|
+
|
|
167
|
+
### Trigger 2: Pre-publish PreToolUse gate (hard stop)
|
|
168
|
+
|
|
169
|
+
`packages/itil/hooks/pre-publish-intake-gate.sh` matches `npm publish` and `gh pr merge ... changeset-release/*` and denies if any of the five intake files are missing AND the decline marker is absent AND `INTAKE_BYPASS` is not set.
|
|
170
|
+
|
|
171
|
+
**Override paths** (in priority order):
|
|
172
|
+
1. `INTAKE_BYPASS=1 npm publish` — short-circuits the gate before existence check (consistent with `RISK_BYPASS` naming).
|
|
173
|
+
2. `.claude/.intake-scaffold-declined` marker — explicit opt-out.
|
|
174
|
+
3. Run `/wr-itil:scaffold-intake` to remove the cause.
|
|
175
|
+
|
|
176
|
+
### Trigger 3: Optional CI check (deferred to v2)
|
|
177
|
+
|
|
178
|
+
`--ci` flag emits `.github/workflows/intake-check.yml` asserting the four files exist on every PR. Not shipped by default; layered ADD-on for adopters with GitHub Actions.
|
|
179
|
+
|
|
180
|
+
## Idempotent re-runs and template drift
|
|
181
|
+
|
|
182
|
+
When this repo's intake files evolve (e.g., a future P066-style reform), downstream adopters' previously-scaffolded files stay frozen. Re-running the skill at any time picks up the diff: the present-and-outdated branch reports each file's diff and, with `--force`, performs diff-and-replace. Template drift policy (when this should fire automatically) is out of scope for v1 — see ADR-036 Reassessment Criteria.
|
|
183
|
+
|
|
184
|
+
## Related
|
|
185
|
+
|
|
186
|
+
- [ADR-036](../../../../docs/decisions/036-scaffold-downstream-oss-intake.proposed.md) — design record (driver decision).
|
|
187
|
+
- [ADR-024](../../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) — sibling skill (`/wr-itil:report-upstream`); reciprocal pair.
|
|
188
|
+
- [ADR-032](../../../../docs/decisions/032-governance-skill-invocation-patterns.proposed.md) — foreground-synchronous pattern + Rule 6 audit requirement.
|
|
189
|
+
- [ADR-013](../../../../docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md) — Rule 1 (interactive prompt) + Rule 6 (AFK fail-safe).
|
|
190
|
+
- [ADR-009](../../../../docs/decisions/009-gate-marker-lifecycle.proposed.md) — marker lifecycle (the `.intake-scaffold-{done,declined}` markers).
|
|
191
|
+
- [ADR-014](../../../../docs/decisions/014-governance-skills-commit-their-own-work.proposed.md) — commit discipline.
|
|
192
|
+
- P065 — driver ticket.
|
|
193
|
+
- P066 — parent (template seed source); the corrected problem-first intake shape this skill propagates.
|
|
194
|
+
- JTBD-301 — primary persona; downstream-project intake coverage.
|
|
195
|
+
- JTBD-101 — clear patterns for adopters extending the suite.
|
|
196
|
+
- JTBD-006 — AFK fail-safe constraint.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in {{project_name}}. This guide covers how to contribute code, decisions, and bug reports.
|
|
4
|
+
|
|
5
|
+
## Before you start
|
|
6
|
+
|
|
7
|
+
- **Problems**: open an issue first using the **Report a problem** template under `.github/ISSUE_TEMPLATE/`. You do not need to pre-classify it as a bug or a feature -- this project practises ITIL problem management, so triage decides the category. Drive-by PRs without an issue are harder to merge because the problem framing is missing.
|
|
8
|
+
- **Security vulnerabilities**: do not open a public issue. See [SECURITY.md](SECURITY.md).
|
|
9
|
+
- **Usage questions**: use [Discussions]({{project_url}}/discussions), not issues.
|
|
10
|
+
|
|
11
|
+
## Pull requests
|
|
12
|
+
|
|
13
|
+
1. **Branch off the default branch**.
|
|
14
|
+
2. **Make your change** with tests where applicable.
|
|
15
|
+
3. **Open the PR** with a clear description of what is changing and why.
|
|
16
|
+
|
|
17
|
+
### Commit style
|
|
18
|
+
|
|
19
|
+
- Follow Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, etc.).
|
|
20
|
+
- Reference problem tickets in the commit message when the work closes one.
|
|
21
|
+
- Sign-off is not required.
|
|
22
|
+
|
|
23
|
+
## Code of conduct
|
|
24
|
+
|
|
25
|
+
Be kind. Engage in good faith. Critique ideas, not people. Maintainers reserve the right to lock or remove discussions, issues, or PRs that derail into personal attacks.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
By contributing, you agree your contributions are licensed under {{project_name}}'s license. See the project root for the canonical LICENSE file.
|
|
30
|
+
|
|
31
|
+
Copyright (c) {{year}} the {{project_name}} contributors.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
**Do not file a public issue for security vulnerabilities.**
|
|
6
|
+
|
|
7
|
+
{{security_contact}} to disclose privately. Maintainers receive an email; the report stays private until a fix ships.
|
|
8
|
+
|
|
9
|
+
A useful report includes:
|
|
10
|
+
|
|
11
|
+
- The affected version of {{project_name}} (e.g. `{{project_name}}@x.y.z`).
|
|
12
|
+
- The runtime / platform you reproduced on.
|
|
13
|
+
- Reproduction steps that demonstrate the impact.
|
|
14
|
+
- Your assessment of impact (data loss, code execution, escalation, secret leak, etc.).
|
|
15
|
+
- Any suggested fix or mitigation.
|
|
16
|
+
|
|
17
|
+
## What's in scope
|
|
18
|
+
|
|
19
|
+
Code shipped from this repository: {{project_url}}.
|
|
20
|
+
|
|
21
|
+
## What's out of scope
|
|
22
|
+
|
|
23
|
+
- Vulnerabilities in third-party dependencies -- report to their maintainers.
|
|
24
|
+
- Issues that require an attacker to already have local code-execution on the user's machine.
|
|
25
|
+
|
|
26
|
+
## Disclosure timeline
|
|
27
|
+
|
|
28
|
+
We aim to:
|
|
29
|
+
|
|
30
|
+
- **Acknowledge** the report within 7 calendar days.
|
|
31
|
+
- **Provide an initial assessment** (in scope, severity, planned fix) within 14 days.
|
|
32
|
+
- **Ship a fix** within 90 days for confirmed vulnerabilities, with most resolved sooner.
|
|
33
|
+
- **Coordinate disclosure** with the reporter so a public advisory and credit can be published when the fix lands.
|
|
34
|
+
|
|
35
|
+
If we cannot meet a 90-day timeline, we will say so in the advisory thread before the deadline.
|
|
36
|
+
|
|
37
|
+
## Credit
|
|
38
|
+
|
|
39
|
+
Reporters who follow this private-disclosure path are credited in the published advisory unless they ask to remain anonymous.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Getting Help
|
|
2
|
+
|
|
3
|
+
Where to go depends on what you need:
|
|
4
|
+
|
|
5
|
+
## Usage questions, configuration help, pattern advice
|
|
6
|
+
|
|
7
|
+
[GitHub Discussions]({{project_url}}/discussions). Discussions stay searchable for the next person who hits the same question.
|
|
8
|
+
|
|
9
|
+
Before posting, search existing discussions and the project README.
|
|
10
|
+
|
|
11
|
+
## Report a problem
|
|
12
|
+
|
|
13
|
+
[Open an issue]({{project_url}}/issues/new/choose) using the **Report a problem** template. You do not need to pre-classify it as a bug or feature -- this project practises ITIL problem management, and triage decides whether the root cause is a defect, a missing capability, a documentation gap, or something else. Describe what you observed and let triage decide the category.
|
|
14
|
+
|
|
15
|
+
Include:
|
|
16
|
+
|
|
17
|
+
- A description of what is happening and what you expected
|
|
18
|
+
- Observable symptoms (errors, command output, transcripts, screenshots -- redact secrets)
|
|
19
|
+
- Any workaround you tried, even if it did not help
|
|
20
|
+
- Affected component and version of {{project_name}}
|
|
21
|
+
- Runtime / platform and operating system
|
|
22
|
+
- Minimal reproduction steps or evidence
|
|
23
|
+
|
|
24
|
+
The template prompts for these. Issues without the template fields are slower to triage.
|
|
25
|
+
|
|
26
|
+
## Security vulnerabilities
|
|
27
|
+
|
|
28
|
+
**Do not open a public issue.** {{security_contact}} for private disclosure. See [SECURITY.md](SECURITY.md) for the disclosure timeline and what's in scope.
|
|
29
|
+
|
|
30
|
+
## Project documentation
|
|
31
|
+
|
|
32
|
+
Start with the README at the root of {{project_url}} for project-specific behaviour.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Ask a question (GitHub Discussions)
|
|
4
|
+
url: {{project_url}}/discussions
|
|
5
|
+
about: For usage help, configuration questions, and pattern advice. The issue tracker is for problems -- triage decides whether the root cause is a defect, a missing capability, or something else.
|
|
6
|
+
- name: Report a security vulnerability
|
|
7
|
+
url: {{project_url}}/security/advisories/new
|
|
8
|
+
about: Use GitHub Security Advisories for private disclosure. Do not file a public issue. See SECURITY.md.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Report a problem
|
|
2
|
+
description: Report a problem with {{project_name}}. You do not need to pre-classify it as a bug or feature -- triage decides.
|
|
3
|
+
title: "[problem] "
|
|
4
|
+
labels: ["problem", "needs-triage"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for reporting a problem. This project practises ITIL problem management -- you describe what you observed, and triage decides whether the root cause is a defect, a missing capability, a documentation gap, or something else. You do not need to choose in advance.
|
|
10
|
+
|
|
11
|
+
For security vulnerabilities, **do not file here** -- use [GitHub Security Advisories]({{project_url}}/security/advisories/new) instead. For usage questions, use [Discussions]({{project_url}}/discussions).
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: description
|
|
15
|
+
attributes:
|
|
16
|
+
label: Description
|
|
17
|
+
description: What is happening? What did you expect instead? Describe the observation, not the fix.
|
|
18
|
+
placeholder: |
|
|
19
|
+
e.g. "When I run the tool in a fresh project, it reports no items even though the source data has them. I expected it to list them."
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
- type: textarea
|
|
24
|
+
id: symptoms
|
|
25
|
+
attributes:
|
|
26
|
+
label: Symptoms
|
|
27
|
+
description: Observable behaviour -- error messages, command output, transcript snippets, screenshots. Redact secrets.
|
|
28
|
+
placeholder: |
|
|
29
|
+
- Command returns exit code 1
|
|
30
|
+
- Output contains "project root not found"
|
|
31
|
+
- ...
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
|
|
35
|
+
- type: textarea
|
|
36
|
+
id: workaround
|
|
37
|
+
attributes:
|
|
38
|
+
label: Workaround
|
|
39
|
+
description: Anything you tried that got you unstuck, even if temporary. "None found" is a valid answer.
|
|
40
|
+
placeholder: |
|
|
41
|
+
Retried after restarting -- same result. No workaround found.
|
|
42
|
+
|
|
43
|
+
- type: input
|
|
44
|
+
id: affected-component
|
|
45
|
+
attributes:
|
|
46
|
+
label: Affected component
|
|
47
|
+
description: Which part of {{project_name}} is involved? "multiple" or "unsure" is a valid answer -- triage will re-scope.
|
|
48
|
+
placeholder: "e.g. CLI / UI / specific module"
|
|
49
|
+
validations:
|
|
50
|
+
required: true
|
|
51
|
+
|
|
52
|
+
- type: input
|
|
53
|
+
id: frequency
|
|
54
|
+
attributes:
|
|
55
|
+
label: Frequency
|
|
56
|
+
description: How often does this happen? (every time, intermittently, once, ...)
|
|
57
|
+
placeholder: "e.g. every time I run the command"
|
|
58
|
+
validations:
|
|
59
|
+
required: true
|
|
60
|
+
|
|
61
|
+
- type: textarea
|
|
62
|
+
id: environment
|
|
63
|
+
attributes:
|
|
64
|
+
label: Environment
|
|
65
|
+
description: Version, runtime, and operating system.
|
|
66
|
+
placeholder: |
|
|
67
|
+
- {{project_name}} version: x.y.z
|
|
68
|
+
- Runtime: Node 20.x / Python 3.12 / ...
|
|
69
|
+
- OS: macOS 15.2
|
|
70
|
+
validations:
|
|
71
|
+
required: true
|
|
72
|
+
|
|
73
|
+
- type: textarea
|
|
74
|
+
id: evidence
|
|
75
|
+
attributes:
|
|
76
|
+
label: Evidence
|
|
77
|
+
description: Minimal reproduction steps, transcript links, screenshots, or related prior discussions. Feeds the investigation tasks triage will open.
|
|
78
|
+
placeholder: |
|
|
79
|
+
1. Clone the repo
|
|
80
|
+
2. Run the failing command
|
|
81
|
+
3. Observe output
|
|
82
|
+
|
|
83
|
+
- type: textarea
|
|
84
|
+
id: additional
|
|
85
|
+
attributes:
|
|
86
|
+
label: Additional context
|
|
87
|
+
description: Anything else relevant -- related tickets, prior art, a hypothesis you already formed.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036 / ADR-037 Confirmation: scaffold-intake SKILL.md must
|
|
4
|
+
# encode the contract documented in ADR-036's Decision Outcome.
|
|
5
|
+
#
|
|
6
|
+
# Doc-lint structural test (Permitted Exception per ADR-005 — structural
|
|
7
|
+
# SKILL.md content checks, not behavioural). Mirrors the report-upstream
|
|
8
|
+
# contract bats pattern and the ADR-037 "Source review (at implementation
|
|
9
|
+
# time)" requirement that every shipped skill ships at least one
|
|
10
|
+
# `<skill>-contract.bats`.
|
|
11
|
+
|
|
12
|
+
setup() {
|
|
13
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
14
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/scaffold-intake/SKILL.md"
|
|
15
|
+
TEMPLATE_DIR="$REPO_ROOT/packages/itil/skills/scaffold-intake/templates"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@test "scaffold-intake: SKILL.md exists" {
|
|
19
|
+
[ -f "$SKILL_MD" ]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@test "scaffold-intake: SKILL.md frontmatter declares the skill name" {
|
|
23
|
+
run grep -F 'name: wr-itil:scaffold-intake' "$SKILL_MD"
|
|
24
|
+
[ "$status" -eq 0 ]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@test "scaffold-intake: SKILL.md cross-references ADR-036 (driver decision)" {
|
|
28
|
+
run grep -F 'ADR-036' "$SKILL_MD"
|
|
29
|
+
[ "$status" -eq 0 ]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@test "scaffold-intake: SKILL.md cites ADR-032 foreground-synchronous pattern" {
|
|
33
|
+
run grep -F 'ADR-032' "$SKILL_MD"
|
|
34
|
+
[ "$status" -eq 0 ]
|
|
35
|
+
run grep -iE 'foreground.synchronous' "$SKILL_MD"
|
|
36
|
+
[ "$status" -eq 0 ]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@test "scaffold-intake: SKILL.md contains an explicit Rule 6 audit section (ADR-032 + ADR-013 Rule 6)" {
|
|
40
|
+
# ADR-032 line 191 requires every skill that uses AskUserQuestion to
|
|
41
|
+
# carry an enumerable Rule 6 audit. ADR-036 lines 92-97 ship the table
|
|
42
|
+
# shape.
|
|
43
|
+
run grep -iE 'rule 6 audit|rule-6 audit' "$SKILL_MD"
|
|
44
|
+
[ "$status" -eq 0 ]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@test "scaffold-intake: SKILL.md enumerates the three trigger surfaces (ADR-036)" {
|
|
48
|
+
# Trigger 1: first-run prompt from manage-problem / work-problems.
|
|
49
|
+
# Trigger 2: pre-publish PreToolUse gate (hard stop).
|
|
50
|
+
# Trigger 3: optional CI check (deferred / --ci flag).
|
|
51
|
+
run grep -iE 'trigger 1|first-run' "$SKILL_MD"
|
|
52
|
+
[ "$status" -eq 0 ]
|
|
53
|
+
run grep -iE 'trigger 2|pre-publish' "$SKILL_MD"
|
|
54
|
+
[ "$status" -eq 0 ]
|
|
55
|
+
run grep -iE 'trigger 3|ci check|--ci' "$SKILL_MD"
|
|
56
|
+
[ "$status" -eq 0 ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "scaffold-intake: SKILL.md cites the INTAKE_BYPASS env override (ADR-036)" {
|
|
60
|
+
run grep -F 'INTAKE_BYPASS' "$SKILL_MD"
|
|
61
|
+
[ "$status" -eq 0 ]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@test "scaffold-intake: SKILL.md cites both marker files (ADR-036 + ADR-009)" {
|
|
65
|
+
run grep -F '.claude/.intake-scaffold-done' "$SKILL_MD"
|
|
66
|
+
[ "$status" -eq 0 ]
|
|
67
|
+
run grep -F '.claude/.intake-scaffold-declined' "$SKILL_MD"
|
|
68
|
+
[ "$status" -eq 0 ]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@test "scaffold-intake: SKILL.md documents idempotent + --force semantics (ADR-036)" {
|
|
72
|
+
run grep -iE 'idempotent' "$SKILL_MD"
|
|
73
|
+
[ "$status" -eq 0 ]
|
|
74
|
+
run grep -F -- '--force' "$SKILL_MD"
|
|
75
|
+
[ "$status" -eq 0 ]
|
|
76
|
+
run grep -F -- '--dry-run' "$SKILL_MD"
|
|
77
|
+
[ "$status" -eq 0 ]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@test "scaffold-intake: SKILL.md enumerates the five required intake files (ADR-036 Detection 5)" {
|
|
81
|
+
for path in 'config.yml' 'problem-report.yml' 'SECURITY.md' 'SUPPORT.md' 'CONTRIBUTING.md'; do
|
|
82
|
+
run grep -F "$path" "$SKILL_MD"
|
|
83
|
+
[ "$status" -eq 0 ] || { echo "missing reference: $path"; return 1; }
|
|
84
|
+
done
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@test "scaffold-intake: SKILL.md documents AFK fail-safe (no auto-scaffold) per JTBD-006" {
|
|
88
|
+
# JTBD-006 + ADR-013 Rule 6: AFK orchestrator branch must NOT auto-write
|
|
89
|
+
# template files. The skill documents this as a Rule 6 fail-safe.
|
|
90
|
+
run grep -iE 'afk|rule 6' "$SKILL_MD"
|
|
91
|
+
[ "$status" -eq 0 ]
|
|
92
|
+
run grep -iE 'no auto.?scaffold|do not auto.?scaffold|silent note|pending.intake.scaffold' "$SKILL_MD"
|
|
93
|
+
[ "$status" -eq 0 ]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@test "scaffold-intake: SKILL.md cites ADR-014 commit discipline" {
|
|
97
|
+
run grep -F 'ADR-014' "$SKILL_MD"
|
|
98
|
+
[ "$status" -eq 0 ]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# --- Templates directory ---
|
|
102
|
+
|
|
103
|
+
@test "scaffold-intake: templates/ directory exists with five template seeds" {
|
|
104
|
+
[ -d "$TEMPLATE_DIR" ]
|
|
105
|
+
[ -f "$TEMPLATE_DIR/config.yml.tmpl" ]
|
|
106
|
+
[ -f "$TEMPLATE_DIR/problem-report.yml.tmpl" ]
|
|
107
|
+
[ -f "$TEMPLATE_DIR/SECURITY.md.tmpl" ]
|
|
108
|
+
[ -f "$TEMPLATE_DIR/SUPPORT.md.tmpl" ]
|
|
109
|
+
[ -f "$TEMPLATE_DIR/CONTRIBUTING.md.tmpl" ]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@test "scaffold-intake: problem-report.yml.tmpl is problem-first (P066 shape)" {
|
|
113
|
+
run grep -F 'title: "[problem] "' "$TEMPLATE_DIR/problem-report.yml.tmpl"
|
|
114
|
+
[ "$status" -eq 0 ]
|
|
115
|
+
run grep -F 'labels: ["problem", "needs-triage"]' "$TEMPLATE_DIR/problem-report.yml.tmpl"
|
|
116
|
+
[ "$status" -eq 0 ]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@test "scaffold-intake: templates use mustache-style substitution tokens (ADR-036)" {
|
|
120
|
+
# ADR-036 declares the token list. Each token must appear in at least
|
|
121
|
+
# one template file for the substitution surface to be wired.
|
|
122
|
+
for token in '{{project_name}}' '{{project_url}}' '{{security_contact}}'; do
|
|
123
|
+
run grep -rF "$token" "$TEMPLATE_DIR/"
|
|
124
|
+
[ "$status" -eq 0 ] || { echo "missing token: $token"; return 1; }
|
|
125
|
+
done
|
|
126
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036 Confirmation behavioural-replay 1+2:
|
|
4
|
+
# Fixture-based behavioural test for scaffold-intake. Exercises the skill's
|
|
5
|
+
# core write-and-substitute contract against a mock empty downstream repo.
|
|
6
|
+
#
|
|
7
|
+
# This is the behavioural counterpart to scaffold-intake-contract.bats.
|
|
8
|
+
# It does NOT invoke the skill via Claude Code — it asserts the
|
|
9
|
+
# contract by replaying the skill's documented bash steps:
|
|
10
|
+
# 1. detect project name + url from package.json
|
|
11
|
+
# 2. enumerate missing intake files
|
|
12
|
+
# 3. write substituted templates to the correct paths
|
|
13
|
+
# 4. mark .claude/.intake-scaffold-done
|
|
14
|
+
# The bats reads templates/*.tmpl directly, applies the substitution rules
|
|
15
|
+
# defined in ADR-036, and asserts on the resulting files.
|
|
16
|
+
#
|
|
17
|
+
# Per feedback_behavioural_tests.md (P081): asserts observable file-system
|
|
18
|
+
# outcomes (files exist, content substituted, idempotent re-run no-ops),
|
|
19
|
+
# not source content.
|
|
20
|
+
|
|
21
|
+
setup() {
|
|
22
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
23
|
+
TEMPLATE_DIR="$REPO_ROOT/packages/itil/skills/scaffold-intake/templates"
|
|
24
|
+
ORIG_DIR="$PWD"
|
|
25
|
+
TEST_DIR=$(mktemp -d)
|
|
26
|
+
cd "$TEST_DIR"
|
|
27
|
+
|
|
28
|
+
# Seed a minimal package.json so the skill's detection step has inputs.
|
|
29
|
+
cat > package.json <<'JSON'
|
|
30
|
+
{
|
|
31
|
+
"name": "example-downstream",
|
|
32
|
+
"repository": {
|
|
33
|
+
"url": "https://github.com/example/downstream.git"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
JSON
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
teardown() {
|
|
40
|
+
cd "$ORIG_DIR"
|
|
41
|
+
rm -rf "$TEST_DIR"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Helper: replay the skill's substitute-and-write step for one template.
|
|
45
|
+
# This is the inline mustache-style substitution declared in ADR-036.
|
|
46
|
+
scaffold_one() {
|
|
47
|
+
local tmpl="$1"
|
|
48
|
+
local out="$2"
|
|
49
|
+
local project_name="example-downstream"
|
|
50
|
+
local project_url="https://github.com/example/downstream"
|
|
51
|
+
local security_contact="Use GitHub Security Advisories"
|
|
52
|
+
local plugin_list="@windyroad/itil"
|
|
53
|
+
local year="2026"
|
|
54
|
+
mkdir -p "$(dirname "$out")"
|
|
55
|
+
sed \
|
|
56
|
+
-e "s|{{project_name}}|$project_name|g" \
|
|
57
|
+
-e "s|{{project_url}}|$project_url|g" \
|
|
58
|
+
-e "s|{{security_contact}}|$security_contact|g" \
|
|
59
|
+
-e "s|{{plugin_list}}|$plugin_list|g" \
|
|
60
|
+
-e "s|{{year}}|$year|g" \
|
|
61
|
+
"$TEMPLATE_DIR/$tmpl" > "$out"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
scaffold_all() {
|
|
65
|
+
scaffold_one "config.yml.tmpl" ".github/ISSUE_TEMPLATE/config.yml"
|
|
66
|
+
scaffold_one "problem-report.yml.tmpl" ".github/ISSUE_TEMPLATE/problem-report.yml"
|
|
67
|
+
scaffold_one "SECURITY.md.tmpl" "SECURITY.md"
|
|
68
|
+
scaffold_one "SUPPORT.md.tmpl" "SUPPORT.md"
|
|
69
|
+
scaffold_one "CONTRIBUTING.md.tmpl" "CONTRIBUTING.md"
|
|
70
|
+
mkdir -p .claude
|
|
71
|
+
: > .claude/.intake-scaffold-done
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# --- Empty repo: scaffold writes all five files ---
|
|
75
|
+
|
|
76
|
+
@test "fixture: empty repo gains all five intake files" {
|
|
77
|
+
scaffold_all
|
|
78
|
+
[ -f .github/ISSUE_TEMPLATE/config.yml ]
|
|
79
|
+
[ -f .github/ISSUE_TEMPLATE/problem-report.yml ]
|
|
80
|
+
[ -f SECURITY.md ]
|
|
81
|
+
[ -f SUPPORT.md ]
|
|
82
|
+
[ -f CONTRIBUTING.md ]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@test "fixture: substitution tokens are resolved (no raw {{...}} left in scaffolded files)" {
|
|
86
|
+
scaffold_all
|
|
87
|
+
for f in \
|
|
88
|
+
.github/ISSUE_TEMPLATE/config.yml \
|
|
89
|
+
.github/ISSUE_TEMPLATE/problem-report.yml \
|
|
90
|
+
SECURITY.md \
|
|
91
|
+
SUPPORT.md \
|
|
92
|
+
CONTRIBUTING.md; do
|
|
93
|
+
run grep -F '{{' "$f"
|
|
94
|
+
[ "$status" -ne 0 ] || { echo "raw mustache token left in $f"; return 1; }
|
|
95
|
+
done
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@test "fixture: project_name substitution propagated into scaffolded SECURITY.md or CONTRIBUTING.md" {
|
|
99
|
+
scaffold_all
|
|
100
|
+
# The project name should appear at least once in either SECURITY.md or
|
|
101
|
+
# CONTRIBUTING.md; the exact placement is template-dependent but
|
|
102
|
+
# ABSENCE from both files would indicate a substitution miss.
|
|
103
|
+
run bash -c "grep -F 'example-downstream' SECURITY.md SUPPORT.md CONTRIBUTING.md"
|
|
104
|
+
[ "$status" -eq 0 ]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@test "fixture: scaffolded problem-report.yml retains problem-first shape (P066)" {
|
|
108
|
+
scaffold_all
|
|
109
|
+
run grep -F 'title: "[problem] "' .github/ISSUE_TEMPLATE/problem-report.yml
|
|
110
|
+
[ "$status" -eq 0 ]
|
|
111
|
+
run grep -F 'labels: ["problem", "needs-triage"]' .github/ISSUE_TEMPLATE/problem-report.yml
|
|
112
|
+
[ "$status" -eq 0 ]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@test "fixture: done marker written after successful scaffold" {
|
|
116
|
+
scaffold_all
|
|
117
|
+
[ -f .claude/.intake-scaffold-done ]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# --- Idempotency: re-scaffolding produces no diff ---
|
|
121
|
+
|
|
122
|
+
@test "fixture: full re-application is idempotent (no diff)" {
|
|
123
|
+
scaffold_all
|
|
124
|
+
# Snapshot.
|
|
125
|
+
cp -R . "$TEST_DIR/.snapshot-1"
|
|
126
|
+
# Re-apply.
|
|
127
|
+
scaffold_all
|
|
128
|
+
# Diff against snapshot — exclude the snapshot dir itself + any tmp.
|
|
129
|
+
run diff -ru \
|
|
130
|
+
--exclude='.snapshot-1' \
|
|
131
|
+
--exclude='.git' \
|
|
132
|
+
"$TEST_DIR/.snapshot-1" "$TEST_DIR"
|
|
133
|
+
# diff exit 0 means no differences.
|
|
134
|
+
[ "$status" -eq 0 ]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# --- Partial repo: pre-existing CONTRIBUTING.md is preserved ---
|
|
138
|
+
|
|
139
|
+
@test "fixture: pre-existing CONTRIBUTING.md is preserved (idempotent skip)" {
|
|
140
|
+
echo "# Custom Contributing" > CONTRIBUTING.md
|
|
141
|
+
echo "Custom adopter content; must not be overwritten by scaffold." >> CONTRIBUTING.md
|
|
142
|
+
ORIGINAL=$(cat CONTRIBUTING.md)
|
|
143
|
+
|
|
144
|
+
# Scaffold the OTHER four files (skill skips the existing one).
|
|
145
|
+
scaffold_one "config.yml.tmpl" ".github/ISSUE_TEMPLATE/config.yml"
|
|
146
|
+
scaffold_one "problem-report.yml.tmpl" ".github/ISSUE_TEMPLATE/problem-report.yml"
|
|
147
|
+
scaffold_one "SECURITY.md.tmpl" "SECURITY.md"
|
|
148
|
+
scaffold_one "SUPPORT.md.tmpl" "SUPPORT.md"
|
|
149
|
+
# Deliberately do NOT call scaffold_one for CONTRIBUTING.md — that's
|
|
150
|
+
# the skill's idempotent skip behaviour for present files.
|
|
151
|
+
|
|
152
|
+
# Other files were created.
|
|
153
|
+
[ -f .github/ISSUE_TEMPLATE/config.yml ]
|
|
154
|
+
[ -f .github/ISSUE_TEMPLATE/problem-report.yml ]
|
|
155
|
+
[ -f SECURITY.md ]
|
|
156
|
+
[ -f SUPPORT.md ]
|
|
157
|
+
|
|
158
|
+
# CONTRIBUTING.md unchanged.
|
|
159
|
+
AFTER=$(cat CONTRIBUTING.md)
|
|
160
|
+
[ "$ORIGINAL" = "$AFTER" ]
|
|
161
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036 / ADR-037 Confirmation: scaffold-intake's templates and
|
|
4
|
+
# SKILL.md must not leak absolute paths, host-specific identifiers, or
|
|
5
|
+
# secrets that would compromise downstream adopters who scaffold from
|
|
6
|
+
# them.
|
|
7
|
+
#
|
|
8
|
+
# Sentinel test: per ADR-037 "Source review", every skill that emits
|
|
9
|
+
# user-substituted content ships a `<skill>-secrets-absent.bats` to
|
|
10
|
+
# catch hardcoded environment leakage at PR time.
|
|
11
|
+
|
|
12
|
+
setup() {
|
|
13
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
14
|
+
TEMPLATE_DIR="$REPO_ROOT/packages/itil/skills/scaffold-intake/templates"
|
|
15
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/scaffold-intake/SKILL.md"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# --- Templates: no absolute paths from the author's environment ---
|
|
19
|
+
|
|
20
|
+
@test "secrets-absent: templates contain no /Users/ or /home/ absolute paths" {
|
|
21
|
+
run grep -rE '/Users/[a-zA-Z0-9_-]+/' "$TEMPLATE_DIR/"
|
|
22
|
+
[ "$status" -ne 0 ]
|
|
23
|
+
run grep -rE '/home/[a-zA-Z0-9_-]+/' "$TEMPLATE_DIR/"
|
|
24
|
+
[ "$status" -ne 0 ]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@test "secrets-absent: templates contain no Windows-style absolute paths" {
|
|
28
|
+
run grep -rE '[A-Z]:\\Users\\' "$TEMPLATE_DIR/"
|
|
29
|
+
[ "$status" -ne 0 ]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# --- Templates: no obvious credential shapes ---
|
|
33
|
+
|
|
34
|
+
@test "secrets-absent: templates contain no credential-shaped tokens (AKIA, ghp_, sk_, github_pat_)" {
|
|
35
|
+
run grep -rE 'AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{80,}|sk_(live|test)_[A-Za-z0-9]{24,}' "$TEMPLATE_DIR/"
|
|
36
|
+
[ "$status" -ne 0 ]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@test "secrets-absent: templates contain no aws_access or aws_secret keys in plain text" {
|
|
40
|
+
run grep -rEi 'aws_access_key_id|aws_secret_access_key' "$TEMPLATE_DIR/"
|
|
41
|
+
[ "$status" -ne 0 ]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# --- Templates: name-of-this-repo must be substituted, not hardcoded ---
|
|
45
|
+
|
|
46
|
+
@test "secrets-absent: templates do not hardcode 'windyroad/agent-plugins' (must use {{project_url}}/{{project_name}} substitution)" {
|
|
47
|
+
# The author repo is windyroad/agent-plugins; downstream-scaffolded
|
|
48
|
+
# files must use the per-project substitution token, not the literal
|
|
49
|
+
# author repo. Otherwise downstream adopters get our SECURITY.md
|
|
50
|
+
# advisory URL pointing to OUR security advisories, which is wrong.
|
|
51
|
+
#
|
|
52
|
+
# Allow it ONLY in commentary lines that explain "templated from this
|
|
53
|
+
# repo's intake".
|
|
54
|
+
run grep -rl 'windyroad/agent-plugins' "$TEMPLATE_DIR/"
|
|
55
|
+
if [ "$status" -eq 0 ]; then
|
|
56
|
+
# Anything found must be inside a comment explicitly framing it as
|
|
57
|
+
# an example. Fail otherwise.
|
|
58
|
+
while read -r f; do
|
|
59
|
+
run grep -nE 'windyroad/agent-plugins' "$f"
|
|
60
|
+
while read -r line; do
|
|
61
|
+
line_no="${line%%:*}"
|
|
62
|
+
line_text="${line#*:}"
|
|
63
|
+
# Allow only if line begins with a comment marker (#) AND mentions example/seed.
|
|
64
|
+
if echo "$line_text" | grep -qE '^[[:space:]]*#' && echo "$line_text" | grep -qiE 'example|seed|template|reference'; then
|
|
65
|
+
continue
|
|
66
|
+
fi
|
|
67
|
+
echo "hardcoded windyroad/agent-plugins reference at $f:$line_no"
|
|
68
|
+
echo " $line_text"
|
|
69
|
+
return 1
|
|
70
|
+
done <<< "$output"
|
|
71
|
+
done < <(grep -rl 'windyroad/agent-plugins' "$TEMPLATE_DIR/")
|
|
72
|
+
fi
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# --- SKILL.md: no transcript-leak shapes ---
|
|
76
|
+
|
|
77
|
+
@test "secrets-absent: SKILL.md contains no /Users/ or /home/ absolute paths" {
|
|
78
|
+
run grep -E '/Users/[a-zA-Z0-9_-]+/' "$SKILL_MD"
|
|
79
|
+
[ "$status" -ne 0 ]
|
|
80
|
+
run grep -E '/home/[a-zA-Z0-9_-]+/' "$SKILL_MD"
|
|
81
|
+
[ "$status" -ne 0 ]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@test "secrets-absent: SKILL.md contains no credential-shaped tokens" {
|
|
85
|
+
run grep -E 'AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{80,}' "$SKILL_MD"
|
|
86
|
+
[ "$status" -ne 0 ]
|
|
87
|
+
}
|
|
@@ -14,6 +14,24 @@ The user is AFK during this process, so every decision point that would normally
|
|
|
14
14
|
|
|
15
15
|
Each iteration is one cycle of: scan backlog, pick highest-WSJF problem, work it, report result. The loop continues until a stop condition is met.
|
|
16
16
|
|
|
17
|
+
## First-run intake-scaffold pointer (P065 / ADR-036)
|
|
18
|
+
|
|
19
|
+
This skill is one of the two host skills wired to surface the [`/wr-itil:scaffold-intake`](../scaffold-intake/SKILL.md) skill on first invocation in a project that has not yet adopted the OSS intake surface. The contract is documented in [ADR-036](../../../../docs/decisions/036-scaffold-downstream-oss-intake.proposed.md) (Scaffold downstream OSS intake — skill + layered triggers).
|
|
20
|
+
|
|
21
|
+
**Preamble check** (run once at session start, before Step 0 of the loop):
|
|
22
|
+
|
|
23
|
+
1. Look for the four intake paths: `.github/ISSUE_TEMPLATE/config.yml`, `.github/ISSUE_TEMPLATE/problem-report.yml`, `SECURITY.md`, `SUPPORT.md`, `CONTRIBUTING.md`.
|
|
24
|
+
2. Look for `.claude/.intake-scaffold-declined` (explicit decline marker — never re-prompt).
|
|
25
|
+
3. Look for `.claude/.intake-scaffold-done` (done marker — already scaffolded).
|
|
26
|
+
|
|
27
|
+
If any intake file is missing AND both markers are absent: this skill is **always invoked from an AFK orchestrator context** (per the skill's allowed-tools and persona). The Rule 6 fail-safe applies unconditionally:
|
|
28
|
+
|
|
29
|
+
- Do **not** fire `AskUserQuestion`.
|
|
30
|
+
- Do **not** auto-scaffold.
|
|
31
|
+
- Append a one-line `"pending intake scaffold"` note to the iteration's `ITERATION_SUMMARY` notes field. The note is a per-iteration audit trail signal — accumulating one line per AFK iter is acceptable per ADR-036 § Bad consequences and JTBD-006 "audit trail — every action taken during AFK mode should be traceable".
|
|
32
|
+
|
|
33
|
+
The user reviews the pending note on their next interactive session and runs `/wr-itil:scaffold-intake` (or `/wr-itil:manage-problem` with the foreground prompt branch) at that point. JTBD-006 forbids the agent from making this judgement call autonomously.
|
|
34
|
+
|
|
17
35
|
### Step 0: Preflight (per ADR-019)
|
|
18
36
|
|
|
19
37
|
Before opening the work loop, reconcile local state with origin so the orchestrator does not iterate against a stale backlog or create tickets with IDs that collide with parallel sessions (P040).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P065 / ADR-036 Confirmation line 199: work-problems SKILL.md must wire
|
|
4
|
+
# the first-run intake-scaffold pointer + AFK fail-safe + marker contract.
|
|
5
|
+
#
|
|
6
|
+
# Doc-lint structural test (Permitted Exception per ADR-005). Sister bats
|
|
7
|
+
# to manage-problem-first-run-intake-prompt.bats.
|
|
8
|
+
|
|
9
|
+
setup() {
|
|
10
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
11
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/work-problems/SKILL.md"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@test "work-problems: SKILL.md cites ADR-036 (first-run-prompt contract)" {
|
|
15
|
+
run grep -F 'ADR-036' "$SKILL_MD"
|
|
16
|
+
[ "$status" -eq 0 ]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@test "work-problems: SKILL.md cross-references the scaffold-intake skill" {
|
|
20
|
+
run grep -F 'wr-itil:scaffold-intake' "$SKILL_MD"
|
|
21
|
+
[ "$status" -eq 0 ]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@test "work-problems: SKILL.md documents the AFK fail-safe (no auto-scaffold; iteration-report pending-note)" {
|
|
25
|
+
# AFK orchestrator branch must NOT auto-scaffold; instead the iteration
|
|
26
|
+
# appends a one-line "pending intake scaffold" note to its summary.
|
|
27
|
+
# The SKILL.md preamble pointer documents this so AFK consumers see
|
|
28
|
+
# the divergence at glance.
|
|
29
|
+
run grep -iE 'pending.intake.scaffold|pending intake scaffold|silent note' "$SKILL_MD"
|
|
30
|
+
[ "$status" -eq 0 ]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@test "work-problems: SKILL.md cites the decline marker path (ADR-009 + ADR-036)" {
|
|
34
|
+
run grep -F '.claude/.intake-scaffold-declined' "$SKILL_MD"
|
|
35
|
+
[ "$status" -eq 0 ]
|
|
36
|
+
}
|