@nklisch/pi-agile-workflow 0.15.3
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 +10 -0
- package/.codex-plugin/plugin.json +28 -0
- package/CHANGELOG.md +591 -0
- package/README.md +107 -0
- package/docs/ARCHITECTURE.md +716 -0
- package/docs/MIGRATION.md +419 -0
- package/docs/PRINCIPLES.md +464 -0
- package/docs/ROADMAP.md +95 -0
- package/docs/SPEC.md +855 -0
- package/docs/VISION.md +106 -0
- package/extensions/agile-workflow.test.ts +419 -0
- package/extensions/agile-workflow.ts +467 -0
- package/hooks/hooks.json +52 -0
- package/hooks/scripts/prompt-context.py +628 -0
- package/hooks/scripts/substrate-maintainer.py +359 -0
- package/hooks/scripts/test_prompt_context.py +757 -0
- package/package.json +30 -0
- package/scripts/install-work-view.sh +165 -0
- package/scripts/tests/agent-metadata.test.sh +75 -0
- package/scripts/tests/bump-version.test.sh +552 -0
- package/scripts/tests/channel-parity.test.sh +117 -0
- package/scripts/tests/convert-content-integrity.test.sh +246 -0
- package/scripts/tests/convert-install-routing.test.sh +159 -0
- package/scripts/tests/install-work-view.test.sh +411 -0
- package/scripts/tests/pi-package-metadata.test.sh +119 -0
- package/scripts/tests/work-board-shim.test.sh +239 -0
- package/scripts/tests/work-view-dist-version.test.sh +105 -0
- package/scripts/work-board.sh +64 -0
- package/scripts/work-view.sh +423 -0
- package/skills/autopilot/SKILL.md +330 -0
- package/skills/autopilot/agents/openai.yaml +6 -0
- package/skills/board/SKILL.md +62 -0
- package/skills/board/agents/openai.yaml +6 -0
- package/skills/bold-refactor/SKILL.md +244 -0
- package/skills/bold-refactor/agents/openai.yaml +6 -0
- package/skills/bug-scan/SKILL.md +378 -0
- package/skills/bug-scan/agents/openai.yaml +6 -0
- package/skills/bug-scan/references/async-promises.md +119 -0
- package/skills/bug-scan/references/concurrency-races.md +148 -0
- package/skills/bug-scan/references/data-layer.md +167 -0
- package/skills/bug-scan/references/error-handling.md +197 -0
- package/skills/bug-scan/references/gate-item-template.md +94 -0
- package/skills/bug-scan/references/language-footguns.md +219 -0
- package/skills/bug-scan/references/parked-item-template.md +103 -0
- package/skills/bug-scan/references/report-template.md +115 -0
- package/skills/bug-scan/references/resource-leaks.md +199 -0
- package/skills/bug-scan/references/state-closures.md +148 -0
- package/skills/bug-scan/references/time-numbers.md +158 -0
- package/skills/convert/SKILL.md +1463 -0
- package/skills/convert/agents/openai.yaml +6 -0
- package/skills/convert/references/legacy-overlap-migration.md +179 -0
- package/skills/deep-code-scan/SKILL.md +378 -0
- package/skills/deep-code-scan/agents/openai.yaml +6 -0
- package/skills/deep-code-scan/references/artifact-ledger.md +148 -0
- package/skills/deep-code-scan/references/consolidation.md +91 -0
- package/skills/deep-code-scan/references/decomposition.md +127 -0
- package/skills/deep-code-scan/references/item-templates.md +263 -0
- package/skills/deep-code-scan/references/lane-catalog.md +129 -0
- package/skills/deep-code-scan/references/review-gauntlet.md +108 -0
- package/skills/deep-code-scan/references/scanner-brief.md +152 -0
- package/skills/e2e-test-design/SKILL.md +517 -0
- package/skills/e2e-test-design/references/anti-tautology.md +261 -0
- package/skills/e2e-test-design/references/service-mocks.md +234 -0
- package/skills/e2e-test-design/references/test-taxonomy.md +211 -0
- package/skills/epic-design/SKILL.md +476 -0
- package/skills/epicize/SKILL.md +210 -0
- package/skills/epicize/agents/openai.yaml +6 -0
- package/skills/feature-design/SKILL.md +472 -0
- package/skills/fix/SKILL.md +173 -0
- package/skills/gate-cruft/SKILL.md +258 -0
- package/skills/gate-docs/SKILL.md +278 -0
- package/skills/gate-patterns/SKILL.md +389 -0
- package/skills/gate-refactor/SKILL.md +408 -0
- package/skills/gate-security/SKILL.md +275 -0
- package/skills/gate-tests/SKILL.md +367 -0
- package/skills/groom/SKILL.md +162 -0
- package/skills/groom/agents/openai.yaml +6 -0
- package/skills/ideate/SKILL.md +185 -0
- package/skills/ideate/agents/openai.yaml +6 -0
- package/skills/implement/SKILL.md +288 -0
- package/skills/implement-orchestrator/SKILL.md +604 -0
- package/skills/park/SKILL.md +111 -0
- package/skills/perf-design/SKILL.md +427 -0
- package/skills/perf-scout/SKILL.md +404 -0
- package/skills/perf-scout/agents/openai.yaml +6 -0
- package/skills/perf-scout/references/algorithmic-and-data-structures.md +126 -0
- package/skills/perf-scout/references/approximation-and-precomputation.md +136 -0
- package/skills/perf-scout/references/caching-and-memoization.md +123 -0
- package/skills/perf-scout/references/compiler-runtime-and-language.md +136 -0
- package/skills/perf-scout/references/database-and-storage-internals.md +144 -0
- package/skills/perf-scout/references/distributed-systems.md +150 -0
- package/skills/perf-scout/references/game-engine-and-realtime.md +136 -0
- package/skills/perf-scout/references/gpu-and-accelerators.md +130 -0
- package/skills/perf-scout/references/idea-ranking.md +75 -0
- package/skills/perf-scout/references/io-and-batching.md +135 -0
- package/skills/perf-scout/references/memory-and-data-locality.md +137 -0
- package/skills/perf-scout/references/parallelism-and-vectorization.md +126 -0
- package/skills/perf-scout/references/parked-item-template.md +117 -0
- package/skills/perf-scout/references/peer-review-pass.md +150 -0
- package/skills/perf-scout/references/report-template.md +148 -0
- package/skills/principles/SKILL.md +678 -0
- package/skills/principles/references/models.md +191 -0
- package/skills/principles/references/subagents.md +158 -0
- package/skills/prose-author/SKILL.md +185 -0
- package/skills/refactor-conventions-creator/SKILL.md +359 -0
- package/skills/refactor-conventions-creator/agents/openai.yaml +6 -0
- package/skills/refactor-conventions-creator/references/common-structures.md +91 -0
- package/skills/refactor-conventions-creator/references/common-styles.md +90 -0
- package/skills/refactor-design/SKILL.md +384 -0
- package/skills/release-deploy/SKILL.md +667 -0
- package/skills/release-deploy/agents/openai.yaml +6 -0
- package/skills/research/SKILL.md +166 -0
- package/skills/research/agents/openai.yaml +6 -0
- package/skills/review/SKILL.md +267 -0
- package/skills/review/references/deep-review.md +98 -0
- package/skills/review/references/review-lenses.md +68 -0
- package/skills/review/references/substrate-side-effects.md +161 -0
- package/skills/review/references/target-resolution.md +52 -0
- package/skills/scope/SKILL.md +486 -0
- package/work-view/Cargo.lock +486 -0
- package/work-view/Cargo.toml +9 -0
- package/work-view/crates/cli/.work-view-version +1 -0
- package/work-view/crates/cli/Cargo.toml +18 -0
- package/work-view/crates/cli/src/actionable.rs +500 -0
- package/work-view/crates/cli/src/args.rs +792 -0
- package/work-view/crates/cli/src/board/assets/board.css +855 -0
- package/work-view/crates/cli/src/board/assets/board.js +259 -0
- package/work-view/crates/cli/src/board/assets/card.js +139 -0
- package/work-view/crates/cli/src/board/assets/components.css +290 -0
- package/work-view/crates/cli/src/board/assets/dependency.js +1193 -0
- package/work-view/crates/cli/src/board/assets/detail.js +256 -0
- package/work-view/crates/cli/src/board/assets/filters.js +389 -0
- package/work-view/crates/cli/src/board/assets/index.html +77 -0
- package/work-view/crates/cli/src/board/assets/kanban.js +231 -0
- package/work-view/crates/cli/src/board/assets/markdown.js +260 -0
- package/work-view/crates/cli/src/board/assets/motion.css +131 -0
- package/work-view/crates/cli/src/board/assets/state.js +245 -0
- package/work-view/crates/cli/src/board/assets/table.js +304 -0
- package/work-view/crates/cli/src/board/assets/tokens.css +602 -0
- package/work-view/crates/cli/src/board/assets/views.js +41 -0
- package/work-view/crates/cli/src/board/assets.rs +162 -0
- package/work-view/crates/cli/src/board/feed.rs +204 -0
- package/work-view/crates/cli/src/board/mod.rs +246 -0
- package/work-view/crates/cli/src/board/open.rs +144 -0
- package/work-view/crates/cli/src/board/server.rs +407 -0
- package/work-view/crates/cli/src/main.rs +195 -0
- package/work-view/crates/cli/src/render.rs +354 -0
- package/work-view/crates/cli/src/scope.rs +157 -0
- package/work-view/crates/cli/src/stale.rs +581 -0
- package/work-view/crates/cli/tests/board-js/dependency-table.test.mjs +64 -0
- package/work-view/crates/cli/tests/board-js/expanded-browsing.test.mjs +79 -0
- package/work-view/crates/cli/tests/board-js/harness-smoke.test.mjs +11 -0
- package/work-view/crates/cli/tests/board-js/harness.mjs +437 -0
- package/work-view/crates/cli/tests/board-js/kanban-detail.test.mjs +75 -0
- package/work-view/crates/cli/tests/board-js/markdown-filter.test.mjs +60 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/CONVENTIONS.md +3 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/epics/epic-alpha.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-a.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-b.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-alpha-1.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-research-1.md +19 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-done.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-shipped.md +18 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/backlog/idea-backlog.md +7 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/CONVENTIONS.md +6 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-dep-done.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-design-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-impl-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/stories/story-review-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/CONVENTIONS.md +4 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/epics/malformed-no-closing-fence.md +8 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/features/good-item.md +16 -0
- package/work-view/crates/cli/tests/integration.rs +2833 -0
- package/work-view/crates/core/Cargo.toml +13 -0
- package/work-view/crates/core/src/error.rs +61 -0
- package/work-view/crates/core/src/filter.rs +804 -0
- package/work-view/crates/core/src/graph.rs +324 -0
- package/work-view/crates/core/src/index.rs +590 -0
- package/work-view/crates/core/src/lib.rs +54 -0
- package/work-view/crates/core/src/model.rs +202 -0
- package/work-view/crates/core/src/parse.rs +494 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/CONVENTIONS.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/epics/epic-core.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-parser.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-query.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/malformed-no-id.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-parse-unit.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-security-gate.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-old.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-parser.md +17 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/backlog/idea-future-board.md +7 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/CONVENTIONS.md +10 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/archive/dup-item.md +18 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/releases/v2.0/dup-item.md +18 -0
- package/work-view/crates/core/tests/integration.rs +541 -0
- package/work-view/dist/.gitattributes +1 -0
- package/work-view/dist/README.md +42 -0
- package/work-view/dist/aarch64-apple-darwin/work-view +0 -0
- package/work-view/dist/aarch64-unknown-linux-musl/work-view +0 -0
- package/work-view/dist/x86_64-apple-darwin/work-view +0 -0
- package/work-view/dist/x86_64-unknown-linux-musl/work-view +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# convert-content-integrity.test.sh — structural regression guards for two
|
|
3
|
+
# markdown-skill invariants that are cheaply machine-checkable:
|
|
4
|
+
#
|
|
5
|
+
# (a) convert/SKILL.md DEFINES the content-integrity gate once, the gate
|
|
6
|
+
# enumerates the destructive ops it governs, and the document's destructive
|
|
7
|
+
# verb sites (git rm / git mv / shim / symlink / managed-section overwrite /
|
|
8
|
+
# mirror replacement) each sit near a content-integrity reference. A future
|
|
9
|
+
# edit could silently strip the gate or perform a destructive op without
|
|
10
|
+
# routing through it.
|
|
11
|
+
#
|
|
12
|
+
# (b) Each enumerated design/implement/review-family SKILL.md grounds on
|
|
13
|
+
# `.agents/rules/*.md` (the project's force-loaded agent rules) in its
|
|
14
|
+
# grounding phase. A future edit could silently drop one.
|
|
15
|
+
#
|
|
16
|
+
# These are structural tests on SKILL.md prose, mirroring
|
|
17
|
+
# convert-install-routing.test.sh. Runnable locally and from CI. Exits non-zero
|
|
18
|
+
# if any assertion fails.
|
|
19
|
+
|
|
20
|
+
set -uo pipefail
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
SKILLS_DIR="${SCRIPT_DIR}/../../skills"
|
|
24
|
+
CONVERT_MD="${SKILLS_DIR}/convert/SKILL.md"
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# Counters
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
PASS=0
|
|
30
|
+
FAIL=0
|
|
31
|
+
ERRORS=()
|
|
32
|
+
|
|
33
|
+
assert_eq() {
|
|
34
|
+
local label="$1" expected="$2" actual="$3"
|
|
35
|
+
if [ "$expected" = "$actual" ]; then
|
|
36
|
+
echo " PASS: $label"
|
|
37
|
+
((PASS++))
|
|
38
|
+
else
|
|
39
|
+
echo " FAIL: $label"
|
|
40
|
+
echo " expected: $(printf '%q' "$expected")"
|
|
41
|
+
echo " actual: $(printf '%q' "$actual")"
|
|
42
|
+
((FAIL++))
|
|
43
|
+
ERRORS+=("$label")
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
assert_true() {
|
|
48
|
+
local label="$1"
|
|
49
|
+
if eval "$2"; then
|
|
50
|
+
echo " PASS: $label"
|
|
51
|
+
((PASS++))
|
|
52
|
+
else
|
|
53
|
+
echo " FAIL: $label"
|
|
54
|
+
echo " condition false: $2"
|
|
55
|
+
((FAIL++))
|
|
56
|
+
ERRORS+=("$label")
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Preflight: convert/SKILL.md present
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
echo ""
|
|
64
|
+
echo "=== Preflight: convert/SKILL.md present ==="
|
|
65
|
+
assert_true "convert/SKILL.md exists" "[ -f '$CONVERT_MD' ]"
|
|
66
|
+
if [ ! -f "$CONVERT_MD" ]; then
|
|
67
|
+
echo "ERROR: cannot locate convert/SKILL.md at $CONVERT_MD" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
# Test group 1: content-integrity gate is defined exactly once
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
echo ""
|
|
75
|
+
echo "=== Test group 1: content-integrity gate defined ==="
|
|
76
|
+
|
|
77
|
+
# The canonical gate-definition line (a blockquote bold lead-in). Use a
|
|
78
|
+
# fixed-string match so the literal parentheses are not interpreted as regex.
|
|
79
|
+
GATE_DEF_LITERAL='Content-integrity gate (before any destructive op)'
|
|
80
|
+
GATE_DEF_COUNT="$(grep -cF "$GATE_DEF_LITERAL" "$CONVERT_MD")"
|
|
81
|
+
assert_eq "content-integrity gate defined exactly once" "1" "$GATE_DEF_COUNT"
|
|
82
|
+
|
|
83
|
+
# The gate must describe itself as a hard precondition, not advice.
|
|
84
|
+
assert_true "gate is stated as a hard precondition (not advice)" \
|
|
85
|
+
"grep -q 'hard precondition, not advice' '$CONVERT_MD'"
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
# Test group 2: the gate enumerates each destructive op kind it governs
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
echo ""
|
|
91
|
+
echo "=== Test group 2: gate enumerates the destructive op kinds ==="
|
|
92
|
+
|
|
93
|
+
# Extract the gate's definition block: from the gate-definition line up to the
|
|
94
|
+
# blank-quote separator line (`>` alone) that closes the enumeration.
|
|
95
|
+
GATE_BLOCK="$(awk -v needle="$GATE_DEF_LITERAL" '
|
|
96
|
+
index($0, needle) { collecting = 1 }
|
|
97
|
+
collecting && $0 == ">" { print; exit }
|
|
98
|
+
collecting { print }
|
|
99
|
+
' "$CONVERT_MD")"
|
|
100
|
+
|
|
101
|
+
assert_true "gate block is non-empty" "[ -n \"\$GATE_BLOCK\" ]"
|
|
102
|
+
assert_true "gate enumerates git rm / delete" \
|
|
103
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -qE 'git rm.*delete|delete'"
|
|
104
|
+
assert_true "gate enumerates git mv / move" \
|
|
105
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -qE 'git mv.*move|move'"
|
|
106
|
+
assert_true "gate enumerates replace-with-symlink" \
|
|
107
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -q 'replace-with-symlink'"
|
|
108
|
+
assert_true "gate enumerates replace-with-shim" \
|
|
109
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -q 'replace-with-shim'"
|
|
110
|
+
assert_true "gate enumerates managed-section overwrite" \
|
|
111
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -q 'managed-section'"
|
|
112
|
+
assert_true "gate enumerates mirror replacement" \
|
|
113
|
+
"printf '%s' \"\$GATE_BLOCK\" | grep -q 'mirror replacement'"
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# Test group 3: the Hard-rules summary restates the precondition
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
echo ""
|
|
119
|
+
echo "=== Test group 3: Hard-rules summary restates content integrity first ==="
|
|
120
|
+
|
|
121
|
+
assert_true "summary: content integrity mandatory before every destructive op" \
|
|
122
|
+
"grep -q 'Content integrity is mandatory before every destructive op' '$CONVERT_MD'"
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# Test group 4: destructive verb sites each sit near a content-integrity ref
|
|
126
|
+
#
|
|
127
|
+
# Invariant: every line that performs/names a destructive op must be authored in
|
|
128
|
+
# proximity to the gate. A site is satisfied if EITHER
|
|
129
|
+
# * a 'content-integrity' reference appears within +/- WINDOW lines, OR
|
|
130
|
+
# * the site lies inside the gate's own defining section (Phase 1.8 through its
|
|
131
|
+
# manifest sub-sections) — those lines describe the ops the gate governs and
|
|
132
|
+
# ARE the gate, so they need no separate nearby reference.
|
|
133
|
+
# This catches a future destructive instruction added in isolation from the gate
|
|
134
|
+
# (e.g. a new shim/removal step in a migration path with no gate routing).
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
echo ""
|
|
137
|
+
echo "=== Test group 4: destructive verb sites reference content-integrity nearby ==="
|
|
138
|
+
|
|
139
|
+
WINDOW=8
|
|
140
|
+
TOTAL_LINES="$(wc -l < "$CONVERT_MD")"
|
|
141
|
+
|
|
142
|
+
# Bounds of the content-integrity gate's defining section: from the "### Phase
|
|
143
|
+
# 1.8" heading to the line before the next top-level "### Phase 2" heading.
|
|
144
|
+
GATE_SECTION_START="$(grep -nE '^### Phase 1\.8' "$CONVERT_MD" | head -n1 | cut -d: -f1)"
|
|
145
|
+
GATE_SECTION_END="$(grep -nE '^### Phase 2' "$CONVERT_MD" | head -n1 | cut -d: -f1)"
|
|
146
|
+
assert_true "Phase 1.8 gate section start located" "[ -n '$GATE_SECTION_START' ]"
|
|
147
|
+
assert_true "Phase 2 boundary (gate section end) located" "[ -n '$GATE_SECTION_END' ]"
|
|
148
|
+
|
|
149
|
+
# Lines naming a destructive op via this skill's operative phrasings.
|
|
150
|
+
mapfile -t DESTRUCTIVE_LINES < <(
|
|
151
|
+
grep -nE 'shim/removal|managed-section overwrite|mirror replacement|source-eliminating op|replace the file with the .* shim|replace-with-symlink / replace-with-shim' "$CONVERT_MD" \
|
|
152
|
+
| cut -d: -f1
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
assert_true "found destructive-op sites to check" "[ \"\${#DESTRUCTIVE_LINES[@]}\" -gt 0 ]"
|
|
156
|
+
|
|
157
|
+
in_gate_section() {
|
|
158
|
+
local line="$1"
|
|
159
|
+
[ "$line" -ge "$GATE_SECTION_START" ] && [ "$line" -lt "$GATE_SECTION_END" ]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
near_content_integrity() {
|
|
163
|
+
local line="$1"
|
|
164
|
+
local lo=$(( line - WINDOW )); [ "$lo" -lt 1 ] && lo=1
|
|
165
|
+
local hi=$(( line + WINDOW )); [ "$hi" -gt "$TOTAL_LINES" ] && hi="$TOTAL_LINES"
|
|
166
|
+
sed -n "${lo},${hi}p" "$CONVERT_MD" | grep -qiE 'content[- ]integrity'
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ALL_GUARDED=1
|
|
170
|
+
for ln in "${DESTRUCTIVE_LINES[@]}"; do
|
|
171
|
+
if in_gate_section "$ln" || near_content_integrity "$ln"; then
|
|
172
|
+
continue
|
|
173
|
+
fi
|
|
174
|
+
ALL_GUARDED=0
|
|
175
|
+
echo " destructive-op site at line $ln is neither inside the gate section nor within +/- ${WINDOW} lines of a content-integrity reference" >&2
|
|
176
|
+
done
|
|
177
|
+
assert_true "every destructive-op site is gate-guarded (in gate section or near a content-integrity ref)" \
|
|
178
|
+
"[ '$ALL_GUARDED' -eq 1 ]"
|
|
179
|
+
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
# Test group 5: family skills ground on .agents/rules/*.md
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
echo ""
|
|
184
|
+
echo "=== Test group 5: design/implement/review-family skills ground on .agents/rules ==="
|
|
185
|
+
|
|
186
|
+
# The enumerated family skills whose grounding phase must read the project's
|
|
187
|
+
# force-loaded agent rules.
|
|
188
|
+
FAMILY_SKILLS=(
|
|
189
|
+
feature-design
|
|
190
|
+
epic-design
|
|
191
|
+
refactor-design
|
|
192
|
+
perf-design
|
|
193
|
+
e2e-test-design
|
|
194
|
+
implement
|
|
195
|
+
implement-orchestrator
|
|
196
|
+
review
|
|
197
|
+
fix
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# grounds_on_rules <skill-md> — true iff the file references the
|
|
201
|
+
# `.agents/rules/*.md` path AND a "force-loaded" grounding descriptor appears
|
|
202
|
+
# within +/- GROUND_WINDOW lines of that path. Window-based so it tolerates the
|
|
203
|
+
# line-wrapping some skills use ("...the project's force-loaded" / "agent rules"
|
|
204
|
+
# on the next line) without weakening to a bare path match.
|
|
205
|
+
GROUND_WINDOW=2
|
|
206
|
+
grounds_on_rules() {
|
|
207
|
+
local md="$1"
|
|
208
|
+
local total; total="$(wc -l < "$md")"
|
|
209
|
+
local ln lo hi
|
|
210
|
+
while IFS=: read -r ln _; do
|
|
211
|
+
[ -z "$ln" ] && continue
|
|
212
|
+
lo=$(( ln - GROUND_WINDOW )); [ "$lo" -lt 1 ] && lo=1
|
|
213
|
+
hi=$(( ln + GROUND_WINDOW )); [ "$hi" -gt "$total" ] && hi="$total"
|
|
214
|
+
if sed -n "${lo},${hi}p" "$md" | grep -qi 'force-loaded'; then
|
|
215
|
+
return 0
|
|
216
|
+
fi
|
|
217
|
+
done < <(grep -n '\.agents/rules/\*\.md' "$md")
|
|
218
|
+
return 1
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for skill in "${FAMILY_SKILLS[@]}"; do
|
|
222
|
+
md="${SKILLS_DIR}/${skill}/SKILL.md"
|
|
223
|
+
assert_true "${skill}/SKILL.md exists" "[ -f '$md' ]"
|
|
224
|
+
# Grounding context: the .agents/rules/*.md path paired with the recurring
|
|
225
|
+
# "force-loaded [agent rules]" grounding descriptor near that path.
|
|
226
|
+
assert_true "${skill} references .agents/rules/*.md path" \
|
|
227
|
+
"grep -q '\.agents/rules/\*\.md' '$md'"
|
|
228
|
+
assert_true "${skill} grounds .agents/rules as force-loaded agent rules" \
|
|
229
|
+
"grounds_on_rules '$md'"
|
|
230
|
+
done
|
|
231
|
+
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
# Summary
|
|
234
|
+
# ---------------------------------------------------------------------------
|
|
235
|
+
echo ""
|
|
236
|
+
echo "============================================================"
|
|
237
|
+
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
238
|
+
if [ ${#ERRORS[@]} -gt 0 ]; then
|
|
239
|
+
echo "Failed tests:"
|
|
240
|
+
for e in "${ERRORS[@]}"; do
|
|
241
|
+
echo " - $e"
|
|
242
|
+
done
|
|
243
|
+
fi
|
|
244
|
+
echo "============================================================"
|
|
245
|
+
|
|
246
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# convert-install-routing.test.sh — structural regression guard for convert/SKILL.md
|
|
3
|
+
#
|
|
4
|
+
# The convert skill's install path MUST route through install-work-view.sh in
|
|
5
|
+
# BOTH the bootstrap branch (Phase 4: Create substrate skeleton) and the sync
|
|
6
|
+
# branch (Phase S3: Apply refreshes). A future edit that reverts either block to
|
|
7
|
+
# a raw `cp .../work-view.sh .work/bin/work-view` would silently bypass the
|
|
8
|
+
# platform-aware prebuilt selector the installer provides, and pass the direct
|
|
9
|
+
# installer tests while breaking the convert seam.
|
|
10
|
+
#
|
|
11
|
+
# This is a structural test on the SKILL.md prose (Recommendation A from the
|
|
12
|
+
# cross-model consult): for a markdown skill, guard the instructions as written.
|
|
13
|
+
# It extracts the bounded Phase 4 and Phase S3 blocks and asserts:
|
|
14
|
+
# 1. each block references install-work-view.sh
|
|
15
|
+
# 2. neither block contains a raw `cp ... work-view.sh ... .work/bin/work-view`
|
|
16
|
+
# 3. the work_view doctor marker references --version and drift_plugin
|
|
17
|
+
#
|
|
18
|
+
# Runnable locally and from CI. Exits non-zero if any assertion fails.
|
|
19
|
+
|
|
20
|
+
set -uo pipefail
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
SKILL_MD="${SCRIPT_DIR}/../../skills/convert/SKILL.md"
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Counters
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
PASS=0
|
|
29
|
+
FAIL=0
|
|
30
|
+
ERRORS=()
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# assert helpers
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
assert_true() {
|
|
36
|
+
local label="$1"
|
|
37
|
+
if eval "$2"; then
|
|
38
|
+
echo " PASS: $label"
|
|
39
|
+
((PASS++))
|
|
40
|
+
else
|
|
41
|
+
echo " FAIL: $label"
|
|
42
|
+
echo " condition false: $2"
|
|
43
|
+
((FAIL++))
|
|
44
|
+
ERRORS+=("$label")
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
assert_false() {
|
|
49
|
+
local label="$1"
|
|
50
|
+
if ! eval "$2"; then
|
|
51
|
+
echo " PASS: $label"
|
|
52
|
+
((PASS++))
|
|
53
|
+
else
|
|
54
|
+
echo " FAIL: $label"
|
|
55
|
+
echo " expected false but was true: $2"
|
|
56
|
+
((FAIL++))
|
|
57
|
+
ERRORS+=("$label")
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# extract_block <start-heading> <end-heading>
|
|
63
|
+
# Prints the lines of SKILL.md from the start heading (inclusive) up to but not
|
|
64
|
+
# including the end heading. Anchors on exact "### <Phase ...>" headings.
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
extract_block() {
|
|
67
|
+
local start="$1" end="$2"
|
|
68
|
+
awk -v start="$start" -v end="$end" '
|
|
69
|
+
$0 == start { collecting = 1 }
|
|
70
|
+
collecting && $0 == end { collecting = 0 }
|
|
71
|
+
collecting { print }
|
|
72
|
+
' "$SKILL_MD"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# Preflight: SKILL.md exists
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
echo ""
|
|
79
|
+
echo "=== Preflight: convert/SKILL.md present ==="
|
|
80
|
+
assert_true "convert/SKILL.md exists" "[ -f '$SKILL_MD' ]"
|
|
81
|
+
if [ ! -f "$SKILL_MD" ]; then
|
|
82
|
+
echo "ERROR: cannot locate convert/SKILL.md at $SKILL_MD" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Extract the two bounded blocks
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
PHASE4_BLOCK="$(extract_block "### Phase 4: Create substrate skeleton" "### Phase 5: Write CONVENTIONS.md")"
|
|
90
|
+
PHASES3_BLOCK="$(extract_block "### Phase S3: Apply refreshes" "### Phase S4: Preserve user state")"
|
|
91
|
+
DOCTOR_BLOCK="$(awk '
|
|
92
|
+
/\.work\/bin\/work-view` —/ { collecting = 1 }
|
|
93
|
+
collecting && /^ - `CLAUDE\.md` compatibility state/ { collecting = 0 }
|
|
94
|
+
collecting { print }
|
|
95
|
+
' "$SKILL_MD")"
|
|
96
|
+
|
|
97
|
+
# A raw fallback copy regression looks like:
|
|
98
|
+
# cp "${PLUGIN_ROOT}/scripts/work-view.sh" .work/bin/work-view
|
|
99
|
+
# i.e. a single line containing all three of: cp, work-view.sh, .work/bin/work-view
|
|
100
|
+
RAW_CP_RE='cp.*work-view\.sh.*\.work/bin/work-view'
|
|
101
|
+
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
# Test group 1: blocks were located and are non-empty
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
echo ""
|
|
106
|
+
echo "=== Test group 1: phase blocks located ==="
|
|
107
|
+
assert_true "Phase 4 block is non-empty" "[ -n \"\$PHASE4_BLOCK\" ]"
|
|
108
|
+
assert_true "Phase 4 block starts at its heading" \
|
|
109
|
+
"printf '%s' \"\$PHASE4_BLOCK\" | head -n1 | grep -q '^### Phase 4: Create substrate skeleton$'"
|
|
110
|
+
assert_true "Phase S3 block is non-empty" "[ -n \"\$PHASES3_BLOCK\" ]"
|
|
111
|
+
assert_true "Phase S3 block starts at its heading" \
|
|
112
|
+
"printf '%s' \"\$PHASES3_BLOCK\" | head -n1 | grep -q '^### Phase S3: Apply refreshes$'"
|
|
113
|
+
assert_true "work_view doctor marker block is non-empty" "[ -n \"\$DOCTOR_BLOCK\" ]"
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# Test group 2: both blocks route through install-work-view.sh
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
echo ""
|
|
119
|
+
echo "=== Test group 2: install routing present ==="
|
|
120
|
+
assert_true "Phase 4 references install-work-view.sh" \
|
|
121
|
+
"printf '%s' \"\$PHASE4_BLOCK\" | grep -q 'install-work-view\.sh'"
|
|
122
|
+
assert_true "Phase S3 references install-work-view.sh" \
|
|
123
|
+
"printf '%s' \"\$PHASES3_BLOCK\" | grep -q 'install-work-view\.sh'"
|
|
124
|
+
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
# Test group 3: neither block contains a raw fallback cp regression
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
echo ""
|
|
129
|
+
echo "=== Test group 3: no raw 'cp ... work-view.sh ... .work/bin/work-view' regression ==="
|
|
130
|
+
assert_false "Phase 4 has no raw cp fallback" \
|
|
131
|
+
"printf '%s' \"\$PHASE4_BLOCK\" | grep -Eq '$RAW_CP_RE'"
|
|
132
|
+
assert_false "Phase S3 has no raw cp fallback" \
|
|
133
|
+
"printf '%s' \"\$PHASES3_BLOCK\" | grep -Eq '$RAW_CP_RE'"
|
|
134
|
+
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
# Test group 4: doctor marker stays version-aware
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
echo ""
|
|
139
|
+
echo "=== Test group 4: work_view doctor marker is version-aware ==="
|
|
140
|
+
assert_true "work_view doctor marker references --version" \
|
|
141
|
+
"printf '%s' \"\$DOCTOR_BLOCK\" | grep -q -- '--version'"
|
|
142
|
+
assert_true "work_view doctor marker classifies drift_plugin" \
|
|
143
|
+
"printf '%s' \"\$DOCTOR_BLOCK\" | grep -q 'drift_plugin'"
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Summary
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
echo ""
|
|
149
|
+
echo "============================================================"
|
|
150
|
+
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
151
|
+
if [ ${#ERRORS[@]} -gt 0 ]; then
|
|
152
|
+
echo "Failed tests:"
|
|
153
|
+
for e in "${ERRORS[@]}"; do
|
|
154
|
+
echo " - $e"
|
|
155
|
+
done
|
|
156
|
+
fi
|
|
157
|
+
echo "============================================================"
|
|
158
|
+
|
|
159
|
+
[ "$FAIL" -eq 0 ] || exit 1
|