@lvlup-sw/exarchos 2.0.1

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.
Files changed (153) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +17 -0
  3. package/.mcp.json +17 -0
  4. package/AGENTS.md +59 -0
  5. package/CLAUDE.md.template +62 -0
  6. package/LICENSE +202 -0
  7. package/README.md +258 -0
  8. package/commands/autocompact.md +37 -0
  9. package/commands/checkpoint.md +85 -0
  10. package/commands/cleanup.md +99 -0
  11. package/commands/debug.md +145 -0
  12. package/commands/delegate.md +56 -0
  13. package/commands/ideate.md +82 -0
  14. package/commands/plan.md +150 -0
  15. package/commands/refactor.md +139 -0
  16. package/commands/reload.md +37 -0
  17. package/commands/resume.md +130 -0
  18. package/commands/review.md +51 -0
  19. package/commands/sync-schemas.md +74 -0
  20. package/commands/synthesize.md +122 -0
  21. package/commands/tdd.md +58 -0
  22. package/dist/exarchos-cli.js +8828 -0
  23. package/dist/exarchos-mcp.js +50 -0
  24. package/hooks/hooks.json +53 -0
  25. package/package.json +59 -0
  26. package/rules/coding-standards.md +46 -0
  27. package/rules/mcp-tool-guidance.md +26 -0
  28. package/rules/pr-descriptions.md +12 -0
  29. package/rules/rm-safety.md +9 -0
  30. package/rules/skill-path-resolution.md +10 -0
  31. package/rules/tdd.md +41 -0
  32. package/rules/telemetry-awareness.md +9 -0
  33. package/scripts/assess-refactor-scope.sh +239 -0
  34. package/scripts/check-benchmark-regression.sh +229 -0
  35. package/scripts/check-coderabbit.sh +288 -0
  36. package/scripts/check-coverage-thresholds.sh +194 -0
  37. package/scripts/check-polish-scope.sh +245 -0
  38. package/scripts/check-property-tests.sh +167 -0
  39. package/scripts/check-tdd-compliance.sh +265 -0
  40. package/scripts/coderabbit-review-gate.sh +518 -0
  41. package/scripts/debug-review-gate.sh +201 -0
  42. package/scripts/extract-fix-tasks.sh +179 -0
  43. package/scripts/extract-task.sh +67 -0
  44. package/scripts/generate-traceability.sh +209 -0
  45. package/scripts/investigation-timer.sh +171 -0
  46. package/scripts/needs-schema-sync.sh +174 -0
  47. package/scripts/new-project.sh +103 -0
  48. package/scripts/post-delegation-check.sh +317 -0
  49. package/scripts/pre-synthesis-check.sh +440 -0
  50. package/scripts/reconcile-state.sh +346 -0
  51. package/scripts/reconstruct-stack.sh +432 -0
  52. package/scripts/review-diff.sh +63 -0
  53. package/scripts/review-verdict.sh +169 -0
  54. package/scripts/security-scan.sh +248 -0
  55. package/scripts/select-debug-track.sh +186 -0
  56. package/scripts/setup-worktree.sh +323 -0
  57. package/scripts/spec-coverage-check.sh +230 -0
  58. package/scripts/static-analysis-gate.sh +236 -0
  59. package/scripts/sync-labels.sh +122 -0
  60. package/scripts/validate-companion.sh +161 -0
  61. package/scripts/validate-dotnet-standards.sh +267 -0
  62. package/scripts/validate-installation.sh +101 -0
  63. package/scripts/validate-plugin.sh +223 -0
  64. package/scripts/validate-refactor.sh +234 -0
  65. package/scripts/validate-rm.sh +93 -0
  66. package/scripts/verify-delegation-saga.sh +240 -0
  67. package/scripts/verify-doc-links.sh +211 -0
  68. package/scripts/verify-ideate-artifacts.sh +296 -0
  69. package/scripts/verify-plan-coverage.sh +228 -0
  70. package/scripts/verify-review-triage.sh +219 -0
  71. package/scripts/verify-worktree-baseline.sh +159 -0
  72. package/scripts/verify-worktree.sh +84 -0
  73. package/settings.json +47 -0
  74. package/skills/brainstorming/SKILL.md +127 -0
  75. package/skills/brainstorming/references/design-template.md +65 -0
  76. package/skills/cleanup/SKILL.md +147 -0
  77. package/skills/cleanup/references/merge-verification.md +40 -0
  78. package/skills/debug/SKILL.md +204 -0
  79. package/skills/debug/references/hotfix-track.md +134 -0
  80. package/skills/debug/references/investigation-checklist.md +217 -0
  81. package/skills/debug/references/rca-template.md +150 -0
  82. package/skills/debug/references/state-schema.md +294 -0
  83. package/skills/debug/references/thorough-track.md +194 -0
  84. package/skills/debug/references/triage-questions.md +155 -0
  85. package/skills/debug/references/troubleshooting.md +47 -0
  86. package/skills/delegation/SKILL.md +150 -0
  87. package/skills/delegation/references/adaptive-orchestration.md +31 -0
  88. package/skills/delegation/references/agent-teams-saga.md +248 -0
  89. package/skills/delegation/references/fix-mode.md +74 -0
  90. package/skills/delegation/references/fixer-prompt.md +162 -0
  91. package/skills/delegation/references/implementer-prompt.md +322 -0
  92. package/skills/delegation/references/parallel-strategy.md +124 -0
  93. package/skills/delegation/references/pbt-patterns.md +172 -0
  94. package/skills/delegation/references/pr-fixes-mode.md +154 -0
  95. package/skills/delegation/references/state-management.md +51 -0
  96. package/skills/delegation/references/testing-patterns.md +129 -0
  97. package/skills/delegation/references/troubleshooting.md +33 -0
  98. package/skills/delegation/references/workflow-steps.md +127 -0
  99. package/skills/delegation/references/worktree-enforcement.md +64 -0
  100. package/skills/dotnet-standards/SKILL.md +269 -0
  101. package/skills/dotnet-standards/references/csharp-standards.md +120 -0
  102. package/skills/dotnet-standards/templates/.editorconfig +366 -0
  103. package/skills/dotnet-standards/templates/Directory.Build.props +56 -0
  104. package/skills/dotnet-standards/templates/Directory.Packages.props +69 -0
  105. package/skills/dotnet-standards/templates/global.json +6 -0
  106. package/skills/dotnet-standards/templates/nuget.config +9 -0
  107. package/skills/dotnet-standards/templates/stylecop.json +37 -0
  108. package/skills/git-worktrees/SKILL.md +255 -0
  109. package/skills/implementation-planning/SKILL.md +233 -0
  110. package/skills/implementation-planning/references/plan-document-template.md +42 -0
  111. package/skills/implementation-planning/references/spec-tracing-guide.md +51 -0
  112. package/skills/implementation-planning/references/task-template.md +43 -0
  113. package/skills/implementation-planning/references/testing-strategy-guide.md +88 -0
  114. package/skills/quality-review/SKILL.md +278 -0
  115. package/skills/quality-review/references/code-quality-checklist.md +159 -0
  116. package/skills/quality-review/references/review-report-template.md +65 -0
  117. package/skills/quality-review/references/security-checklist.md +79 -0
  118. package/skills/quality-review/references/typescript-standards.md +24 -0
  119. package/skills/refactor/COMMAND.md +67 -0
  120. package/skills/refactor/SKILL.md +198 -0
  121. package/skills/refactor/phases/auto-chain.md +262 -0
  122. package/skills/refactor/phases/brief.md +176 -0
  123. package/skills/refactor/phases/explore.md +132 -0
  124. package/skills/refactor/phases/overhaul-delegate.md +136 -0
  125. package/skills/refactor/phases/overhaul-plan.md +312 -0
  126. package/skills/refactor/phases/overhaul-review.md +304 -0
  127. package/skills/refactor/phases/polish-implement.md +349 -0
  128. package/skills/refactor/phases/polish-validate.md +218 -0
  129. package/skills/refactor/phases/update-docs.md +234 -0
  130. package/skills/refactor/references/brief-template.md +81 -0
  131. package/skills/refactor/references/doc-update-checklist.md +110 -0
  132. package/skills/refactor/references/explore-checklist.md +73 -0
  133. package/skills/refactor/references/overhaul-track.md +215 -0
  134. package/skills/refactor/references/polish-track.md +170 -0
  135. package/skills/shared/prompts/context-reading.md +58 -0
  136. package/skills/shared/prompts/report-format.md +54 -0
  137. package/skills/shared/prompts/tdd-requirements.md +39 -0
  138. package/skills/shepherd/SKILL.md +264 -0
  139. package/skills/shepherd/references/assess-checklist.md +124 -0
  140. package/skills/shepherd/references/fix-strategies.md +191 -0
  141. package/skills/spec-review/SKILL.md +229 -0
  142. package/skills/spec-review/references/review-checklist.md +60 -0
  143. package/skills/sync-schemas/SKILL.md +114 -0
  144. package/skills/sync-schemas/references/configuration.md +73 -0
  145. package/skills/synthesis/SKILL.md +129 -0
  146. package/skills/synthesis/references/pr-descriptions.md +87 -0
  147. package/skills/synthesis/references/synthesis-steps.md +109 -0
  148. package/skills/synthesis/references/troubleshooting.md +115 -0
  149. package/skills/validate-all-skills.sh +57 -0
  150. package/skills/validate-frontmatter.sh +237 -0
  151. package/skills/workflow-state/SKILL.md +210 -0
  152. package/skills/workflow-state/references/mcp-tool-reference.md +111 -0
  153. package/skills/workflow-state/references/phase-transitions.md +141 -0
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env bash
2
+ # Validate Refactor
3
+ # Runs tests/lint/typecheck with structured pass/fail output.
4
+ # Replaces validate phase prose checklist with deterministic validation.
5
+ #
6
+ # Usage: validate-refactor.sh --repo-root <path> [--skip-lint] [--skip-typecheck]
7
+ #
8
+ # Exit codes:
9
+ # 0 = all checks pass
10
+ # 1 = one or more checks failed
11
+ # 2 = usage error (missing required args)
12
+
13
+ set -euo pipefail
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+
17
+ # Colors
18
+ RED='\033[0;31m'
19
+ GREEN='\033[0;32m'
20
+ YELLOW='\033[1;33m'
21
+ NC='\033[0m'
22
+
23
+ # ============================================================
24
+ # ARGUMENT PARSING
25
+ # ============================================================
26
+
27
+ REPO_ROOT=""
28
+ SKIP_LINT=false
29
+ SKIP_TYPECHECK=false
30
+
31
+ usage() {
32
+ cat << 'USAGE'
33
+ Usage: validate-refactor.sh --repo-root <path> [--skip-lint] [--skip-typecheck]
34
+
35
+ Required:
36
+ --repo-root <path> Path to the repository root (must contain package.json)
37
+
38
+ Optional:
39
+ --skip-lint Skip lint check
40
+ --skip-typecheck Skip typecheck
41
+ --help Show this help message
42
+
43
+ Exit codes:
44
+ 0 All checks pass
45
+ 1 One or more checks failed
46
+ 2 Usage error (missing required args)
47
+
48
+ Checks performed:
49
+ - npm run test:run (required)
50
+ - npm run lint (skipped if missing or --skip-lint)
51
+ - npm run typecheck (skipped if missing or --skip-typecheck)
52
+ USAGE
53
+ }
54
+
55
+ while [[ $# -gt 0 ]]; do
56
+ case "$1" in
57
+ --repo-root)
58
+ if [[ -z "${2:-}" ]]; then
59
+ echo "Error: --repo-root requires a path argument" >&2
60
+ exit 2
61
+ fi
62
+ REPO_ROOT="$2"
63
+ shift 2
64
+ ;;
65
+ --skip-lint)
66
+ SKIP_LINT=true
67
+ shift
68
+ ;;
69
+ --skip-typecheck)
70
+ SKIP_TYPECHECK=true
71
+ shift
72
+ ;;
73
+ --help)
74
+ usage
75
+ exit 0
76
+ ;;
77
+ *)
78
+ echo "Error: Unknown argument '$1'" >&2
79
+ usage >&2
80
+ exit 2
81
+ ;;
82
+ esac
83
+ done
84
+
85
+ if [[ -z "$REPO_ROOT" ]]; then
86
+ echo "Error: --repo-root is required" >&2
87
+ usage >&2
88
+ exit 2
89
+ fi
90
+
91
+ # ============================================================
92
+ # CHECK FUNCTIONS
93
+ # ============================================================
94
+
95
+ CHECK_PASS=0
96
+ CHECK_FAIL=0
97
+ RESULTS=()
98
+
99
+ check_pass() {
100
+ local name="$1"
101
+ RESULTS+=("- **PASS**: $name")
102
+ CHECK_PASS=$((CHECK_PASS + 1))
103
+ }
104
+
105
+ check_fail() {
106
+ local name="$1"
107
+ local detail="${2:-}"
108
+ if [[ -n "$detail" ]]; then
109
+ RESULTS+=("- **FAIL**: $name — $detail")
110
+ else
111
+ RESULTS+=("- **FAIL**: $name")
112
+ fi
113
+ CHECK_FAIL=$((CHECK_FAIL + 1))
114
+ }
115
+
116
+ check_skip() {
117
+ local name="$1"
118
+ RESULTS+=("- **SKIP**: $name")
119
+ }
120
+
121
+ # ============================================================
122
+ # HELPER: Check if npm script exists in package.json
123
+ # ============================================================
124
+
125
+ has_npm_script() {
126
+ local script_name="$1"
127
+ if [[ -f "$REPO_ROOT/package.json" ]]; then
128
+ # Use node or jq to check; fall back to grep
129
+ if command -v jq &>/dev/null; then
130
+ jq -e ".scripts[\"$script_name\"]" "$REPO_ROOT/package.json" &>/dev/null
131
+ return $?
132
+ else
133
+ grep -q "\"$script_name\"" "$REPO_ROOT/package.json" 2>/dev/null
134
+ return $?
135
+ fi
136
+ fi
137
+ return 1
138
+ }
139
+
140
+ # ============================================================
141
+ # CHECK 1: Tests (npm run test:run)
142
+ # ============================================================
143
+
144
+ check_tests() {
145
+ local output
146
+ if ! output="$(cd "$REPO_ROOT" && npm run test:run 2>&1)"; then
147
+ check_fail "Tests (npm run test:run)" "Tests failed"
148
+ return 1
149
+ fi
150
+ check_pass "Tests (npm run test:run)"
151
+ return 0
152
+ }
153
+
154
+ # ============================================================
155
+ # CHECK 2: Lint (npm run lint)
156
+ # ============================================================
157
+
158
+ check_lint() {
159
+ if [[ "$SKIP_LINT" == true ]]; then
160
+ check_skip "Lint (--skip-lint)"
161
+ return 0
162
+ fi
163
+
164
+ if ! has_npm_script "lint"; then
165
+ check_skip "Lint (no lint script in package.json)"
166
+ return 0
167
+ fi
168
+
169
+ local output
170
+ if ! output="$(cd "$REPO_ROOT" && npm run lint 2>&1)"; then
171
+ check_fail "Lint (npm run lint)" "Lint errors found"
172
+ return 1
173
+ fi
174
+ check_pass "Lint (npm run lint)"
175
+ return 0
176
+ }
177
+
178
+ # ============================================================
179
+ # CHECK 3: Typecheck (npm run typecheck)
180
+ # ============================================================
181
+
182
+ check_typecheck() {
183
+ if [[ "$SKIP_TYPECHECK" == true ]]; then
184
+ check_skip "Typecheck (--skip-typecheck)"
185
+ return 0
186
+ fi
187
+
188
+ if ! has_npm_script "typecheck"; then
189
+ check_skip "Typecheck (no typecheck script in package.json)"
190
+ return 0
191
+ fi
192
+
193
+ local output
194
+ if ! output="$(cd "$REPO_ROOT" && npm run typecheck 2>&1)"; then
195
+ check_fail "Typecheck (npm run typecheck)" "Type errors found"
196
+ return 1
197
+ fi
198
+ check_pass "Typecheck (npm run typecheck)"
199
+ return 0
200
+ }
201
+
202
+ # ============================================================
203
+ # EXECUTE CHECKS
204
+ # ============================================================
205
+
206
+ check_tests || true
207
+ check_lint || true
208
+ check_typecheck || true
209
+
210
+ # ============================================================
211
+ # STRUCTURED OUTPUT
212
+ # ============================================================
213
+
214
+ echo "## Refactor Validation Report"
215
+ echo ""
216
+ echo "**Repository:** \`$REPO_ROOT\`"
217
+ echo ""
218
+
219
+ for result in "${RESULTS[@]}"; do
220
+ echo "$result"
221
+ done
222
+
223
+ echo ""
224
+ TOTAL=$((CHECK_PASS + CHECK_FAIL))
225
+ echo "---"
226
+ echo ""
227
+
228
+ if [[ $CHECK_FAIL -eq 0 ]]; then
229
+ echo "**Result: PASS** ($CHECK_PASS/$TOTAL checks passed)"
230
+ exit 0
231
+ else
232
+ echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL checks failed)"
233
+ exit 1
234
+ fi
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # Validates rm commands - blocks those targeting outside current directory
3
+ # Exit 0 = allow, Exit 2 = block with message to stderr
4
+
5
+ set -euo pipefail
6
+
7
+ # Read tool input from stdin
8
+ INPUT=$(cat)
9
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
10
+
11
+ # If not an rm command, allow it
12
+ if [[ ! "$COMMAND" =~ ^[[:space:]]*(rm|/bin/rm|/usr/bin/rm)[[:space:]] ]]; then
13
+ exit 0
14
+ fi
15
+
16
+ # Extract the working directory
17
+ CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
18
+ [[ -z "$CWD" ]] && CWD="$PWD"
19
+
20
+ # Function to check if a path is inside CWD
21
+ is_inside_cwd() {
22
+ local target="$1"
23
+ local resolved
24
+
25
+ # Handle relative paths
26
+ if [[ "$target" != /* ]]; then
27
+ target="$CWD/$target"
28
+ fi
29
+
30
+ # Resolve to absolute path (handle .., symlinks, etc)
31
+ # Use realpath if target exists, otherwise normalize manually
32
+ if [[ -e "$target" ]]; then
33
+ resolved=$(realpath "$target" 2>/dev/null) || resolved="$target"
34
+ else
35
+ # For non-existent paths, normalize parent + basename
36
+ local parent=$(dirname "$target")
37
+ local base=$(basename "$target")
38
+ if [[ -d "$parent" ]]; then
39
+ resolved="$(realpath "$parent")/$base"
40
+ else
41
+ resolved="$target"
42
+ fi
43
+ fi
44
+
45
+ # Check if resolved path starts with CWD
46
+ local resolved_cwd
47
+ resolved_cwd=$(realpath "$CWD" 2>/dev/null) || resolved_cwd="$CWD"
48
+
49
+ [[ "$resolved" == "$resolved_cwd" || "$resolved" == "$resolved_cwd"/* ]]
50
+ }
51
+
52
+ # Quick sanity check for obviously catastrophic commands
53
+ # The path resolution below handles the full check
54
+ NORMALIZED_CMD=$(echo "$COMMAND" | tr -s ' ')
55
+ if [[ "$NORMALIZED_CMD" =~ rm[[:space:]]+-[rRf]*[[:space:]]+/[[:space:]]*$ ]] || \
56
+ [[ "$NORMALIZED_CMD" =~ rm[[:space:]]+-[rRf]*[[:space:]]+/\*[[:space:]]*$ ]]; then
57
+ echo "BLOCKED: rm targeting filesystem root" >&2
58
+ exit 2
59
+ fi
60
+
61
+ # Parse rm arguments to find target paths
62
+ # Strip rm command and flags, get remaining arguments
63
+ TARGETS=$(echo "$COMMAND" | sed -E 's/^[[:space:]]*(rm|\/bin\/rm|\/usr\/bin\/rm)[[:space:]]+//' | \
64
+ sed -E 's/-[rRfivI]+[[:space:]]*//g' | \
65
+ sed -E 's/--[a-z-]+[[:space:]]*//g' | \
66
+ xargs -n1 2>/dev/null || true)
67
+
68
+ # If no targets found, allow (rm with no args will fail anyway)
69
+ [[ -z "$TARGETS" ]] && exit 0
70
+
71
+ # Check each target
72
+ BLOCKED_PATHS=()
73
+ while IFS= read -r target; do
74
+ [[ -z "$target" ]] && continue
75
+
76
+ # Skip if target contains unexpanded variables (could be dangerous)
77
+ if [[ "$target" == *'$'* ]]; then
78
+ BLOCKED_PATHS+=("$target (contains unexpanded variable)")
79
+ continue
80
+ fi
81
+
82
+ if ! is_inside_cwd "$target"; then
83
+ BLOCKED_PATHS+=("$target")
84
+ fi
85
+ done <<< "$TARGETS"
86
+
87
+ if [[ ${#BLOCKED_PATHS[@]} -gt 0 ]]; then
88
+ echo "BLOCKED: rm targets paths outside current directory ($CWD):" >&2
89
+ printf " - %s\n" "${BLOCKED_PATHS[@]}" >&2
90
+ exit 2
91
+ fi
92
+
93
+ exit 0
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env bash
2
+ # verify-delegation-saga.sh — Verify saga step ordering in delegation event streams
3
+ # Reads a JSONL event file and validates that delegation saga events appear in correct order.
4
+ #
5
+ # Usage: verify-delegation-saga.sh --feature-id <id> [--state-dir <path>]
6
+ #
7
+ # Exit codes:
8
+ # 0 = valid saga (or no team events to validate)
9
+ # 1 = violations found
10
+ # 2 = usage error (missing args, no event file, empty stream)
11
+
12
+ set -euo pipefail
13
+
14
+ # ============================================================
15
+ # ARGUMENT PARSING
16
+ # ============================================================
17
+
18
+ FEATURE_ID=""
19
+ STATE_DIR="${HOME}/.claude/workflow-state"
20
+
21
+ usage() {
22
+ cat >&2 << 'USAGE'
23
+ Usage: verify-delegation-saga.sh --feature-id <id> [--state-dir <path>]
24
+
25
+ Required:
26
+ --feature-id <id> Feature identifier (used to locate events JSONL file)
27
+
28
+ Optional:
29
+ --state-dir <path> Directory containing event files (default: ~/.claude/workflow-state/)
30
+ --help Show this help message
31
+
32
+ Exit codes:
33
+ 0 Valid saga ordering (or no team events to validate)
34
+ 1 Saga ordering violations found
35
+ 2 Usage error (missing args, no event file, empty stream)
36
+ USAGE
37
+ }
38
+
39
+ while [[ $# -gt 0 ]]; do
40
+ case "$1" in
41
+ --feature-id)
42
+ if [[ -z "${2:-}" ]]; then
43
+ echo "Error: --feature-id requires a value" >&2
44
+ exit 2
45
+ fi
46
+ FEATURE_ID="$2"
47
+ shift 2
48
+ ;;
49
+ --state-dir)
50
+ if [[ -z "${2:-}" ]]; then
51
+ echo "Error: --state-dir requires a path argument" >&2
52
+ exit 2
53
+ fi
54
+ STATE_DIR="$2"
55
+ shift 2
56
+ ;;
57
+ --help)
58
+ usage
59
+ exit 0
60
+ ;;
61
+ *)
62
+ echo "Error: Unknown argument '$1'" >&2
63
+ usage
64
+ exit 2
65
+ ;;
66
+ esac
67
+ done
68
+
69
+ if [[ -z "$FEATURE_ID" ]]; then
70
+ echo "Error: --feature-id is required" >&2
71
+ usage
72
+ exit 2
73
+ fi
74
+
75
+ # ============================================================
76
+ # DEPENDENCY CHECK
77
+ # ============================================================
78
+
79
+ if ! command -v jq &>/dev/null; then
80
+ echo "Error: jq is required but not installed" >&2
81
+ exit 2
82
+ fi
83
+
84
+ # ============================================================
85
+ # LOCATE EVENT FILE
86
+ # ============================================================
87
+
88
+ EVENT_FILE="${STATE_DIR}/${FEATURE_ID}.events.jsonl"
89
+
90
+ if [[ ! -f "$EVENT_FILE" ]]; then
91
+ echo "Error: Event file not found: $EVENT_FILE" >&2
92
+ exit 2
93
+ fi
94
+
95
+ # Check for empty file
96
+ if [[ ! -s "$EVENT_FILE" ]]; then
97
+ echo "Error: Event file is empty: $EVENT_FILE" >&2
98
+ exit 2
99
+ fi
100
+
101
+ # ============================================================
102
+ # EXTRACT TEAM EVENTS
103
+ # ============================================================
104
+
105
+ # Filter to only team.* events, preserving order
106
+ TEAM_EVENTS="$(jq -c 'select(.type | startswith("team."))' "$EVENT_FILE")"
107
+
108
+ # If no team events exist, nothing to validate — exit clean
109
+ if [[ -z "$TEAM_EVENTS" ]]; then
110
+ echo "No team events found in event stream. Skipping saga validation."
111
+ exit 0
112
+ fi
113
+
114
+ # ============================================================
115
+ # VALIDATION STATE
116
+ # ============================================================
117
+
118
+ VIOLATIONS=()
119
+ HAS_SPAWNED=false
120
+ HAS_PLANNED=false
121
+ HAS_DISBANDED=false
122
+ DISBANDED_SEQUENCE=0
123
+
124
+ # Track planned task IDs
125
+ declare -a PLANNED_TASK_IDS=()
126
+
127
+ # Track dispatched task IDs (from assignedTaskIds)
128
+ declare -a DISPATCHED_TASK_IDS=()
129
+
130
+ # ============================================================
131
+ # RULE VALIDATION — Process events in sequence order
132
+ # ============================================================
133
+
134
+ while IFS= read -r event; do
135
+ event_type="$(echo "$event" | jq -r '.type')"
136
+ event_seq="$(echo "$event" | jq -r '.sequence')"
137
+
138
+ case "$event_type" in
139
+ team.spawned)
140
+ HAS_SPAWNED=true
141
+ ;;
142
+
143
+ team.task.planned)
144
+ # Rule 1: team.spawned must appear before any team.task.planned
145
+ if [[ "$HAS_SPAWNED" != true ]]; then
146
+ VIOLATIONS+=("VIOLATION: team.task.planned (seq $event_seq) appeared before team.spawned")
147
+ fi
148
+
149
+ # Rule 4: team.disbanded must be the last team event
150
+ if [[ "$HAS_DISBANDED" == true ]]; then
151
+ VIOLATIONS+=("VIOLATION: team.task.planned (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
152
+ fi
153
+
154
+ # Track planned task IDs — support both single taskId and batched taskIds[]
155
+ while IFS= read -r tid; do
156
+ PLANNED_TASK_IDS+=("$tid")
157
+ done < <(echo "$event" | jq -r '(.data.taskIds // [])[] // empty, (.data.taskId // empty)')
158
+ HAS_PLANNED=true
159
+ ;;
160
+
161
+ team.teammate.dispatched)
162
+ # Rule 1: team.spawned must appear before dispatch
163
+ if [[ "$HAS_SPAWNED" != true ]]; then
164
+ VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared before team.spawned")
165
+ fi
166
+
167
+ # Rule 2: team.task.planned must appear before any team.teammate.dispatched
168
+ if [[ "$HAS_PLANNED" != true ]]; then
169
+ VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared before any team.task.planned")
170
+ fi
171
+
172
+ # Rule 4: team.disbanded must be the last team event
173
+ if [[ "$HAS_DISBANDED" == true ]]; then
174
+ VIOLATIONS+=("VIOLATION: team.teammate.dispatched (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
175
+ fi
176
+
177
+ # Track dispatched task IDs for coverage check (safe on missing/null field)
178
+ while IFS= read -r tid; do
179
+ DISPATCHED_TASK_IDS+=("$tid")
180
+ done < <(echo "$event" | jq -r '(.data.assignedTaskIds // [])[]')
181
+ ;;
182
+
183
+ team.disbanded)
184
+ HAS_DISBANDED=true
185
+ DISBANDED_SEQUENCE="$event_seq"
186
+ ;;
187
+
188
+ # Other team.* events — no specific rules yet, but check disbanded constraint
189
+ team.*)
190
+ if [[ "$HAS_DISBANDED" == true ]]; then
191
+ VIOLATIONS+=("VIOLATION: $event_type (seq $event_seq) appeared after team.disbanded (seq $DISBANDED_SEQUENCE)")
192
+ fi
193
+ ;;
194
+ esac
195
+ done <<< "$TEAM_EVENTS"
196
+
197
+ # ============================================================
198
+ # RULE 3: All dispatched task IDs must have been planned
199
+ # ============================================================
200
+
201
+ if [[ ${#DISPATCHED_TASK_IDS[@]} -gt 0 ]]; then
202
+ for dispatched_id in "${DISPATCHED_TASK_IDS[@]}"; do
203
+ found=false
204
+ if [[ ${#PLANNED_TASK_IDS[@]} -gt 0 ]]; then
205
+ for planned_id in "${PLANNED_TASK_IDS[@]}"; do
206
+ if [[ "$dispatched_id" == "$planned_id" ]]; then
207
+ found=true
208
+ break
209
+ fi
210
+ done
211
+ fi
212
+ if [[ "$found" != true ]]; then
213
+ VIOLATIONS+=("VIOLATION: Dispatched task '$dispatched_id' was never planned (no team.task.planned event with this taskId)")
214
+ fi
215
+ done
216
+ fi
217
+
218
+ # ============================================================
219
+ # OUTPUT AND EXIT
220
+ # ============================================================
221
+
222
+ if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
223
+ echo "## Delegation Saga Validation"
224
+ echo ""
225
+ echo "**Status:** FAILED for feature \`$FEATURE_ID\`"
226
+ echo ""
227
+ echo "### Violations"
228
+ echo ""
229
+ for v in "${VIOLATIONS[@]}"; do
230
+ echo "- $v"
231
+ done
232
+ echo ""
233
+ echo "**Total:** ${#VIOLATIONS[@]} violation(s) found."
234
+ exit 1
235
+ fi
236
+
237
+ echo "## Delegation Saga Validation"
238
+ echo ""
239
+ echo "**Status:** PASSED for feature \`$FEATURE_ID\`"
240
+ exit 0