@jenga-ai/agent 1.0.0 → 1.1.0
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/README.md +28 -10
- package/agents/scrum-master.md +75 -0
- package/mcp/router/embedder.js +1 -1
- package/mcp/training_runner/index.js +239 -0
- package/mcp/training_runner/package-lock.json +1065 -0
- package/mcp/training_runner/package.json +15 -0
- package/package.json +13 -11
- package/skills/close-story/SKILL.md +203 -0
- package/skills/close-story/scripts/check-story-closeable.sh +195 -0
- package/skills/close-story/scripts/compute-scope-divergence.sh +128 -0
- package/skills/close-story/scripts/extract-diff-stats.sh +48 -0
- package/skills/close-story/scripts/extract-task-diff-stats.sh +97 -0
- package/skills/close-story/scripts/update-task-frontmatter.sh +103 -0
- package/skills/commit/SKILL.md +18 -0
- package/skills/distribute/CONFIG_SCHEMA.md +90 -0
- package/skills/distribute/SKILL.md +173 -0
- package/skills/distribute/scripts/check-version.sh +74 -0
- package/skills/distribute/scripts/commit-version-bump.sh +108 -0
- package/skills/distribute/scripts/distribute-changes.sh +381 -0
- package/skills/do/SKILL.md +314 -0
- package/skills/do/assets/intent-vs-diff-prompt.md +69 -0
- package/skills/doc/assets/path-objectives.yaml +13 -0
- package/skills/init/SKILL.md +4 -3
- package/skills/init/assets/strategy_stub_template.md +38 -0
- package/skills/init/scripts/init.sh +6 -1
- package/skills/jenga/SKILL.md +51 -2
- package/skills/strategy/SKILL.md +312 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +49 -0
- package/skills/train/SKILL.md +0 -116
- package/skills/train/assets/dashboard-templates/classifiers.html +0 -106
- package/skills/train/assets/dashboard-templates/nlp.html +0 -102
- package/skills/train/assets/dashboard-templates/transformers.html +0 -98
- package/skills/train/assets/results-parsers/__init__.py +0 -9
- package/skills/train/assets/results-parsers/classifiers.py +0 -84
- package/skills/train/assets/results-parsers/nlp.py +0 -88
- package/skills/train/assets/results-parsers/reporter.py +0 -154
- package/skills/train/assets/results-parsers/transformers.py +0 -120
- package/skills/train/train_cli.py +0 -786
package/skills/do/SKILL.md
CHANGED
|
@@ -18,9 +18,256 @@ metadata:
|
|
|
18
18
|
|
|
19
19
|
## Instructions
|
|
20
20
|
|
|
21
|
+
### 0. Load threshold config
|
|
22
|
+
|
|
23
|
+
Read `project/configs/scope-thresholds.json`.
|
|
24
|
+
|
|
25
|
+
If the file does not exist, emit:
|
|
26
|
+
```
|
|
27
|
+
ERROR: project/configs/scope-thresholds.json not found. Cannot proceed.
|
|
28
|
+
```
|
|
29
|
+
and halt. Do not fall back to any default values.
|
|
30
|
+
|
|
31
|
+
If the file is not valid JSON, emit:
|
|
32
|
+
```
|
|
33
|
+
ERROR: project/configs/scope-thresholds.json is malformed (invalid JSON). Cannot proceed.
|
|
34
|
+
```
|
|
35
|
+
and halt.
|
|
36
|
+
|
|
37
|
+
Extract the following named values for use throughout this skill:
|
|
38
|
+
- `inline_max_files` — maximum files a task may touch to qualify for inline execution scope
|
|
39
|
+
- `inline_max_lines` — maximum total lines changed for inline scope
|
|
40
|
+
- `story_max_files` — maximum files a task may touch to qualify for story-scope bundling
|
|
41
|
+
- `bundle_lock_ttl_minutes` — time-to-live in minutes for a story-scope bundle lock
|
|
42
|
+
|
|
43
|
+
These values must be read fresh on each invocation. Never use hardcoded fallbacks.
|
|
44
|
+
|
|
21
45
|
### 1. Check for `project/todo.md`
|
|
22
46
|
Run `bash scripts/todo_manager.sh exists`. If it exits non-zero, inform the user there are no queued tasks and exit.
|
|
23
47
|
|
|
48
|
+
### 1.5. Story-Bundle Execution Mode
|
|
49
|
+
|
|
50
|
+
When `/do` is invoked with a story ID (e.g., `/do E##_S##`), skip steps 2–4 and enter story-bundle execution mode:
|
|
51
|
+
|
|
52
|
+
1. **Read the story file** from `project/board/stories/<E##_S##>_*.md`. Extract the `tasks:` list. If the story file does not exist, emit:
|
|
53
|
+
```
|
|
54
|
+
ERROR: Story file for <story_id> not found. Cannot execute bundle.
|
|
55
|
+
```
|
|
56
|
+
and halt.
|
|
57
|
+
|
|
58
|
+
#### Epic-Level Bundle Lock
|
|
59
|
+
|
|
60
|
+
Before proceeding, acquire the epic-level sequential lock for this bundle:
|
|
61
|
+
|
|
62
|
+
a. **Derive the lock file path**: `project/queue/epic-lock-<E##>.json` where `<E##>` is the epic ID extracted from the story ID.
|
|
63
|
+
|
|
64
|
+
b. **Check for an existing lock**:
|
|
65
|
+
- If `project/queue/epic-lock-<E##>.json` exists:
|
|
66
|
+
1. Parse the JSON and read the `started_at` field (ISO 8601 UTC timestamp).
|
|
67
|
+
2. Compute the lock age: `age_minutes = (now_utc - started_at) / 60`.
|
|
68
|
+
3. Read `bundle_lock_ttl_minutes` from `project/configs/scope-thresholds.json` (already loaded in step 0).
|
|
69
|
+
4. If `age_minutes < bundle_lock_ttl_minutes` (lock is **non-stale**), emit:
|
|
70
|
+
```
|
|
71
|
+
BLOCKED: Epic <E##> already has a running bundle (<bundle_story_id>, started <started_at>). Wait for it to complete or for the TTL to expire.
|
|
72
|
+
```
|
|
73
|
+
and **halt** — do not proceed with this bundle.
|
|
74
|
+
5. If `age_minutes >= bundle_lock_ttl_minutes` (lock is **stale**), delete it with `rm -f project/queue/epic-lock-<E##>.json` and continue to the write step below.
|
|
75
|
+
- If `project/queue/epic-lock-<E##>.json` does not exist, continue to the write step below.
|
|
76
|
+
|
|
77
|
+
c. **Write the lock atomically**:
|
|
78
|
+
1. Compose the lock JSON:
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"epic_id": "<E##>",
|
|
82
|
+
"bundle_story_id": "<E##_S##>",
|
|
83
|
+
"started_at": "<current ISO 8601 UTC timestamp>"
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
2. Write this JSON to `project/queue/epic-lock-<E##>.json.tmp` (temporary file in the same directory).
|
|
87
|
+
3. Rename (move) `project/queue/epic-lock-<E##>.json.tmp` to `project/queue/epic-lock-<E##>.json`. This rename is atomic on POSIX filesystems and prevents any observer from reading a partially-written lock file.
|
|
88
|
+
|
|
89
|
+
> **Note:** Each epic has its own lock file keyed by `<E##>`. Bundles belonging to different epics have separate lock files and do not block each other.
|
|
90
|
+
|
|
91
|
+
> **Cleanup contract:** The epic lock file **must** be deleted on every exit path — success, failure, and interruption. Register a cleanup/trap handler immediately after writing the lock so that the lock is released even if the skill is interrupted mid-execution (e.g., `trap 'rm -f project/queue/epic-lock-<E##>.json' EXIT` in a shell implementation, or equivalent in other runtimes). Deleting a non-existent lock file must be idempotent — always use `rm -f` (never `rm` alone).
|
|
92
|
+
|
|
93
|
+
#### Pre-execution cross-bundle conflict check
|
|
94
|
+
|
|
95
|
+
After acquiring the epic lock and before writing the bundle manifest, scan all other active bundle manifests to detect file-level overlap with the current bundle:
|
|
96
|
+
|
|
97
|
+
1. **Glob other bundle manifests**: list all files matching `project/queue/bundle-*.json`. Exclude the current bundle's own file (`project/queue/bundle-<E##_S##>.json` — it does not exist yet at this point, so no special filter is needed; simply exclude any path whose basename equals `bundle-<E##_S##>.json`).
|
|
98
|
+
|
|
99
|
+
2. **If no other manifests exist**: skip steps 3–6 entirely — this step completes silently.
|
|
100
|
+
|
|
101
|
+
3. **For each other manifest file found**:
|
|
102
|
+
a. Parse the JSON and read its `task_changed_files` map.
|
|
103
|
+
b. Collect the union of all file-path arrays across every key in `task_changed_files` into a flat, deduplicated set called `other_touched_files`.
|
|
104
|
+
c. Record the other bundle's `story_id` (or derive it from the filename: `bundle-<story_id>.json`).
|
|
105
|
+
|
|
106
|
+
4. **Build the expected file set for the current bundle** (`current_expected_files`):
|
|
107
|
+
For each task in the current bundle's `tasks:` list:
|
|
108
|
+
a. Read the task file at `project/board/tasks/<task_id>_*.md`.
|
|
109
|
+
b. Extract file paths from the task's `scope_rationale` frontmatter field and from the full text of the task's `## Description` section. A string is treated as a file path if it contains a forward-slash (`/`) or a dot-separated extension (e.g. `.md`, `.json`, `.sh`, `.ts`, `.js`, `.py`). Extract all such tokens.
|
|
110
|
+
c. Add all extracted paths to `current_expected_files` (deduplicated set).
|
|
111
|
+
|
|
112
|
+
5. **Compute overlap**: for each other bundle scanned in step 3, compute the intersection of `other_touched_files` and `current_expected_files`.
|
|
113
|
+
|
|
114
|
+
6. **If any overlap is found**:
|
|
115
|
+
a. Emit the following log line to the console (one line per conflicting bundle):
|
|
116
|
+
```
|
|
117
|
+
[CROSS-BUNDLE CONFLICT] Bundle <current_story_id> and bundle <other_story_id> share expected files: <file1>, <file2>
|
|
118
|
+
```
|
|
119
|
+
b. Store the conflict data in memory as `pending_conflicts`:
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"<other_story_id>": ["<file1>", "<file2>"]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
If multiple other bundles each have overlap, accumulate all of them under their respective story IDs in `pending_conflicts`.
|
|
126
|
+
|
|
127
|
+
7. **Pass `pending_conflicts` forward**: when writing the bundle manifest in the next step (step 2 below), initialise the `cross_bundle_conflicts` key in the manifest with the contents of `pending_conflicts` (or `{}` if no conflicts were found). See "### Bundle Manifest and Rollback Anchor" for the updated manifest structure.
|
|
128
|
+
|
|
129
|
+
> **This check is non-blocking.** Regardless of whether conflicts are found, execution always continues to step 2. The conflict data is recorded for later review only.
|
|
130
|
+
|
|
131
|
+
2. **Invoke rollback anchor**: write a bundle manifest at `project/queue/bundle-<E##_S##>.json` before the first task executes. See "### Bundle Manifest and Rollback Anchor" below for the full procedure.
|
|
132
|
+
|
|
133
|
+
3. **Spawn one developer subagent** in one shared worktree named `bundle-<E##_S##>`. This is the only developer subagent for the entire story bundle — do not spawn additional subagents per task.
|
|
134
|
+
|
|
135
|
+
4. **Execute tasks sequentially** in `tasks:` list order, within the same shared developer subagent context:
|
|
136
|
+
|
|
137
|
+
For each task in the `tasks:` list:
|
|
138
|
+
|
|
139
|
+
a. **Before the task begins** — write `status: In Progress` and `date_started: <today>` to the task's frontmatter using the file-locking protocol:
|
|
140
|
+
1. Locate the task file: `project/board/tasks/<task_id>_*.md`.
|
|
141
|
+
2. Check for an existing lock file at `project/board/tasks/<task_id>_*.md.lock`. If it exists and is less than 60 seconds old, wait 10 seconds and retry once. If still locked after the retry, log a warning and skip this status write (do not block task execution).
|
|
142
|
+
3. Create the lock file: write the current ISO 8601 timestamp into `project/board/tasks/<task_id>_*.md.lock`.
|
|
143
|
+
4. Update the task frontmatter fields `status: In Progress` and `date_started: <YYYY-MM-DD>` (today's date).
|
|
144
|
+
5. Delete the lock file immediately after the write completes (before step b).
|
|
145
|
+
|
|
146
|
+
b. **Pass task context** to the shared developer subagent: task file content (title, description, acceptance criteria), parent story file, and parent epic file.
|
|
147
|
+
|
|
148
|
+
c. **After successful task**: first run the **Intent-vs-Diff Check** (see `### 5.1. Intent-vs-Diff Check` below) for this task. Then write `status: Passed` and `date_completed: <today>` to the task's frontmatter using the file-locking protocol:
|
|
149
|
+
1. Check for an existing lock file at `project/board/tasks/<task_id>_*.md.lock`. If it exists and is less than 60 seconds old, wait 10 seconds and retry once. If still locked, log a warning and skip this status write.
|
|
150
|
+
2. Create the lock file: write the current ISO 8601 timestamp into `project/board/tasks/<task_id>_*.md.lock`.
|
|
151
|
+
3. Update the task frontmatter fields `status: Passed` and `date_completed: <YYYY-MM-DD>` (today's date).
|
|
152
|
+
4. Delete the lock file immediately after the write completes.
|
|
153
|
+
> The lock must be released before proceeding to task N+1. Never hold a lock across task execution.
|
|
154
|
+
|
|
155
|
+
c.1. **Post-task file extraction** — immediately after the status write in step c and before proceeding to the next task, record the files changed by this task into the bundle manifest:
|
|
156
|
+
1. Run `git diff --name-only HEAD~1` to obtain the list of files changed by the just-completed task. Capture each line as a relative file path. If the command returns empty output (the task made no commits), treat the result as an empty array `[]` — this is not an error.
|
|
157
|
+
2. Read the bundle manifest at `project/queue/bundle-<E##_S##>.json`.
|
|
158
|
+
3. Add or update the key `task_changed_files` in the manifest object. Set `task_changed_files[<task_id>]` to the captured file list (an array of strings, or `[]` if no files changed). If `task_changed_files` does not yet exist in the manifest, create it as an empty object first.
|
|
159
|
+
4. Write the updated manifest atomically:
|
|
160
|
+
a. Serialise the updated manifest to JSON.
|
|
161
|
+
b. Write the JSON to `project/queue/bundle-<E##_S##>.json.tmp`.
|
|
162
|
+
c. Rename (move) `project/queue/bundle-<E##_S##>.json.tmp` to `project/queue/bundle-<E##_S##>.json`. The rename is atomic on POSIX filesystems and prevents any consumer from reading a partially-written file.
|
|
163
|
+
> This step is mandatory before proceeding to task N+1. The extraction must complete (even if the file list is empty) before the next task begins.
|
|
164
|
+
|
|
165
|
+
c.2. **Conflict report** — immediately after step c.1, compare the extracted file list against the files the task was expected to touch, and write a conflict report if unexpected files are found:
|
|
166
|
+
|
|
167
|
+
1. **Extract expected files** from the task's frontmatter field `scope_rationale` and from the task's `## Description` section. Use a best-effort prose heuristic: split the text on whitespace and punctuation, then retain any token that either (a) contains a `/` character or (b) matches the pattern `*.*` (a dot surrounded by non-dot characters on both sides, e.g. `SKILL.md`, `foo.json`). Collect all retained tokens into a set called `expected_files`. This is intentionally permissive — false positives (expected files that were never actually changed) are acceptable and produce no report.
|
|
168
|
+
|
|
169
|
+
2. **Compute unexpected files**: let `actual_files` = the array stored at `task_changed_files[<task_id>]` in the bundle manifest (from step c.1). Compute `unexpected = actual_files − expected_files` (set difference: files in `actual_files` that have no match in `expected_files`). Matching is case-sensitive and exact against the relative path or the basename of the path — a token like `SKILL.md` matches any actual file whose basename is `SKILL.md` (e.g. `skills/do/SKILL.md`).
|
|
170
|
+
|
|
171
|
+
3. **If `unexpected` is non-empty**:
|
|
172
|
+
a. Write a Markdown conflict report to `project/queue/conflict-<task_id>.md` with the following structure:
|
|
173
|
+
```markdown
|
|
174
|
+
# Conflict Report: <task_id>
|
|
175
|
+
|
|
176
|
+
**Generated:** <ISO 8601 UTC timestamp>
|
|
177
|
+
**Bundle:** <E##_S##>
|
|
178
|
+
|
|
179
|
+
## Task ID
|
|
180
|
+
<task_id>
|
|
181
|
+
|
|
182
|
+
## Expected Files
|
|
183
|
+
Files inferred from scope_rationale and description:
|
|
184
|
+
- <file1>
|
|
185
|
+
- ...
|
|
186
|
+
|
|
187
|
+
## Actual Files Changed
|
|
188
|
+
Files changed by this task (from git diff):
|
|
189
|
+
- <file1>
|
|
190
|
+
- ...
|
|
191
|
+
|
|
192
|
+
## Unexpected Files
|
|
193
|
+
Files changed that were not anticipated by the task definition:
|
|
194
|
+
- <file1>
|
|
195
|
+
- ...
|
|
196
|
+
```
|
|
197
|
+
b. Read the bundle manifest at `project/queue/bundle-<E##_S##>.json`.
|
|
198
|
+
c. Append `<task_id>` to the `conflict_reports` array in the manifest. If the key `conflict_reports` does not yet exist, create it as an empty array first.
|
|
199
|
+
d. Write the updated manifest atomically (tmp-file rename pattern — same as step c.1.4).
|
|
200
|
+
|
|
201
|
+
4. **If `unexpected` is empty**: skip this step entirely. Do not write a conflict report. Do not modify the bundle manifest.
|
|
202
|
+
|
|
203
|
+
> **This step is non-blocking.** A conflict report does NOT affect the task's `Passed` status or halt bundle execution. It is informational only — a human can review `project/queue/conflict-<task_id>.md` after the bundle completes.
|
|
204
|
+
|
|
205
|
+
d. **After task failure** — write `status: Failed` to the task's frontmatter using the file-locking protocol:
|
|
206
|
+
1. Check for an existing lock file at `project/board/tasks/<task_id>_*.md.lock`. If it exists and is less than 60 seconds old, wait 10 seconds and retry once. If still locked, log a warning and skip this status write.
|
|
207
|
+
2. Create the lock file: write the current ISO 8601 timestamp into `project/board/tasks/<task_id>_*.md.lock`.
|
|
208
|
+
3. Update the task frontmatter field `status: Failed`.
|
|
209
|
+
4. Delete the lock file immediately after the write completes.
|
|
210
|
+
Then invoke the rollback anchor cleanup procedure (see "### Bundle Manifest and Rollback Anchor" — failure path). After the rollback anchor completes, **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`. Mark the bundle as failed and **halt** — do not execute any remaining tasks in the sequence.
|
|
211
|
+
|
|
212
|
+
5. **Post-bundle verification** (runs only if all tasks completed without failure):
|
|
213
|
+
- Inspect each task in the bundle for `needs_docs: true` in its frontmatter.
|
|
214
|
+
- **If any task has `needs_docs: true`**: invoke the tester agent once for the full story. Pass the story ID, the list of all task IDs in the bundle, and the shared worktree path. The tester is responsible for updating individual task statuses.
|
|
215
|
+
- **If all tasks have `needs_docs: false`**: the developer agent self-verifies — reviews each task's implementation against its acceptance criteria without invoking the tester. After self-verification passes, write `status: Passed` and `date_completed: <YYYY-MM-DD>` (today's date) to each task file using the file-locking protocol (steps c.1–c.4 above). No tester invocation occurs.
|
|
216
|
+
|
|
217
|
+
6. **Cleanup**: on successful bundle completion, invoke the rollback anchor success path (see "### Bundle Manifest and Rollback Anchor" — success path) to delete the bundle manifest. Then **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`.
|
|
218
|
+
|
|
219
|
+
### Bundle Manifest and Rollback Anchor
|
|
220
|
+
|
|
221
|
+
The bundle manifest records the repository state before any task in the bundle executes. It serves as the anchor for rollback on failure and as a reference for conflict reports (E32_S07) and concurrency locks (E32_S08).
|
|
222
|
+
|
|
223
|
+
#### Before the first task executes
|
|
224
|
+
|
|
225
|
+
1. Run `git rev-parse HEAD` to capture the current HEAD SHA as `anchor_sha`.
|
|
226
|
+
2. Write `project/queue/bundle-<E##_S##>.json` with the following structure:
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"story_id": "<E##_S##>",
|
|
230
|
+
"anchor_sha": "<SHA from git rev-parse HEAD>",
|
|
231
|
+
"started_at": "<ISO 8601 UTC timestamp>",
|
|
232
|
+
"tasks": ["<E##_S##_T##>", "..."],
|
|
233
|
+
"task_changed_files": {},
|
|
234
|
+
"conflict_reports": [],
|
|
235
|
+
"cross_bundle_conflicts": {}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
- `story_id`: the story ID for this bundle (e.g. `E32_S05`)
|
|
239
|
+
- `anchor_sha`: the full 40-character SHA returned by `git rev-parse HEAD`
|
|
240
|
+
- `started_at`: current UTC timestamp in ISO 8601 format (e.g. `2026-08-16T12:00:00Z`)
|
|
241
|
+
- `tasks`: ordered array of task IDs drawn from the story's `tasks:` frontmatter list
|
|
242
|
+
- `task_changed_files`: starts as an empty object `{}`; populated after each task completes (see step 4c.1). Each key is a task ID and each value is an array of relative file paths changed by that task.
|
|
243
|
+
- `conflict_reports`: starts as an empty array `[]`; populated after each task's step 4c.2 conflict check. Each entry is a task ID string for which a conflict report was written to `project/queue/conflict-<task_id>.md`.
|
|
244
|
+
- `cross_bundle_conflicts`: populated from the pre-execution conflict check (see "Pre-execution cross-bundle conflict check" above). Each key is the `story_id` of a conflicting bundle and each value is an array of overlapping file paths. Initialised with the `pending_conflicts` data collected during the conflict check; if no conflicts were found, initialised as `{}`.
|
|
245
|
+
|
|
246
|
+
Do not proceed to the first task until this file is written successfully.
|
|
247
|
+
|
|
248
|
+
#### On any task failure (failure path)
|
|
249
|
+
|
|
250
|
+
Execute the following steps in order. Do not skip any step.
|
|
251
|
+
|
|
252
|
+
1. **Save partial diff**: run `git diff <anchor_sha> HEAD` and write the output to `project/queue/bundle-<E##_S##>-partial.patch`. This captures all commits made since the anchor, providing a record of partial work for diagnosis.
|
|
253
|
+
2. **Verify clean worktree**: run `git status --porcelain`. If the output is non-empty, there are uncommitted changes that would cause `git reset --hard` to error or lose work. In this case, emit:
|
|
254
|
+
```
|
|
255
|
+
ROLLBACK WARNING: worktree has uncommitted changes. Stashing before reset.
|
|
256
|
+
```
|
|
257
|
+
Then run `git stash` before proceeding. This ensures the reset completes without git complaints.
|
|
258
|
+
3. **Reset to anchor**: run `git reset --hard <anchor_sha>`. The repository returns to the exact state it was in before the bundle started.
|
|
259
|
+
4. **Delete bundle manifest**: remove `project/queue/bundle-<E##_S##>.json`.
|
|
260
|
+
|
|
261
|
+
After completing the failure path, halt bundle execution. Do not execute any remaining tasks in the sequence.
|
|
262
|
+
|
|
263
|
+
#### On successful bundle completion (success path)
|
|
264
|
+
|
|
265
|
+
1. **Delete bundle manifest**: remove `project/queue/bundle-<E##_S##>.json`.
|
|
266
|
+
|
|
267
|
+
No patch file is written on success.
|
|
268
|
+
|
|
269
|
+
If `/do` is invoked with a task ID (`E##_S##_T##`) or a plain-text title, skip this section and use the normal task execution path (steps 2–8 below).
|
|
270
|
+
|
|
24
271
|
### 2. List tasks and let the user choose
|
|
25
272
|
Run `bash scripts/todo_manager.sh list` to display the queued tasks. Ask the user:
|
|
26
273
|
- Execute a specific task (by number or title)
|
|
@@ -52,6 +299,39 @@ Before starting:
|
|
|
52
299
|
2. If the file does not exist, warn the user and skip — do not proceed with a task that has no scrum board definition
|
|
53
300
|
3. Present a brief summary of the task: title, acceptance criteria, parent story, parent epic
|
|
54
301
|
|
|
302
|
+
### 4.1. Override validation
|
|
303
|
+
|
|
304
|
+
Before invoking the developer, check whether this task was manually scoped by a human operator.
|
|
305
|
+
|
|
306
|
+
1. Read `jenga_assigned` from the resolved task's frontmatter.
|
|
307
|
+
2. If `jenga_assigned` is `true` or the field is absent — proceed to step 5 without any further check.
|
|
308
|
+
3. If `jenga_assigned` is `false`:
|
|
309
|
+
a. Read `override_justification` from the task's frontmatter.
|
|
310
|
+
b. If `override_justification` is absent or its value is an empty string, **halt execution** and emit:
|
|
311
|
+
```
|
|
312
|
+
OVERRIDE VALIDATION ERROR [<task_id>]: jenga_assigned is false but override_justification is missing or empty. Add a justification before proceeding.
|
|
313
|
+
```
|
|
314
|
+
Do not invoke the developer. Do not remove the task from `project/todo.md`. Return control to the user.
|
|
315
|
+
c. If `override_justification` is present and non-empty, log:
|
|
316
|
+
```
|
|
317
|
+
Override acknowledged for <task_id>: <override_justification>
|
|
318
|
+
```
|
|
319
|
+
Then proceed to step 5.
|
|
320
|
+
|
|
321
|
+
### 4.2. Inline Execution Path (execution_scope: inline)
|
|
322
|
+
|
|
323
|
+
If the resolved task has `execution_scope: inline` in its frontmatter, execute the task directly in the current session without spawning a developer subagent:
|
|
324
|
+
|
|
325
|
+
1. Read the task file and load its full content (description, acceptance criteria).
|
|
326
|
+
2. Implement the task inline — make the required changes to files directly in the current session.
|
|
327
|
+
3. Commit the changes using the standard commit convention (`task(<task_id>): <short description>`).
|
|
328
|
+
4. Run the **Intent-vs-Diff Check** (see `### 5.1. Intent-vs-Diff Check` below) for this task.
|
|
329
|
+
5. Self-verify the implementation against the acceptance criteria.
|
|
330
|
+
6. Write `status: Passed` and `date_completed: <today>` to the task's frontmatter if verification passes.
|
|
331
|
+
7. Continue to step 6 (documentation verification) and then step 7 (post-completion).
|
|
332
|
+
|
|
333
|
+
If the implementation cannot be completed inline (scope is larger than anticipated), abort and re-route to the normal developer path (step 5).
|
|
334
|
+
|
|
55
335
|
### 5. Invoke the developer agent
|
|
56
336
|
Pass the following to the developer agent:
|
|
57
337
|
|
|
@@ -68,6 +348,40 @@ The developer agent will:
|
|
|
68
348
|
- Implement, commit at milestones, and invoke the tester agent
|
|
69
349
|
- Return when the tester has verified the work
|
|
70
350
|
|
|
351
|
+
### 5.1. Intent-vs-Diff Check (needs_docs: false only)
|
|
352
|
+
|
|
353
|
+
After the developer agent returns (or after inline execution completes), run the following check if the task has `needs_docs: false` in its frontmatter. If `needs_docs: true`, skip this check entirely — the full documentation lifecycle handles divergence detection for those tasks.
|
|
354
|
+
|
|
355
|
+
**Steps:**
|
|
356
|
+
|
|
357
|
+
1. Read `needs_docs` from the task's frontmatter. If `needs_docs: true` (or absent and defaulting to `true`), skip to step 6.
|
|
358
|
+
|
|
359
|
+
2. Run `git diff --name-only HEAD~1` to retrieve the list of changed file names (relative paths, one per line).
|
|
360
|
+
|
|
361
|
+
3. Read the prompt template from `skills/do/assets/intent-vs-diff-prompt.md`. Extract the prompt block (the content between the triple backticks under `## Prompt`).
|
|
362
|
+
|
|
363
|
+
4. Substitute the placeholders:
|
|
364
|
+
- `{description}` — full text of the task's `## Description` section
|
|
365
|
+
- `{acceptance_criteria}` — full text of the task's `## Acceptance Criteria` section
|
|
366
|
+
- `{changed_files}` — newline-separated output from step 2
|
|
367
|
+
|
|
368
|
+
5. Invoke the LLM with the constructed prompt (using the orchestrating agent's LLM access). Ask for a JSON array response only.
|
|
369
|
+
|
|
370
|
+
6. Parse the LLM response as a JSON array:
|
|
371
|
+
- If parsing fails (malformed JSON): treat as `[]`, emit: `[intent-vs-diff] LLM response was not valid JSON; treating as no divergence.`, and continue.
|
|
372
|
+
- If the array is empty (`[]`): no action — do not modify the task frontmatter.
|
|
373
|
+
- If the array is non-empty: write `divergence_flag: true` to the task's frontmatter, then emit:
|
|
374
|
+
```
|
|
375
|
+
[DIVERGENCE WARNING] Task <task_id>: the following files were changed but are not mentioned or inferable from the task description:
|
|
376
|
+
- <file1>
|
|
377
|
+
- <file2>
|
|
378
|
+
This is a non-blocking warning. Task outcome is not affected.
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**This check is non-blocking.** It does not change the task's Passed/Failed outcome. It only writes `divergence_flag: true` and emits a warning for human review. Execution continues regardless of the check result.
|
|
382
|
+
|
|
383
|
+
**Prompt calibration:** The prompt in `skills/do/assets/intent-vs-diff-prompt.md` is tuned to flag only files with zero plausible connection to the stated task. Test files, documentation files, lock files, and clearly implied files are excluded from flagging. See the `## False-Positive Tuning Rationale` section in the prompt template for full details.
|
|
384
|
+
|
|
71
385
|
### 6. Verify documentation
|
|
72
386
|
After the developer completes the task, confirm the following documentation was written:
|
|
73
387
|
- **Execution plan** — `project/documentation/plans/<E##_S##_T##>-plan.md` must exist (written by the developer before starting work)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Intent-vs-Diff LLM Prompt Template
|
|
2
|
+
|
|
3
|
+
This file contains the structured prompt used by `/do` to compare a completed task's stated intent against its actual diff. It is invoked **only** for tasks where `needs_docs: false`.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
After a `needs_docs: false` task completes, `/do` runs `git diff --name-only HEAD~1` to get the list of changed file names and passes them — along with the task description and acceptance criteria — to the LLM using the prompt below. The LLM identifies any files that fall outside the expected scope of the task, enabling early detection of unregistered scope expansion.
|
|
8
|
+
|
|
9
|
+
## False-Positive Tuning Rationale
|
|
10
|
+
|
|
11
|
+
The prompt is calibrated to minimize false positives:
|
|
12
|
+
|
|
13
|
+
1. **Test file exemption**: Test files that correspond to source files explicitly mentioned in the task description are NOT flagged. Adding tests is universally expected and implied by any implementation task.
|
|
14
|
+
2. **Documentation file exemption**: Files like `README.md`, `WARP.md`, `CHANGELOG.md`, and any `*.md` in a `docs/` directory are NOT flagged. Documentation updates are standard outputs of any change.
|
|
15
|
+
3. **Inferability threshold — both conditions required**: A file is only flagged when it is BOTH (a) not mentioned by name in the description, AND (b) not clearly inferable from what the description says. A task that says "update the login handler" implicitly covers the login handler's test file, its dependency injector, and related middleware — even if those are not named. Only files with zero connection to the stated task are flagged.
|
|
16
|
+
4. **JSON-only output**: The LLM is asked to return only a JSON array to eliminate ambiguity in parsing the response.
|
|
17
|
+
5. **Config/lock file exemption**: `package-lock.json`, `yarn.lock`, `*.lock`, and other machine-managed files are NOT flagged — they change as a side-effect of dependency changes and are never the primary intent of a task.
|
|
18
|
+
|
|
19
|
+
## Prompt
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
You are reviewing a completed task to check if the implementation stayed within scope.
|
|
23
|
+
|
|
24
|
+
Task description: {description}
|
|
25
|
+
|
|
26
|
+
Acceptance criteria:
|
|
27
|
+
{acceptance_criteria}
|
|
28
|
+
|
|
29
|
+
Changed files:
|
|
30
|
+
{changed_files}
|
|
31
|
+
|
|
32
|
+
Your job: identify any changed files that are NOT mentioned and NOT clearly inferable from the task description or acceptance criteria above.
|
|
33
|
+
|
|
34
|
+
Rules for what NOT to flag:
|
|
35
|
+
- Do not flag test files (*.test.*, *.spec.*, files in __tests__/ or test/ directories) for source files that are explicitly mentioned or clearly implied in the task.
|
|
36
|
+
- Do not flag documentation files (*.md, files in docs/ directories, README, WARP.md, CHANGELOG).
|
|
37
|
+
- Do not flag lock files (package-lock.json, yarn.lock, *.lock, *.sum) or generated files (*.generated.*, dist/, build/).
|
|
38
|
+
- Do not flag configuration files (*.config.*, .env.example, tsconfig.json) unless the task has zero connection to configuration.
|
|
39
|
+
- Only flag a file when it has zero plausible connection to the stated task description. If you can construct a reasonable sentence explaining why the file might be touched given the task description, do not flag it.
|
|
40
|
+
|
|
41
|
+
Return ONLY a JSON array of file paths that are unexpected scope expansions. If all files are expected or fall under the exemptions above, return an empty array.
|
|
42
|
+
|
|
43
|
+
Examples of correct output:
|
|
44
|
+
- All files expected: []
|
|
45
|
+
- Two unexpected files: ["src/billing/invoice.ts", "scripts/deploy.sh"]
|
|
46
|
+
|
|
47
|
+
Do not include any explanation, commentary, or text outside the JSON array.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage in `/do`
|
|
51
|
+
|
|
52
|
+
When the check runs, replace the template placeholders as follows:
|
|
53
|
+
|
|
54
|
+
- `{description}` — the full text of the task's `## Description` section from the task board file
|
|
55
|
+
- `{acceptance_criteria}` — the full text of the task's `## Acceptance Criteria` section (each criterion on its own line, including the `- [ ]` or `- [x]` prefix)
|
|
56
|
+
- `{changed_files}` — the newline-separated output of `git diff --name-only HEAD~1`, one file path per line
|
|
57
|
+
|
|
58
|
+
After receiving the LLM response:
|
|
59
|
+
|
|
60
|
+
1. Attempt to parse the response as a JSON array.
|
|
61
|
+
2. If parsing fails (malformed JSON), treat it as `[]` — do not flag divergence. Emit a debug note: `[intent-vs-diff] LLM response was not valid JSON; treating as no divergence.`
|
|
62
|
+
3. If the array is empty (`[]`), proceed without modifying the task frontmatter.
|
|
63
|
+
4. If the array is non-empty, write `divergence_flag: true` to the task's frontmatter and emit the warning:
|
|
64
|
+
```
|
|
65
|
+
[DIVERGENCE WARNING] Task <task_id>: the following files were changed but are not mentioned or inferable from the task description:
|
|
66
|
+
- <file1>
|
|
67
|
+
- <file2>
|
|
68
|
+
This is a non-blocking warning. Task outcome is not affected.
|
|
69
|
+
```
|
|
@@ -36,3 +36,16 @@ rules:
|
|
|
36
36
|
required_sections:
|
|
37
37
|
- Release Entries
|
|
38
38
|
- Notable Changes
|
|
39
|
+
- path: docs/STRATEGY.md
|
|
40
|
+
objective: strategy brief
|
|
41
|
+
summary: Produce a high-level strategy brief suitable for investors and strategic partners. Covers vision, value proposition, scope, and target audience. Does not include revenue model, pricing, or competitive analysis.
|
|
42
|
+
required_sections:
|
|
43
|
+
- Vision
|
|
44
|
+
- Value Proposition
|
|
45
|
+
- Scope
|
|
46
|
+
- Target Audience
|
|
47
|
+
generation_rules:
|
|
48
|
+
tone: Write for an investor or strategic partner audience. Use clear, confident language. Avoid jargon and internal shorthand. Do not use first-person plural ("we") excessively — prefer direct statements about the project.
|
|
49
|
+
sourcing: Draw content exclusively from PROJECT_SUMMARY.md and existing docs/. Do not invent facts, figures, or claims not present in the source material.
|
|
50
|
+
missing_fields: If the project has insufficient context to fill a section, produce a stub with the section header and a clearly marked note — e.g. "<!-- TODO: fill in once product direction is confirmed -->" — rather than erroring or hallucinating content.
|
|
51
|
+
exclusions: Do not include revenue model, pricing strategy, monetisation plans, competitive analysis, or financial projections. If any of these are present in source files, omit them silently.
|
package/skills/init/SKILL.md
CHANGED
|
@@ -35,10 +35,11 @@ This script handles all scaffolding in one step:
|
|
|
35
35
|
6. Creates `project/configs/test-config.json` stub
|
|
36
36
|
7. Creates `project/data/baselines.json`
|
|
37
37
|
8. Creates `project/logs/events.json`
|
|
38
|
-
9.
|
|
38
|
+
9. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
|
|
39
|
+
10. Stages and commits all files with the message `init: scaffold project structure and workflow config`
|
|
39
40
|
|
|
40
41
|
If the script fails, check that you are in the project root and that git is available.
|
|
41
42
|
|
|
42
|
-
###
|
|
43
|
+
### 11. Prompt next step
|
|
43
44
|
|
|
44
|
-
Inform the user that setup is complete and
|
|
45
|
+
Inform the user that setup is complete. Mention that `docs/STRATEGY.md` was created as a strategic brief stub for investors, partners, and the product team — they can fill it in now or return to it later. Suggest running `/pi-plan` to define project goals and epics.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Strategy
|
|
2
|
+
|
|
3
|
+
<!-- This document is intended for investors, strategic partners, and senior stakeholders.
|
|
4
|
+
Write in clear, confident language that conveys conviction and focus.
|
|
5
|
+
Avoid jargon. Prioritise substance over length. -->
|
|
6
|
+
|
|
7
|
+
## Vision
|
|
8
|
+
|
|
9
|
+
<!-- Describe the long-term direction of this project over a 3–5 year horizon.
|
|
10
|
+
What change do you want to see in the world, and what role does this project play in bringing it about?
|
|
11
|
+
A strong vision statement is specific, ambitious, and grounded — it should be possible to hold yourself accountable to it. -->
|
|
12
|
+
|
|
13
|
+
## Value Proposition
|
|
14
|
+
|
|
15
|
+
<!-- Articulate what makes this project uniquely valuable and to whom.
|
|
16
|
+
Answer: Why does this exist? Why now? Why this team?
|
|
17
|
+
Focus on the distinct advantage or insight that underpins the project — not features, but the underlying value delivered. -->
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
|
|
21
|
+
### In Scope
|
|
22
|
+
|
|
23
|
+
<!-- List the capabilities, domains, or problem spaces this project actively addresses.
|
|
24
|
+
Be specific enough that a new stakeholder can quickly understand the boundaries of the work.
|
|
25
|
+
Use bullet points for readability. -->
|
|
26
|
+
|
|
27
|
+
### Out of Scope
|
|
28
|
+
|
|
29
|
+
<!-- List what this project explicitly does not cover.
|
|
30
|
+
Revenue model, pricing strategy, and competitive analysis are intentionally excluded from this document —
|
|
31
|
+
they belong in separate artefacts and are out of scope here.
|
|
32
|
+
Use this section to prevent scope creep and set clear expectations with stakeholders. -->
|
|
33
|
+
|
|
34
|
+
## Target Audience
|
|
35
|
+
|
|
36
|
+
<!-- Describe who this project is built for.
|
|
37
|
+
Include both the end users (who experiences the product) and the stakeholders (who evaluates or funds it).
|
|
38
|
+
Be specific: a well-defined audience sharpens every other section of this document. -->
|
|
@@ -39,7 +39,12 @@ echo '{}' > project/data/baselines.json
|
|
|
39
39
|
echo "→ Creating events.json..."
|
|
40
40
|
echo '[]' > project/logs/events.json
|
|
41
41
|
|
|
42
|
-
# ─── 9.
|
|
42
|
+
# ─── 9. Create docs/STRATEGY.md ──────────────────────────────────────────────
|
|
43
|
+
echo "→ Creating docs/STRATEGY.md (strategic brief for investors, partners, and the product team)..."
|
|
44
|
+
mkdir -p docs
|
|
45
|
+
cp "$ASSETS_DIR/strategy_stub_template.md" docs/STRATEGY.md
|
|
46
|
+
|
|
47
|
+
# ─── 10. Initial commit ──────────────────────────────────────────────────────
|
|
43
48
|
echo "→ Staging and committing scaffolded files..."
|
|
44
49
|
git add -A
|
|
45
50
|
git commit -m "init: scaffold project structure and workflow config"
|
package/skills/jenga/SKILL.md
CHANGED
|
@@ -22,6 +22,30 @@ metadata:
|
|
|
22
22
|
|
|
23
23
|
## Instructions
|
|
24
24
|
|
|
25
|
+
### Phase 0 — Load threshold config
|
|
26
|
+
|
|
27
|
+
Read `project/configs/scope-thresholds.json`.
|
|
28
|
+
|
|
29
|
+
If the file does not exist, emit:
|
|
30
|
+
```
|
|
31
|
+
ERROR: project/configs/scope-thresholds.json not found. Cannot proceed.
|
|
32
|
+
```
|
|
33
|
+
and halt. Do not fall back to any default values.
|
|
34
|
+
|
|
35
|
+
If the file is not valid JSON, emit:
|
|
36
|
+
```
|
|
37
|
+
ERROR: project/configs/scope-thresholds.json is malformed (invalid JSON). Cannot proceed.
|
|
38
|
+
```
|
|
39
|
+
and halt.
|
|
40
|
+
|
|
41
|
+
Extract the following named values for use throughout this skill:
|
|
42
|
+
- `inline_max_files` — maximum files a task may touch to qualify for inline execution scope
|
|
43
|
+
- `inline_max_lines` — maximum total lines changed for inline scope
|
|
44
|
+
- `story_max_files` — maximum files a task may touch to qualify for story-scope bundling
|
|
45
|
+
- `bundle_lock_ttl_minutes` — time-to-live in minutes for a story-scope bundle lock
|
|
46
|
+
|
|
47
|
+
These values must be read fresh on each invocation. Never use hardcoded fallbacks.
|
|
48
|
+
|
|
25
49
|
### Phase 1 — Decompose Epics into Stories
|
|
26
50
|
|
|
27
51
|
Read all files in `project/board/epics/`. For each Epic that has no corresponding story files in `project/board/stories/` (i.e. no files whose name starts with that Epic's ID), invoke `/do` via a **scrum-master sub-agent** to break it down into Stories.
|
|
@@ -40,11 +64,32 @@ Read all files in `project/board/tasks/`. For every Task not already listed in `
|
|
|
40
64
|
|
|
41
65
|
After this phase, `todo.md` reflects the full set of work on the board.
|
|
42
66
|
|
|
67
|
+
### Phase 3.5 — Story-bundle detection
|
|
68
|
+
|
|
69
|
+
Before dispatching individual tasks in Phase 4, check each story for bundle eligibility. This phase runs once after Phase 3 completes.
|
|
70
|
+
|
|
71
|
+
For each story that has one or more tasks listed in `todo.md`:
|
|
72
|
+
|
|
73
|
+
1. **Read the story file** — parse the `tasks:` frontmatter array to get the ordered list of task IDs.
|
|
74
|
+
2. **Guard: empty task list** — if the `tasks:` list is empty (zero entries), this story is **not** eligible for the bundle path. Skip to per-task dispatch in Phase 4.
|
|
75
|
+
3. **Read each task file** — for every task ID in the `tasks:` list, read the corresponding task file from `project/board/tasks/`.
|
|
76
|
+
4. **Collect `execution_scope`** — extract the `execution_scope` field from each task's YAML frontmatter. If the field is absent or has any value other than `story`, treat that task as **not** story-scoped.
|
|
77
|
+
5. **Apply the all-or-nothing rule** — a story qualifies for the bundle path **only if every task** in its `tasks:` list has `execution_scope: story`. A single task with a different scope (or a missing field) disqualifies the entire story.
|
|
78
|
+
6. **Route bundle candidates** — if all tasks in the story are `execution_scope: story` and the list is non-empty:
|
|
79
|
+
a. Emit:
|
|
80
|
+
```
|
|
81
|
+
BUNDLE DETECTED: story <E##_S##> — <N> story-scoped tasks will execute as a bundle.
|
|
82
|
+
```
|
|
83
|
+
where `<E##_S##>` is the story ID and `<N>` is the count of tasks in the list.
|
|
84
|
+
b. Call `/do <E##_S##>` once (with the story ID, not individual task IDs). This invokes the bundle execution path in `/do` (implemented in E32_S05_T02), which runs all tasks sequentially in one shared worktree.
|
|
85
|
+
c. **Mark these tasks as bundled** — record their task IDs so Phase 4 skips individual dispatch for them.
|
|
86
|
+
7. **Non-bundle stories** — stories with a mixed scope, a zero-length task list, or any task missing `execution_scope: story` use the normal per-task dispatch in Phase 4 without any change.
|
|
87
|
+
|
|
43
88
|
### Phase 4 — Execute
|
|
44
89
|
|
|
45
|
-
Loop through `todo.md` and execute all eligible items, running independent ones in parallel
|
|
90
|
+
Loop through `todo.md` and execute all eligible items, running independent ones in parallel. Use the threshold values loaded in Phase 0 (`inline_max_files`, `inline_max_lines`, `story_max_files`, `bundle_lock_ttl_minutes`) when applying execution-scope logic to each task. **Skip any task that was bundled in Phase 3.5** — those tasks will be handled by the `/do` story-bundle call already issued.
|
|
46
91
|
|
|
47
|
-
1. **Collect eligible items** — from `todo.md`, find all items whose board file has `status: Pending` and no unresolved dependencies. A dependency is resolved if the blocking item's status is at least `Running` or `Passed`.
|
|
92
|
+
1. **Collect eligible items** — from `todo.md`, find all items whose board file has `status: Pending` and no unresolved dependencies, **excluding tasks already dispatched as part of a story bundle in Phase 3.5**. A dependency is resolved if the blocking item's status is at least `Running` or `Passed`.
|
|
48
93
|
2. **Group by parallelism** — items with no shared dependencies and no overlapping output files can run concurrently. Items that depend on each other must be sequenced.
|
|
49
94
|
3. **Invoke `/do` in parallel** — launch each independent item as a **background sub-agent** simultaneously. Do not wait for one to finish before starting another if they are independent.
|
|
50
95
|
4. **Mark Running** — update `status: Running` in each launched item's board file (YAML front-matter) immediately after launch.
|
|
@@ -66,3 +111,7 @@ When no eligible candidates remain in Phase 4, exit and output:
|
|
|
66
111
|
- **All tasks in `todo.md` already Running/Passed** — exits cleanly with the completion message.
|
|
67
112
|
- **Unresolved dependencies** — item is skipped in Phase 4 until its blockers are at least `Running`.
|
|
68
113
|
- **`/do` failure (background agent)** — treated as a skip; mark the item's status back to `Pending` and continue the loop with remaining candidates.
|
|
114
|
+
- **Story with zero tasks (empty `tasks:` list)** — does not enter the bundle path in Phase 3.5; tasks (if any appear in `todo.md` independently) are dispatched normally in Phase 4.
|
|
115
|
+
- **Story with mixed `execution_scope` values** — falls back entirely to per-task dispatch in Phase 4; no partial bundling occurs.
|
|
116
|
+
- **Task file missing `execution_scope` field** — treated as not story-scoped; the containing story is disqualified from the bundle path.
|
|
117
|
+
- **Bundle `/do` call failure** — treated as a skip for the entire bundle; mark all bundled tasks' status back to `Pending` and continue Phase 4 with remaining non-bundled candidates.
|