@windyroad/itil 0.61.1 → 0.61.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +7 -2
- package/agents/test/fixtures/proceed-new-genuinely-new.md +0 -43
- package/agents/test/fixtures/proceed-new-subtle-sibling.md +0 -47
- package/agents/test/fixtures/regression-p347-vs-p346.md +0 -54
- package/agents/test/hang-off-check.bats +0 -225
- package/hooks/test/block-list.bats +0 -153
- package/hooks/test/command-detect.bats +0 -181
- package/hooks/test/itil-assistant-output-gate.bats +0 -114
- package/hooks/test/itil-assistant-output-review.bats +0 -224
- package/hooks/test/itil-bash-polling-antipattern-detect.bats +0 -154
- package/hooks/test/itil-changeset-discipline.bats +0 -603
- package/hooks/test/itil-claude-space-protection.bats +0 -294
- package/hooks/test/itil-commit-trailer-transition-advisory.bats +0 -80
- package/hooks/test/itil-correction-detect.bats +0 -189
- package/hooks/test/itil-deferral-cadence-gate.bats +0 -187
- package/hooks/test/itil-fictional-defer-detect.bats +0 -292
- package/hooks/test/itil-fix-title-lifecycle-advisory.bats +0 -129
- package/hooks/test/itil-mid-loop-ask-detect.bats +0 -220
- package/hooks/test/itil-no-implement-draft-gate.bats +0 -210
- package/hooks/test/itil-pending-questions-surface.bats +0 -307
- package/hooks/test/itil-readme-refresh-discipline.bats +0 -642
- package/hooks/test/itil-rfc-oversight-nudge.bats +0 -83
- package/hooks/test/itil-rfc-trailer-advisory.bats +0 -375
- package/hooks/test/itil-story-mirror-migration-nudge.bats +0 -122
- package/hooks/test/manage-problem-enforce-create.bats +0 -424
- package/hooks/test/p057-staging-trap-detect.bats +0 -231
- package/hooks/test/pre-publish-intake-gate.bats +0 -144
- package/hooks/test/runtime-sid-marker.bats +0 -90
- package/hooks/test/session-id.bats +0 -370
- package/scripts/migrate-problems-add-type.sh +0 -116
- package/scripts/test/catchup-scan.bats +0 -274
- package/scripts/test/check-afk-accept-eligible.bats +0 -310
- package/scripts/test/check-fail-soft-skip-discipline.bats +0 -172
- package/scripts/test/check-fix-rfc-trace.bats +0 -164
- package/scripts/test/check-locale-discipline.bats +0 -291
- package/scripts/test/check-problems-readme-budget.bats +0 -192
- package/scripts/test/check-rfc-has-stories.bats +0 -69
- package/scripts/test/check-rfc-rejected-alternatives.bats +0 -108
- package/scripts/test/check-rfc-stories-ratified.bats +0 -82
- package/scripts/test/check-upstream-responses.bats +0 -503
- package/scripts/test/classify-readme-drift.bats +0 -392
- package/scripts/test/derive-first-dispatch.bats +0 -274
- package/scripts/test/derive-release-vehicle.bats +0 -454
- package/scripts/test/detect-unratified-stories-maps.bats +0 -60
- package/scripts/test/dual-tolerant-glob-rfc-002-t2.bats +0 -309
- package/scripts/test/effort-tally.bats +0 -170
- package/scripts/test/evaluate-relevance.bats +0 -773
- package/scripts/test/kind-a-commands.bats +0 -46
- package/scripts/test/mark-create-gate.bats +0 -59
- package/scripts/test/mark-story-oversight-confirmed.bats +0 -53
- package/scripts/test/migrate-problems-add-type.bats +0 -280
- package/scripts/test/migrate-story-status-mirror.bats +0 -167
- package/scripts/test/no-type-regression-guard.bats +0 -74
- package/scripts/test/oversight-mirror-corpus-lint.bats +0 -150
- package/scripts/test/plugin-exercise-index.bats +0 -386
- package/scripts/test/plugin-maturity-doc-lint.bats +0 -470
- package/scripts/test/plugin-maturity-populate.bats +0 -631
- package/scripts/test/plugin-maturity-render.bats +0 -469
- package/scripts/test/plugin-validate-ci-gate.bats +0 -208
- package/scripts/test/reconcile-readme.bats +0 -1007
- package/scripts/test/reconcile-rfcs.bats +0 -496
- package/scripts/test/reconcile-stories.bats +0 -173
- package/scripts/test/reconcile-story-maps.bats +0 -74
- package/scripts/test/release-watch-poll-loop.bats +0 -180
- package/scripts/test/resolve-governance-plugin-dirs.bats +0 -78
- package/scripts/test/rfc-stories-extension.bats +0 -175
- package/scripts/test/skill-invocations.bats +0 -535
- package/scripts/test/skill-md-dual-tolerant-coverage-rfc-002-t3.bats +0 -374
- package/scripts/test/story-oversight-lib.bats +0 -190
- package/scripts/test/update-jtbd-references-related-problems.bats +0 -178
- package/scripts/test/update-problem-references-section.bats +0 -242
- package/scripts/test/update-problem-rfcs-section.bats +0 -242
- package/scripts/test/update-references-section-sibling-helpers.bats +0 -80
- package/scripts/test/update-rfc-commits-section.bats +0 -77
- package/scripts/test/verify-iter-summary.bats +0 -196
- package/scripts/test/working-the-problem-traversal.bats +0 -116
- package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +0 -398
- package/skills/capture-problem/test/capture-problem.bats +0 -389
- package/skills/capture-story/test/capture-story-behavioural.bats +0 -317
- package/skills/capture-story-map/test/capture-story-map-behavioural.bats +0 -98
- package/skills/close-incident/test/close-incident-contract.bats +0 -146
- package/skills/link-incident/test/link-incident-contract.bats +0 -125
- package/skills/list-incidents/test/list-incidents-contract.bats +0 -115
- package/skills/list-problems/test/list-problems-contract.bats +0 -100
- package/skills/list-stories/test/list-stories-contract.bats +0 -127
- package/skills/list-story-maps/test/list-story-maps-contract.bats +0 -46
- package/skills/manage-incident/test/manage-incident-adr-044-contract.bats +0 -206
- package/skills/manage-incident/test/manage-incident-close-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-link-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-list-forwarder.bats +0 -68
- package/skills/manage-incident/test/manage-incident-mitigate-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident-restore-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident.bats +0 -171
- package/skills/manage-problem/test/manage-problem-adr-044-step4-derive-first.bats +0 -151
- package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +0 -53
- package/skills/manage-problem/test/manage-problem-concern-boundary.bats +0 -64
- package/skills/manage-problem/test/manage-problem-effort-buckets.bats +0 -56
- package/skills/manage-problem/test/manage-problem-external-root-cause-detection.bats +0 -119
- package/skills/manage-problem/test/manage-problem-first-run-intake-prompt.bats +0 -43
- package/skills/manage-problem/test/manage-problem-git-mv-restage-reminder.bats +0 -48
- package/skills/manage-problem/test/manage-problem-id-collision-guard.bats +0 -39
- package/skills/manage-problem/test/manage-problem-list-forwarder.bats +0 -66
- package/skills/manage-problem/test/manage-problem-next-id-origin-lookup.bats +0 -50
- package/skills/manage-problem/test/manage-problem-no-prose-options.bats +0 -74
- package/skills/manage-problem/test/manage-problem-output-formatting.bats +0 -30
- package/skills/manage-problem/test/manage-problem-parked-and-cache.bats +0 -73
- package/skills/manage-problem/test/manage-problem-readme-refresh-on-transition.bats +0 -62
- package/skills/manage-problem/test/manage-problem-readme-tie-break-order.bats +0 -318
- package/skills/manage-problem/test/manage-problem-readme-vq-sort-order.bats +0 -206
- package/skills/manage-problem/test/manage-problem-release-vehicle-seed.bats +0 -83
- package/skills/manage-problem/test/manage-problem-review-forwarder.bats +0 -71
- package/skills/manage-problem/test/manage-problem-step-9d-recovery-path.bats +0 -97
- package/skills/manage-problem/test/manage-problem-transition-forwarder.bats +0 -96
- package/skills/manage-problem/test/manage-problem-transitive-dependencies.bats +0 -365
- package/skills/manage-problem/test/manage-problem-verification-detection.bats +0 -64
- package/skills/manage-problem/test/manage-problem-verification-pending.bats +0 -114
- package/skills/manage-problem/test/manage-problem-work-forwarder.bats +0 -88
- package/skills/manage-story/test/manage-story-contract.bats +0 -174
- package/skills/manage-story-map/test/manage-story-map-contract.bats +0 -63
- package/skills/mitigate-incident/test/mitigate-incident-contract.bats +0 -258
- package/skills/reconcile-readme/test/reconcile-readme-contract.bats +0 -201
- package/skills/report-upstream/test/report-upstream-contract.bats +0 -394
- package/skills/restore-incident/test/restore-incident-contract.bats +0 -170
- package/skills/review-problems/test/inbound-channels-cache-shape.bats +0 -127
- package/skills/review-problems/test/inbound-discovery-contract.bats +0 -383
- package/skills/review-problems/test/jtbd-301-verdict-shape-contract.bats +0 -225
- package/skills/review-problems/test/review-problems-contract.bats +0 -235
- package/skills/review-problems/test/review-problems-likely-verified-cell-shape.bats +0 -229
- package/skills/scaffold-intake/test/scaffold-intake-contract.bats +0 -126
- package/skills/scaffold-intake/test/scaffold-intake-fixture.bats +0 -164
- package/skills/scaffold-intake/test/scaffold-intake-secrets-absent.bats +0 -87
- package/skills/transition-problem/test/transition-problem-contract.bats +0 -285
- package/skills/transition-problems/test/transition-problems-contract.bats +0 -331
- package/skills/update-upstream/test/update-upstream-contract.bats +0 -375
- package/skills/work-problem/test/work-problem-contract.bats +0 -294
- package/skills/work-problems/test/work-problems-above-appetite-remediation.bats +0 -139
- package/skills/work-problems/test/work-problems-adr-013-rule-6-p352-amendment.bats +0 -166
- package/skills/work-problems/test/work-problems-auto-migrate-step.bats +0 -57
- package/skills/work-problems/test/work-problems-cost-logging.bats +0 -109
- package/skills/work-problems/test/work-problems-deviation-candidate-shape.bats +0 -121
- package/skills/work-problems/test/work-problems-first-run-intake-prompt.bats +0 -36
- package/skills/work-problems/test/work-problems-inter-iteration-verify.bats +0 -59
- package/skills/work-problems/test/work-problems-mid-loop-userpromptsubmit-handler.bats +0 -69
- package/skills/work-problems/test/work-problems-no-mid-loop-asking.bats +0 -192
- package/skills/work-problems/test/work-problems-p341-pre-all-done-gate.bats +0 -122
- package/skills/work-problems/test/work-problems-p342-r007-derive-then-pass-flags.bats +0 -170
- package/skills/work-problems/test/work-problems-p342-retro-auto-ticket-carveout.bats +0 -120
- package/skills/work-problems/test/work-problems-preflight-failure-handling.bats +0 -248
- package/skills/work-problems/test/work-problems-preflight-session-continuity.bats +0 -139
- package/skills/work-problems/test/work-problems-preflight.bats +0 -69
- package/skills/work-problems/test/work-problems-release-cadence.bats +0 -63
- package/skills/work-problems/test/work-problems-step-0-iter-error-staleness.bats +0 -93
- package/skills/work-problems/test/work-problems-step-0b-cache-staleness-behavioural.bats +0 -138
- package/skills/work-problems/test/work-problems-step-0c-deferred-placeholder-staleness-behavioural.bats +0 -238
- package/skills/work-problems/test/work-problems-step-0d-outbound-responses-staleness-behavioural.bats +0 -174
- package/skills/work-problems/test/work-problems-step-2-5-routing.bats +0 -80
- package/skills/work-problems/test/work-problems-step-2-5b-cross-halt-routing.bats +0 -149
- package/skills/work-problems/test/work-problems-step-3-5-jtbd-ratification-predicate.bats +0 -191
- package/skills/work-problems/test/work-problems-step-5-bats-polling-discipline.bats +0 -91
- package/skills/work-problems/test/work-problems-step-5-delegation.bats +0 -333
- package/skills/work-problems/test/work-problems-step-5-idle-timeout-sigterm.bats +0 -421
- package/skills/work-problems/test/work-problems-step-5-is-error-transient-halt.bats +0 -278
- package/skills/work-problems/test/work-problems-step-5-iter-changeset-required.bats +0 -95
- package/skills/work-problems/test/work-problems-step-5-prompt-body-re-grounding.bats +0 -128
- package/skills/work-problems/test/work-problems-step-5-stream-timeout-salvage.bats +0 -251
- package/skills/work-problems/test/work-problems-step-6-5-always-drain.bats +0 -203
- package/skills/work-problems/test/work-problems-step-6-5-cache-refresh-chain.bats +0 -174
- package/skills/work-problems/test/work-problems-step-6-5-fix-and-continue.bats +0 -254
- package/skills/work-problems/test/work-problems-step-6-5-postrelease-kv-callback.bats +0 -209
- package/skills/work-problems/test/work-problems-stop-condition-questions.bats +0 -85
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# @rfc RFC-002 T2 — Dual-tolerant SKILL.md glob updates
|
|
4
|
-
# @adr ADR-031 (Problem-ticket directory layout)
|
|
5
|
-
# @adr ADR-051 (load-bearing-from-the-start — the dual-tolerant glob
|
|
6
|
-
# contract ships with a behavioural enforcement test, not later by
|
|
7
|
-
# graceful drift)
|
|
8
|
-
# @adr ADR-052 (behavioural-bats default — this test exercises the
|
|
9
|
-
# glob shape against synthetic fixtures and asserts observable
|
|
10
|
-
# enumeration behaviour; it does NOT structurally grep SKILL.md
|
|
11
|
-
# prose for the dual-pattern string, which would be P081-class
|
|
12
|
-
# structural-test-disguised-as-behavioural)
|
|
13
|
-
# @adr ADR-014 (single-purpose: one mechanical contract — dual-glob
|
|
14
|
-
# enumeration parity across both layouts)
|
|
15
|
-
# @problem P069 (driving — flat layout unskimmable; the migration this
|
|
16
|
-
# contract guards is the relief)
|
|
17
|
-
# @problem P081 (no structural-grep on SKILL.md content — this test
|
|
18
|
-
# is the behavioural alternative)
|
|
19
|
-
# @jtbd JTBD-001 (extended scope — multi-commit RFC-grain coordinated
|
|
20
|
-
# change; the test is the cross-skill invariant the SKILL.md edits
|
|
21
|
-
# share)
|
|
22
|
-
# @jtbd JTBD-006 (work-backlog-AFK — dual-tolerant globs preserve
|
|
23
|
-
# AFK-loop continuity across the migration window; without this
|
|
24
|
-
# contract, mid-migration loop iterations silently miss tickets in
|
|
25
|
-
# the un-migrated layout)
|
|
26
|
-
#
|
|
27
|
-
# Contract: SKILL.md enumeration globs of `docs/problems/<state>/...`
|
|
28
|
-
# during the RFC-002 migration window MUST match BOTH the flat layout
|
|
29
|
-
# (`docs/problems/<NNN>-<title>.<state>.md`) AND the per-state subdir
|
|
30
|
-
# layout (`docs/problems/<state>/<NNN>-<title>.md`). The dual-tolerant
|
|
31
|
-
# pattern shape is:
|
|
32
|
-
#
|
|
33
|
-
# ls docs/problems/*.<state>.md docs/problems/<state>/*.md 2>/dev/null
|
|
34
|
-
#
|
|
35
|
-
# (with `2>/dev/null` swallowing the no-match error from whichever
|
|
36
|
-
# half of the OR currently has zero matches.)
|
|
37
|
-
#
|
|
38
|
-
# This test exercises the canonical dual-tolerant pattern shapes (state-
|
|
39
|
-
# filtered enumeration, ID-anchored lookup, all-state-all-tickets) on
|
|
40
|
-
# synthetic fixtures of three shapes (flat-only, per-state-only, mixed).
|
|
41
|
-
# Each cross-product asserts non-empty enumeration of every present
|
|
42
|
-
# ticket and zero false-positive enumeration of absent tickets.
|
|
43
|
-
#
|
|
44
|
-
# CONTRACT NOTE: when one half of the dual-glob has zero matches in the
|
|
45
|
-
# current fixture (single-layout fixtures), `ls X Y 2>/dev/null` exits
|
|
46
|
-
# nonzero — the unmatched literal pathname propagates to ls's argv and
|
|
47
|
-
# `2>/dev/null` only suppresses the stderr noise, not the exit code.
|
|
48
|
-
# This is CORRECT behaviour — SKILL.md call sites MUST treat STDOUT
|
|
49
|
-
# emptiness as the canonical "no tickets" signal, NOT exit code zero.
|
|
50
|
-
# Test assertions therefore probe stdout content, not `$status`, except
|
|
51
|
-
# in the empty-fixture and missing-ID cases where nonzero exit is the
|
|
52
|
-
# intended contract.
|
|
53
|
-
#
|
|
54
|
-
# T6 (post-T5 verification) drops the flat-layout half. This test
|
|
55
|
-
# updates at T6 to single-pattern, NOT removed — the contract becomes
|
|
56
|
-
# "per-state layout enumerates correctly" but remains behavioural.
|
|
57
|
-
|
|
58
|
-
setup() {
|
|
59
|
-
REPO_ROOT="$(mktemp -d)"
|
|
60
|
-
cd "$REPO_ROOT"
|
|
61
|
-
mkdir -p docs/problems
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
teardown() {
|
|
65
|
-
cd /
|
|
66
|
-
rm -rf "$REPO_ROOT"
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
# ── fixture builders ─────────────────────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
build_flat_layout() {
|
|
72
|
-
cat > docs/problems/100-foo.open.md <<'EOF'
|
|
73
|
-
# Problem 100: Foo
|
|
74
|
-
**Status**: Open
|
|
75
|
-
EOF
|
|
76
|
-
cat > docs/problems/101-bar.known-error.md <<'EOF'
|
|
77
|
-
# Problem 101: Bar
|
|
78
|
-
**Status**: Known Error
|
|
79
|
-
EOF
|
|
80
|
-
cat > docs/problems/102-baz.verifying.md <<'EOF'
|
|
81
|
-
# Problem 102: Baz
|
|
82
|
-
**Status**: Verification Pending
|
|
83
|
-
EOF
|
|
84
|
-
cat > docs/problems/103-qux.parked.md <<'EOF'
|
|
85
|
-
# Problem 103: Qux
|
|
86
|
-
**Status**: Parked
|
|
87
|
-
EOF
|
|
88
|
-
cat > docs/problems/104-quux.closed.md <<'EOF'
|
|
89
|
-
# Problem 104: Quux
|
|
90
|
-
**Status**: Closed
|
|
91
|
-
EOF
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
build_per_state_layout() {
|
|
95
|
-
mkdir -p docs/problems/open docs/problems/known-error docs/problems/verifying docs/problems/parked docs/problems/closed
|
|
96
|
-
cat > docs/problems/open/200-foo2.md <<'EOF'
|
|
97
|
-
# Problem 200: Foo2
|
|
98
|
-
**Status**: Open
|
|
99
|
-
EOF
|
|
100
|
-
cat > docs/problems/known-error/201-bar2.md <<'EOF'
|
|
101
|
-
# Problem 201: Bar2
|
|
102
|
-
**Status**: Known Error
|
|
103
|
-
EOF
|
|
104
|
-
cat > docs/problems/verifying/202-baz2.md <<'EOF'
|
|
105
|
-
# Problem 202: Baz2
|
|
106
|
-
**Status**: Verification Pending
|
|
107
|
-
EOF
|
|
108
|
-
cat > docs/problems/parked/203-qux2.md <<'EOF'
|
|
109
|
-
# Problem 203: Qux2
|
|
110
|
-
**Status**: Parked
|
|
111
|
-
EOF
|
|
112
|
-
cat > docs/problems/closed/204-quux2.md <<'EOF'
|
|
113
|
-
# Problem 204: Quux2
|
|
114
|
-
**Status**: Closed
|
|
115
|
-
EOF
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
build_mixed_layout() {
|
|
119
|
-
build_flat_layout
|
|
120
|
-
build_per_state_layout
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# ── Pattern A: state-filtered enumeration ────────────────────────────────────
|
|
124
|
-
|
|
125
|
-
@test "dual-tolerant state-filtered glob: flat-only fixture enumerates open ticket" {
|
|
126
|
-
build_flat_layout
|
|
127
|
-
run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
|
|
128
|
-
# ls exits nonzero because the per-state half has no match — stdout
|
|
129
|
-
# content is the canonical signal, not exit code.
|
|
130
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
@test "dual-tolerant state-filtered glob: per-state-only fixture enumerates open ticket" {
|
|
134
|
-
build_per_state_layout
|
|
135
|
-
run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
|
|
136
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
@test "dual-tolerant state-filtered glob: mixed fixture enumerates BOTH layouts simultaneously" {
|
|
140
|
-
build_mixed_layout
|
|
141
|
-
run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
|
|
142
|
-
[ "$status" -eq 0 ]
|
|
143
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
144
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
@test "dual-tolerant state-filtered glob: mixed fixture, known-error" {
|
|
148
|
-
build_mixed_layout
|
|
149
|
-
run bash -c 'ls docs/problems/*.known-error.md docs/problems/known-error/*.md 2>/dev/null'
|
|
150
|
-
[ "$status" -eq 0 ]
|
|
151
|
-
[[ "$output" == *"101-bar.known-error.md"* ]]
|
|
152
|
-
[[ "$output" == *"known-error/201-bar2.md"* ]]
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@test "dual-tolerant state-filtered glob: mixed fixture, verifying" {
|
|
156
|
-
build_mixed_layout
|
|
157
|
-
run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
|
|
158
|
-
[ "$status" -eq 0 ]
|
|
159
|
-
[[ "$output" == *"102-baz.verifying.md"* ]]
|
|
160
|
-
[[ "$output" == *"verifying/202-baz2.md"* ]]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@test "dual-tolerant state-filtered glob: mixed fixture, parked" {
|
|
164
|
-
build_mixed_layout
|
|
165
|
-
run bash -c 'ls docs/problems/*.parked.md docs/problems/parked/*.md 2>/dev/null'
|
|
166
|
-
[ "$status" -eq 0 ]
|
|
167
|
-
[[ "$output" == *"103-qux.parked.md"* ]]
|
|
168
|
-
[[ "$output" == *"parked/203-qux2.md"* ]]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
@test "dual-tolerant state-filtered glob: state filter excludes other-state tickets in flat layout" {
|
|
172
|
-
build_flat_layout
|
|
173
|
-
run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
|
|
174
|
-
[[ "$output" != *"known-error"* ]]
|
|
175
|
-
[[ "$output" != *"verifying"* ]]
|
|
176
|
-
[[ "$output" != *"parked"* ]]
|
|
177
|
-
[[ "$output" != *"closed"* ]]
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
@test "dual-tolerant state-filtered glob: state filter excludes other-state subdirs in per-state layout" {
|
|
181
|
-
build_per_state_layout
|
|
182
|
-
run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
|
|
183
|
-
# No known-error/, verifying/, parked/, closed/ subdir contents leak into open enumeration.
|
|
184
|
-
[[ "$output" != *"201-bar2.md"* ]]
|
|
185
|
-
[[ "$output" != *"202-baz2.md"* ]]
|
|
186
|
-
[[ "$output" != *"203-qux2.md"* ]]
|
|
187
|
-
[[ "$output" != *"204-quux2.md"* ]]
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
# ── Pattern B: ID-anchored lookup (any state) ────────────────────────────────
|
|
191
|
-
|
|
192
|
-
@test "dual-tolerant ID-anchored glob: flat-only fixture finds ticket by ID" {
|
|
193
|
-
build_flat_layout
|
|
194
|
-
run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
|
|
195
|
-
# Single-layout fixture: ls exits nonzero on the unmatched half;
|
|
196
|
-
# stdout content is the contract signal.
|
|
197
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
@test "dual-tolerant ID-anchored glob: per-state-only fixture finds ticket by ID" {
|
|
201
|
-
build_per_state_layout
|
|
202
|
-
run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
|
|
203
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@test "dual-tolerant ID-anchored glob: mixed fixture finds tickets across both layouts" {
|
|
207
|
-
build_mixed_layout
|
|
208
|
-
run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
|
|
209
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
210
|
-
run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
|
|
211
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
@test "dual-tolerant ID-anchored glob: missing ID returns empty (status nonzero from ls)" {
|
|
215
|
-
build_flat_layout
|
|
216
|
-
set +e
|
|
217
|
-
result=$(ls docs/problems/999-*.md docs/problems/*/999-*.md 2>/dev/null)
|
|
218
|
-
rc=$?
|
|
219
|
-
set -e
|
|
220
|
-
[ -z "$result" ]
|
|
221
|
-
[ "$rc" -ne 0 ]
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
# ── Pattern C: all-states-all-tickets (next-ID compute, count) ───────────────
|
|
225
|
-
|
|
226
|
-
@test "dual-tolerant all-tickets glob: flat-only enumerates every ticket regardless of state" {
|
|
227
|
-
build_flat_layout
|
|
228
|
-
run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
|
|
229
|
-
# Single-layout fixture: stdout is contract signal, not $status.
|
|
230
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
231
|
-
[[ "$output" == *"101-bar.known-error.md"* ]]
|
|
232
|
-
[[ "$output" == *"102-baz.verifying.md"* ]]
|
|
233
|
-
[[ "$output" == *"103-qux.parked.md"* ]]
|
|
234
|
-
[[ "$output" == *"104-quux.closed.md"* ]]
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
@test "dual-tolerant all-tickets glob: per-state-only enumerates every ticket regardless of state" {
|
|
238
|
-
build_per_state_layout
|
|
239
|
-
run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
|
|
240
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
241
|
-
[[ "$output" == *"known-error/201-bar2.md"* ]]
|
|
242
|
-
[[ "$output" == *"verifying/202-baz2.md"* ]]
|
|
243
|
-
[[ "$output" == *"parked/203-qux2.md"* ]]
|
|
244
|
-
[[ "$output" == *"closed/204-quux2.md"* ]]
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
@test "dual-tolerant all-tickets glob: mixed fixture enumerates ALL tickets in BOTH layouts" {
|
|
248
|
-
build_mixed_layout
|
|
249
|
-
run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
|
|
250
|
-
[ "$status" -eq 0 ]
|
|
251
|
-
for ticket in 100-foo.open.md 101-bar.known-error.md 102-baz.verifying.md 103-qux.parked.md 104-quux.closed.md \
|
|
252
|
-
open/200-foo2.md known-error/201-bar2.md verifying/202-baz2.md parked/203-qux2.md closed/204-quux2.md; do
|
|
253
|
-
[[ "$output" == *"${ticket}"* ]]
|
|
254
|
-
done
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
@test "dual-tolerant all-tickets next-ID compute: highest ID across both layouts" {
|
|
258
|
-
# Critical for capture-problem next-ID compute (architect finding 2):
|
|
259
|
-
# the next-ID surface MUST recurse so flat-layout 104 and per-state 204
|
|
260
|
-
# both contribute to max-ID; missing the per-state half re-allocates
|
|
261
|
-
# already-taken IDs.
|
|
262
|
-
build_mixed_layout
|
|
263
|
-
result=$(ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null \
|
|
264
|
-
| sed 's/.*\///' \
|
|
265
|
-
| grep -oE '^[0-9]+' \
|
|
266
|
-
| sort -n \
|
|
267
|
-
| tail -1)
|
|
268
|
-
[ "$result" = "204" ]
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
# ── Pattern D: pathspec-pair shell-glob equivalence ──────────────────────────
|
|
272
|
-
# git log accepts multiple pathspecs; the dual-tolerant filter is two
|
|
273
|
-
# pathspecs side-by-side. We validate this against the working-tree
|
|
274
|
-
# semantics that git uses (the same shell-glob shape).
|
|
275
|
-
|
|
276
|
-
@test "dual-tolerant pathspec pair: each pathspec enumerates its layout half independently" {
|
|
277
|
-
build_mixed_layout
|
|
278
|
-
flat_count=$(ls docs/problems/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
279
|
-
subdir_count=$(ls docs/problems/*/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
280
|
-
[ "$flat_count" -ge 5 ]
|
|
281
|
-
[ "$subdir_count" -ge 5 ]
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
# ── Pattern E: brace-expansion ID + state-set (report-upstream) ──────────────
|
|
285
|
-
|
|
286
|
-
@test "dual-tolerant ID + state-set lookup: flat brace expansion + per-state lookup" {
|
|
287
|
-
build_mixed_layout
|
|
288
|
-
# Old shape: ls docs/problems/${ID}-*.{open,known-error,verifying,closed}.md
|
|
289
|
-
# Dual-tolerant: add docs/problems/*/${ID}-*.md as a sibling pathspec.
|
|
290
|
-
run bash -c 'ls docs/problems/100-*.{open,known-error,verifying,closed}.md docs/problems/*/100-*.md 2>/dev/null'
|
|
291
|
-
[[ "$output" == *"100-foo.open.md"* ]]
|
|
292
|
-
run bash -c 'ls docs/problems/200-*.{open,known-error,verifying,closed}.md docs/problems/*/200-*.md 2>/dev/null'
|
|
293
|
-
[[ "$output" == *"open/200-foo2.md"* ]]
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
# ── Composition: empty-tree fixture exit-code semantics ──────────────────────
|
|
297
|
-
|
|
298
|
-
@test "dual-tolerant glob: empty fixture produces empty output and ls exits nonzero" {
|
|
299
|
-
# Critical for null-safe `2>/dev/null` semantics — `ls` on a
|
|
300
|
-
# non-matching glob exits nonzero. SKILL.md call sites must rely on
|
|
301
|
-
# `2>/dev/null` to suppress the stderr noise but still treat empty
|
|
302
|
-
# stdout as the canonical "no tickets" signal.
|
|
303
|
-
set +e
|
|
304
|
-
result=$(ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null)
|
|
305
|
-
rc=$?
|
|
306
|
-
set -e
|
|
307
|
-
[ -z "$result" ]
|
|
308
|
-
[ "$rc" -ne 0 ]
|
|
309
|
-
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# ADR-067 / P248: effort-tally.sh attributes .afk-run-state/iter*.json actuals
|
|
4
|
-
# back to their source ticket (pNNN filename token) and emits the per-ticket
|
|
5
|
-
# tally. Behavioural — exercises the script against fixture iter-JSON trees.
|
|
6
|
-
|
|
7
|
-
setup() {
|
|
8
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
9
|
-
SCRIPT="$REPO_ROOT/packages/itil/scripts/effort-tally.sh"
|
|
10
|
-
DIR="$(mktemp -d)"
|
|
11
|
-
mkdir -p "$DIR/.afk-run-state"
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
teardown() { rm -rf "$DIR"; }
|
|
15
|
-
|
|
16
|
-
mk_iter() { # mk_iter <filename> <cost> <duration_ms> <input_tokens>
|
|
17
|
-
cat > "$DIR/.afk-run-state/$1" <<EOF
|
|
18
|
-
{"total_cost_usd": $2, "duration_ms": $3, "usage": {"input_tokens": $4, "output_tokens": 0, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}
|
|
19
|
-
EOF
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@test "attributes a single iter to its ticket via the pNNN filename token" {
|
|
23
|
-
mk_iter "iter1-p087.json" 12.50 600000 1000000
|
|
24
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
25
|
-
[ "$status" -eq 0 ]
|
|
26
|
-
[[ "$output" == *"P087"* ]]
|
|
27
|
-
[[ "$output" == *"cost_usd=12.50"* ]]
|
|
28
|
-
[[ "$output" == *"minutes=10.0"* ]]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@test "sums multiple iters for the same ticket" {
|
|
32
|
-
mk_iter "iter1-p087.json" 10.00 300000 500000
|
|
33
|
-
mk_iter "iter2-p087.json" 5.00 300000 500000
|
|
34
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
35
|
-
[[ "$output" == *"P087 | iters=2 | cost_usd=15.00"* ]]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@test "authoritative cost comes from total_cost_usd; tokens flagged best-effort with ~" {
|
|
39
|
-
mk_iter "iter1-p100.json" 7.00 60000 2000000
|
|
40
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
41
|
-
[[ "$output" == *"cost_usd=7.00"* ]]
|
|
42
|
-
[[ "$output" == *"tokens=~2.0M"* ]]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@test "tickets are sorted by descending cost" {
|
|
46
|
-
mk_iter "iter1-p010.json" 3.00 60000 100000
|
|
47
|
-
mk_iter "iter1-p020.json" 30.00 60000 100000
|
|
48
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
49
|
-
# P020 (30.00) must appear before P010 (3.00)
|
|
50
|
-
[[ "$(echo "$output" | grep -n P020 | cut -d: -f1)" -lt "$(echo "$output" | grep -n P010 | cut -d: -f1)" ]]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@test "handles JSON-array (event-stream) shape, not just a single object" {
|
|
54
|
-
cat > "$DIR/.afk-run-state/iter1-p050.json" <<'EOF'
|
|
55
|
-
[{"type":"system"},{"type":"result","total_cost_usd":9.00,"duration_ms":120000,"usage":{"input_tokens":3000000,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}]
|
|
56
|
-
EOF
|
|
57
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
58
|
-
[[ "$output" == *"P050"* ]]
|
|
59
|
-
[[ "$output" == *"cost_usd=9.00"* ]]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@test "files without a pNNN token are ignored" {
|
|
63
|
-
mk_iter "drain-push.json" 99.00 60000 100000
|
|
64
|
-
cp "$DIR/.afk-run-state/drain-push.json" "$DIR/.afk-run-state/work-problems-session-totals.json"
|
|
65
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
66
|
-
[ -z "$output" ]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@test "files without total_cost_usd are skipped" {
|
|
70
|
-
echo '{"pid": 1234, "start": 999}' > "$DIR/.afk-run-state/iter1-p077.json"
|
|
71
|
-
run bash "$SCRIPT" "$DIR/.afk-run-state"
|
|
72
|
-
[ -z "$output" ]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@test "missing afk dir exits 0 with no output" {
|
|
76
|
-
run bash "$SCRIPT" "$DIR/nonexistent"
|
|
77
|
-
[ "$status" -eq 0 ]
|
|
78
|
-
[ -z "$output" ]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# --- render / write modes (ADR-067 item 2 + 2a source flag) ---
|
|
82
|
-
|
|
83
|
-
mk_ticket() { # mk_ticket <filename> <status> e.g. mk_ticket 087-foo.md Open
|
|
84
|
-
cat > "$DIR/$1" <<EOF
|
|
85
|
-
# Problem 087: Example
|
|
86
|
-
|
|
87
|
-
**Status**: $2
|
|
88
|
-
**Priority**: 6 (Medium)
|
|
89
|
-
**Effort**: M
|
|
90
|
-
|
|
91
|
-
## Description
|
|
92
|
-
|
|
93
|
-
Body.
|
|
94
|
-
|
|
95
|
-
## Related
|
|
96
|
-
|
|
97
|
-
- none
|
|
98
|
-
EOF
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
@test "--render prints an Effort Tally section with authoritative cost + best-effort tokens" {
|
|
102
|
-
mk_ticket "087-foo.md" Open
|
|
103
|
-
mk_iter "iter1-p087.json" 12.50 600000 2000000
|
|
104
|
-
run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
105
|
-
[ "$status" -eq 0 ]
|
|
106
|
-
[[ "$output" == *"## Effort Tally"* ]]
|
|
107
|
-
[[ "$output" == *"AUTO-GENERATED"* ]]
|
|
108
|
-
[[ "$output" == *'$12.50'* ]]
|
|
109
|
-
[[ "$output" == *"~2.0M"* ]]
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@test "--render buckets an Open ticket under RCA" {
|
|
113
|
-
mk_ticket "087-foo.md" Open
|
|
114
|
-
mk_iter "iter1-p087.json" 5.00 60000 100000
|
|
115
|
-
run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
116
|
-
[[ "$output" == *"RCA"* ]]
|
|
117
|
-
[[ "$output" != *"| RFC |"* ]]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
@test "--render buckets a Known Error ticket under RFC" {
|
|
121
|
-
mk_ticket "087-foo.md" "Known Error"
|
|
122
|
-
mk_iter "iter1-p087.json" 5.00 60000 100000
|
|
123
|
-
run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
124
|
-
[[ "$output" == *"RFC"* ]]
|
|
125
|
-
[[ "$output" != *"| RCA |"* ]]
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
@test "--render defaults source to afk-backfill; --source live-iter flips it" {
|
|
129
|
-
mk_ticket "087-foo.md" Open
|
|
130
|
-
mk_iter "iter1-p087.json" 5.00 60000 100000
|
|
131
|
-
run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
132
|
-
[[ "$output" == *"source: afk-backfill"* ]]
|
|
133
|
-
run bash "$SCRIPT" --render --source live-iter "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
134
|
-
[[ "$output" == *"source: live-iter"* ]]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@test "--write injects the section into the ticket and is idempotent" {
|
|
138
|
-
mk_ticket "087-foo.md" Open
|
|
139
|
-
mk_iter "iter1-p087.json" 5.00 60000 100000
|
|
140
|
-
run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
141
|
-
[ "$status" -eq 0 ]
|
|
142
|
-
grep -q "## Effort Tally" "$DIR/087-foo.md"
|
|
143
|
-
# original body preserved
|
|
144
|
-
grep -q "^## Description" "$DIR/087-foo.md"
|
|
145
|
-
# idempotent: second run produces no diff
|
|
146
|
-
cp "$DIR/087-foo.md" "$DIR/087-foo.before"
|
|
147
|
-
run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
148
|
-
run diff "$DIR/087-foo.before" "$DIR/087-foo.md"
|
|
149
|
-
[ "$status" -eq 0 ]
|
|
150
|
-
# exactly one section (no duplication)
|
|
151
|
-
[ "$(grep -c '^## Effort Tally' "$DIR/087-foo.md")" -eq 1 ]
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@test "--write lazy-empties: a ticket with zero iters gets no section" {
|
|
155
|
-
mk_ticket "099-bar.md" Open
|
|
156
|
-
run bash "$SCRIPT" --write "$DIR/099-bar.md" "$DIR/.afk-run-state"
|
|
157
|
-
[ "$status" -eq 0 ]
|
|
158
|
-
! grep -q "## Effort Tally" "$DIR/099-bar.md"
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@test "--write removes a stale section when iters disappear (lazy-empty on re-run)" {
|
|
162
|
-
mk_ticket "087-foo.md" Open
|
|
163
|
-
mk_iter "iter1-p087.json" 5.00 60000 100000
|
|
164
|
-
bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
165
|
-
grep -q "## Effort Tally" "$DIR/087-foo.md"
|
|
166
|
-
rm "$DIR/.afk-run-state/iter1-p087.json"
|
|
167
|
-
run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
|
|
168
|
-
[ "$status" -eq 0 ]
|
|
169
|
-
! grep -q "## Effort Tally" "$DIR/087-foo.md"
|
|
170
|
-
}
|