@jenga-ai/agent 1.1.0 → 1.2.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 +7 -3
- package/agents/developer.md +82 -2
- package/agents/scrum-master.md +140 -21
- package/agents/tester.md +90 -8
- package/hooks/on_session_end.sh +171 -20
- package/package.json +1 -1
- package/scripts/check-permission-level.sh +107 -0
- package/scripts/check-publicignore-match.sh +122 -0
- package/scripts/check-worktree-liveness.sh +193 -0
- package/scripts/generate-rapport-manifest.sh +43 -0
- package/scripts/idea_manager.sh +47 -0
- package/scripts/install-worktree-commit-guard.sh +134 -0
- package/scripts/jenga-permission-level-switch.sh +109 -0
- package/scripts/smoke-harness.sh +139 -0
- package/scripts/validate-board.sh +62 -0
- package/scripts/with-lock.sh +158 -0
- package/scripts/worktree-remove-guard.sh +204 -0
- package/skills/clearify/SKILL.md +52 -0
- package/skills/commit/SKILL.md +13 -4
- package/skills/distribute/CONFIG_SCHEMA.md +60 -2
- package/skills/do/SKILL.md +48 -11
- package/skills/doc-sync/SKILL.md +16 -0
- package/skills/doc-sync/assets/doc_targets.md +11 -0
- package/skills/idea/SKILL.md +56 -0
- package/skills/idea/assets/idea_handoff_template.md +26 -0
- package/skills/idea/assets/idea_template.md +3 -0
- package/skills/init/SKILL.md +100 -7
- package/skills/init/assets/directory_structure.txt +1 -0
- package/skills/init/assets/workflow_template.json +1 -1
- package/skills/init/scripts/apply-project-visibility.sh +176 -0
- package/skills/init/scripts/detect-existing-codebase.sh +166 -0
- package/skills/init/scripts/init.sh +30 -1
- package/skills/jenga/SKILL.md +160 -17
- package/skills/jenga/scripts/board-scan.sh +238 -0
- package/skills/jenga/scripts/cascade-resolve.sh +297 -0
- package/skills/jenga/scripts/render-confirmation.sh +679 -0
- package/skills/jenga/scripts/render-picker.sh +439 -0
- package/skills/jenga/scripts/resolve-id.sh +367 -0
- package/skills/jenga-permission-level/SKILL.md +81 -0
- package/skills/proceed/SKILL.md +1 -1
- package/skills/publish/SKILL.md +8 -5
- package/skills/publish/assets/ci-contract.md +2 -2
- package/skills/publish/assets/ownership-matrix.md +1 -1
- package/skills/publish/scripts/finalize_changelog.sh +115 -0
- package/skills/publish/scripts/generate_release_notes.sh +475 -28
- package/skills/publish/scripts/npm_ci_pipeline.sh +44 -6
- package/skills/publish/scripts/publish_deploy.sh +38 -8
- package/skills/publish/scripts/run_gates.sh +2 -2
- package/skills/reconcile/SKILL.md +117 -5
- package/skills/reconcile/scripts/detect-unlinked-code.sh +741 -0
- package/skills/skillify/assets/init-new/assets/directory_structure.txt +5 -1
- package/skills/spinoff/SKILL.md +12 -7
- package/skills/todo/SKILL.md +2 -0
- package/skills/uncharted/SKILL.md +711 -0
- package/skills/uncharted/assets/SEGMENT_PROPOSAL_TEMPLATE.md +129 -0
- package/skills/uncharted/assets/UNDERSTANDING_DOC_TEMPLATE.md +160 -0
- package/skills/uncharted/scripts/apply-subsystem-cap.sh +573 -0
- package/skills/uncharted/scripts/detect-dependencies.sh +732 -0
- package/skills/uncharted/scripts/detect-tests.sh +553 -0
- package/skills/uncharted/scripts/discover-subsystems.sh +1029 -0
- package/skills/uncharted/scripts/enumerate-target.sh +470 -0
- package/skills/uncharted/scripts/import-source.sh +517 -0
- package/skills/uncharted/scripts/inspect-provenance.sh +573 -0
- package/skills/uncharted/scripts/resolve-segment-target.sh +640 -0
- package/skills/uncharted/scripts/run-engine.sh +655 -0
- package/skills/uncharted/scripts/validate-proposed-items.sh +125 -0
- package/skills/uncharted/scripts/write-backfilled-epics.sh +498 -0
- package/skills/wtf/SKILL.md +20 -0
- package/templates/CHANGELOG_TEMPLATE.md +13 -0
- package/templates/PROBLEM_RAPPORT_TEMPLATE.md +4 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +157 -10
- package/templates/permission-levels/README.md +73 -0
- package/templates/permission-levels/level-1-locked.json +71 -0
- package/templates/permission-levels/level-2-guarded.json +64 -0
- package/templates/permission-levels/level-3-standard.json +62 -0
- package/templates/permission-levels/level-4-elevated.json +60 -0
- package/templates/permission-levels/level-5-unrestricted.json +58 -0
- package/skills/convert/SKILL.md +0 -124
- package/skills/convert/convert_cli.py +0 -235
- package/skills/convert/tests/sample.csv +0 -4
- package/skills/convert/tests/sample.json +0 -5
- package/skills/convert/tests/sample.jsonl +0 -3
- package/skills/convert/tests/sample.yaml +0 -18
- package/skills/convert/tests/sample_obj.csv +0 -2
- package/skills/convert/tests/sample_obj.json +0 -9
- package/skills/mirror-public/SKILL.md +0 -237
- package/skills/mirror-public/assets/config.json +0 -5
- package/skills/mirror-public/scripts/mirror.sh +0 -374
- package/skills/self-sync/SKILL.md +0 -73
- package/skills/self-sync/scripts/run.js +0 -136
- package/skills/strategy/SKILL.md +0 -312
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# skills/init/scripts/detect-existing-codebase.sh
|
|
4
|
+
#
|
|
5
|
+
# Deterministic front half of the /init "detect existing project state" step.
|
|
6
|
+
# /init currently always scaffolds as though the target directory were empty.
|
|
7
|
+
# That is wrong whenever a directory already carries a Jenga scaffold, or
|
|
8
|
+
# already carries substantial application code that predates Jenga entirely
|
|
9
|
+
# -- in the second case, scaffolding fresh produces an empty board and a stub
|
|
10
|
+
# PROJECT_SUMMARY.md that actively misrepresent the project.
|
|
11
|
+
#
|
|
12
|
+
# This script answers exactly one question -- "what kind of directory is
|
|
13
|
+
# this?" -- and nothing else. It never scaffolds, never prompts, never runs
|
|
14
|
+
# /uncharted, and never modifies anything on disk. Deciding what to DO with
|
|
15
|
+
# the verdict is agent judgement and lives in skills/init/SKILL.md.
|
|
16
|
+
#
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# VERDICTS -- exactly one is printed on stdout, nothing else
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# empty The target is empty, or contains only boilerplate
|
|
21
|
+
# that does not count as "there is a project here":
|
|
22
|
+
# .git (the directory itself, pruned entirely -- not
|
|
23
|
+
# merely excluded by name), .gitignore, and top-level
|
|
24
|
+
# README*/LICENSE*/LICENCE* files (case-insensitive),
|
|
25
|
+
# plus anything git itself reports as ignored. A repo
|
|
26
|
+
# containing only a README is empty for this purpose.
|
|
27
|
+
# already-scaffolded The target already carries a Jenga scaffold: any one
|
|
28
|
+
# of project/board/, project/PROJECT_SUMMARY.md, or
|
|
29
|
+
# project/configs/workflow.json is present. Checked
|
|
30
|
+
# BEFORE the emptiness scan, so a scaffolded project
|
|
31
|
+
# that also happens to look sparse is never misreported
|
|
32
|
+
# as "empty".
|
|
33
|
+
# existing-codebase The target has real content (source files, configs,
|
|
34
|
+
# docs beyond the boilerplate list above, etc.) and is
|
|
35
|
+
# not already Jenga-scaffolded. This is the case /init
|
|
36
|
+
# currently mishandles: scaffolding fresh here silently
|
|
37
|
+
# discards the fact that pre-existing code exists.
|
|
38
|
+
#
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# NOTES ON GITIGNORE HANDLING
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# /init runs this check BEFORE `git init` in the fresh-scaffold path, so the
|
|
43
|
+
# target directory is very often not a git work tree yet. Gitignore-based
|
|
44
|
+
# filtering is therefore best-effort: it only runs when the target already
|
|
45
|
+
# has a `.git` directory AND `git` is on PATH. A standalone `.gitignore` file
|
|
46
|
+
# with no repository behind it still counts as boilerplate via the name-based
|
|
47
|
+
# exclusion below -- it just cannot hide any OTHER file, because there is no
|
|
48
|
+
# git to ask "is this ignored".
|
|
49
|
+
#
|
|
50
|
+
# README/LICENSE exclusion applies to TOP-LEVEL entries only. A directory
|
|
51
|
+
# whose only content is a buried `docs/README.md` two levels down is real
|
|
52
|
+
# structure, not boilerplate, and must not be reported as empty.
|
|
53
|
+
#
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Usage
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# detect-existing-codebase.sh [target-dir]
|
|
58
|
+
#
|
|
59
|
+
# target-dir Directory to classify. Default: current directory.
|
|
60
|
+
#
|
|
61
|
+
# Options:
|
|
62
|
+
# -h, --help Show this help and exit 0.
|
|
63
|
+
#
|
|
64
|
+
# Exit codes:
|
|
65
|
+
# 0 A verdict was printed on stdout.
|
|
66
|
+
# 1 Usage error (unknown option, more than one positional argument).
|
|
67
|
+
# 2 target-dir does not exist or is not a directory.
|
|
68
|
+
#
|
|
69
|
+
# Requires: bash, find. git is used opportunistically for gitignore
|
|
70
|
+
# filtering when the target is already a git work tree; its absence never
|
|
71
|
+
# causes a failure.
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
set -euo pipefail
|
|
75
|
+
|
|
76
|
+
SELF="$(basename "$0")"
|
|
77
|
+
|
|
78
|
+
usage() {
|
|
79
|
+
sed -n '/^# Usage$/,/^# Requires:/p' "$0" | sed -e 's/^# \{0,1\}//' -e '/^-\{10,\}$/d'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
die_usage() {
|
|
83
|
+
printf '%s: error: %s\n' "$SELF" "$*" >&2
|
|
84
|
+
echo >&2
|
|
85
|
+
usage >&2
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
TARGET_DIR="."
|
|
90
|
+
HAVE_TARGET=0
|
|
91
|
+
|
|
92
|
+
while [ "$#" -gt 0 ]; do
|
|
93
|
+
case "$1" in
|
|
94
|
+
-h|--help) usage; exit 0 ;;
|
|
95
|
+
--)
|
|
96
|
+
shift
|
|
97
|
+
[ "$#" -le 1 ] || die_usage "at most one target directory is allowed"
|
|
98
|
+
if [ "$#" -eq 1 ]; then TARGET_DIR="$1"; HAVE_TARGET=1; fi
|
|
99
|
+
shift $#
|
|
100
|
+
;;
|
|
101
|
+
-*)
|
|
102
|
+
die_usage "unknown option \"$1\"" ;;
|
|
103
|
+
*)
|
|
104
|
+
[ "$HAVE_TARGET" -eq 0 ] || die_usage "at most one target directory is allowed (got \"$TARGET_DIR\" and \"$1\")"
|
|
105
|
+
TARGET_DIR="$1"; HAVE_TARGET=1; shift ;;
|
|
106
|
+
esac
|
|
107
|
+
done
|
|
108
|
+
|
|
109
|
+
[ -d "$TARGET_DIR" ] || { printf '%s: error: not a directory: %s\n' "$SELF" "$TARGET_DIR" >&2; exit 2; }
|
|
110
|
+
|
|
111
|
+
TARGET_DIR="$(cd -- "$TARGET_DIR" && pwd -P)"
|
|
112
|
+
|
|
113
|
+
# --- already-scaffolded: checked first, independent of emptiness -----------
|
|
114
|
+
if [ -d "$TARGET_DIR/project/board" ] \
|
|
115
|
+
|| [ -f "$TARGET_DIR/project/PROJECT_SUMMARY.md" ] \
|
|
116
|
+
|| [ -f "$TARGET_DIR/project/configs/workflow.json" ]; then
|
|
117
|
+
echo "already-scaffolded"
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# --- enumerate candidate files, pruning .git entirely -----------------------
|
|
122
|
+
ALL_FILES=$(mktemp "${TMPDIR:-/tmp}/detect-existing-codebase.XXXXXX")
|
|
123
|
+
IGNORED_FILES=$(mktemp "${TMPDIR:-/tmp}/detect-existing-codebase-ignored.XXXXXX")
|
|
124
|
+
cleanup() { rm -f "$ALL_FILES" "$IGNORED_FILES"; }
|
|
125
|
+
trap cleanup EXIT
|
|
126
|
+
|
|
127
|
+
find "$TARGET_DIR" -path "$TARGET_DIR/.git" -prune -o -type f -print > "$ALL_FILES"
|
|
128
|
+
|
|
129
|
+
# --- best-effort gitignore filtering ----------------------------------------
|
|
130
|
+
: > "$IGNORED_FILES"
|
|
131
|
+
if [ -d "$TARGET_DIR/.git" ] && command -v git >/dev/null 2>&1; then
|
|
132
|
+
git -C "$TARGET_DIR" check-ignore --stdin < "$ALL_FILES" > "$IGNORED_FILES" 2>/dev/null || true
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# --- classify each candidate -------------------------------------------------
|
|
136
|
+
SOURCE_FOUND=0
|
|
137
|
+
while IFS= read -r f; do
|
|
138
|
+
[ -n "$f" ] || continue
|
|
139
|
+
|
|
140
|
+
if [ -s "$IGNORED_FILES" ] && grep -Fxq -- "$f" "$IGNORED_FILES"; then
|
|
141
|
+
continue
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
rel="${f#"$TARGET_DIR"/}"
|
|
145
|
+
base="$(basename -- "$f")"
|
|
146
|
+
dir_rel="$(dirname -- "$rel")"
|
|
147
|
+
|
|
148
|
+
if [ "$dir_rel" = "." ]; then
|
|
149
|
+
if [ "$base" = ".gitignore" ]; then
|
|
150
|
+
continue
|
|
151
|
+
fi
|
|
152
|
+
base_upper="$(printf '%s' "$base" | tr '[:lower:]' '[:upper:]')"
|
|
153
|
+
case "$base_upper" in
|
|
154
|
+
README*|LICENSE*|LICENCE*) continue ;;
|
|
155
|
+
esac
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
SOURCE_FOUND=1
|
|
159
|
+
break
|
|
160
|
+
done < "$ALL_FILES"
|
|
161
|
+
|
|
162
|
+
if [ "$SOURCE_FOUND" -eq 1 ]; then
|
|
163
|
+
echo "existing-codebase"
|
|
164
|
+
else
|
|
165
|
+
echo "empty"
|
|
166
|
+
fi
|
|
@@ -3,6 +3,25 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
5
|
ASSETS_DIR="$SCRIPT_DIR/../assets"
|
|
6
|
+
VISIBILITY_SCRIPT="$SCRIPT_DIR/apply-project-visibility.sh"
|
|
7
|
+
|
|
8
|
+
# ─── 0. Resolve project_files_visibility ─────────────────────────────────────
|
|
9
|
+
# Defaults to `visible` — the only value that touches nothing on disk — so an
|
|
10
|
+
# unattended run can never silently relocate directories or edit .gitignore.
|
|
11
|
+
VISIBILITY="${JENGA_PROJECT_FILES_VISIBILITY:-visible}"
|
|
12
|
+
|
|
13
|
+
while [[ $# -gt 0 ]]; do
|
|
14
|
+
case "$1" in
|
|
15
|
+
--visibility) VISIBILITY="${2:-}"; shift 2 ;;
|
|
16
|
+
--visibility=*) VISIBILITY="${1#*=}"; shift ;;
|
|
17
|
+
*) echo "Unknown argument: $1" >&2
|
|
18
|
+
echo "Usage: $(basename "$0") [--visibility <visible|ignored>]" >&2
|
|
19
|
+
exit 1 ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# Validate before scaffolding so a typo cannot leave a half-initialised project.
|
|
24
|
+
bash "$VISIBILITY_SCRIPT" --check-only "$VISIBILITY"
|
|
6
25
|
|
|
7
26
|
# ─── 1. Initialize git repository ────────────────────────────────────────────
|
|
8
27
|
echo "→ Initializing git repository..."
|
|
@@ -44,7 +63,17 @@ echo "→ Creating docs/STRATEGY.md (strategic brief for investors, partners, an
|
|
|
44
63
|
mkdir -p docs
|
|
45
64
|
cp "$ASSETS_DIR/strategy_stub_template.md" docs/STRATEGY.md
|
|
46
65
|
|
|
47
|
-
# ─── 10.
|
|
66
|
+
# ─── 10. Create CHANGELOG.md ──────────────────────────────────────────────────
|
|
67
|
+
echo "→ Creating CHANGELOG.md from template..."
|
|
68
|
+
cp "$SCRIPT_DIR/../../../templates/CHANGELOG_TEMPLATE.md" CHANGELOG.md
|
|
69
|
+
|
|
70
|
+
# ─── 11. Apply project_files_visibility ──────────────────────────────────────
|
|
71
|
+
# Runs before the commit so the .gitignore entry (ignored) is captured in the
|
|
72
|
+
# initial commit.
|
|
73
|
+
echo "→ Applying project files visibility ($VISIBILITY)..."
|
|
74
|
+
bash "$VISIBILITY_SCRIPT" "$VISIBILITY" "$PWD"
|
|
75
|
+
|
|
76
|
+
# ─── 12. Initial commit ──────────────────────────────────────────────────────
|
|
48
77
|
echo "→ Staging and committing scaffolded files..."
|
|
49
78
|
git add -A
|
|
50
79
|
git commit -m "init: scaffold project structure and workflow config"
|
package/skills/jenga/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jenga
|
|
3
|
-
description:
|
|
3
|
+
description: Interactive-by-default board orchestrator with a fully automated escape hatch. Bare `/jenga` renders a picker and confirmation tree before scoping the run; `/jenga <ids>` resolves an explicit fuzzy-ID scope and confirms it; `/jenga *` reproduces the original zero-prompt behavior — decomposing any unbroken Epics into Stories, any unbroken Stories into Tasks, queuing all unqueued Tasks into todo.md, then executing every eligible item with no user prompts — until the board is fully started.
|
|
4
4
|
keywords:
|
|
5
5
|
- jenga
|
|
6
6
|
- orchestrate
|
|
@@ -18,7 +18,13 @@ metadata:
|
|
|
18
18
|
|
|
19
19
|
## Purpose
|
|
20
20
|
|
|
21
|
-
`/jenga` is
|
|
21
|
+
`/jenga` is interactive by default — it never silently commits to the whole board without showing the user what it's about to run and letting them scope or edit that selection first. It has three entry modes:
|
|
22
|
+
|
|
23
|
+
- **Bare `/jenga`** (no argument) — renders a numbered picker of the full board, then an editable confirmation tree, before anything executes.
|
|
24
|
+
- **`/jenga <ids>`** (explicit comma-separated scope) — resolves the given IDs via the fuzzy-ID grammar, skipping the picker, then still shows the same editable confirmation tree before executing.
|
|
25
|
+
- **`/jenga *`** (literal wildcard) — the explicit escape hatch. Skips both the picker and the confirmation step entirely and reproduces the original hands-free "commit to everything on the board" pipeline: the entire board is fully decomposed, fully queued, and fully executing — without any user interaction, no user prompts. This is the only path where `/jenga` runs with no user prompts at all.
|
|
26
|
+
|
|
27
|
+
Once a run's scope is established (by confirmation, or unconditionally under `*`), `/jenga` runs the same underlying phases against that scope: **entry mode resolution → decompose → queue → execute → loop**.
|
|
22
28
|
|
|
23
29
|
## Instructions
|
|
24
30
|
|
|
@@ -46,36 +52,165 @@ Extract the following named values for use throughout this skill:
|
|
|
46
52
|
|
|
47
53
|
These values must be read fresh on each invocation. Never use hardcoded fallbacks.
|
|
48
54
|
|
|
55
|
+
### Phase 0.5 — Pre-flight Validation
|
|
56
|
+
|
|
57
|
+
Before accepting any task for decomposition or execution, the executing agent must validate the task's scope fields. The threshold values loaded in Phase 0 may be referenced in error messages for context, but are not required for the core validation rules below.
|
|
58
|
+
|
|
59
|
+
For each task read from the board, apply the following checks in order:
|
|
60
|
+
|
|
61
|
+
#### Rule 1 — Valid execution_scope value
|
|
62
|
+
|
|
63
|
+
If the task frontmatter contains an `execution_scope` field, its value must be one of: `task`, `story`, `epic`, `inline`.
|
|
64
|
+
|
|
65
|
+
If the value is anything else, halt immediately with:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
VALIDATION ERROR [<task_id>]: execution_scope "<value>" is not a valid scope. Allowed: task, story, epic, inline.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Do not proceed with this task.
|
|
72
|
+
|
|
73
|
+
#### Rule 2 — scope_rationale must contain a measurable claim
|
|
74
|
+
|
|
75
|
+
If `execution_scope` is present, `scope_rationale` must also be present and must contain at least one digit (0–9) or the word "file" (case-insensitive).
|
|
76
|
+
|
|
77
|
+
If `scope_rationale` is absent, or present but contains no digit and does not contain the word "file", halt with:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
VALIDATION ERROR [<task_id>]: scope_rationale is missing or lacks a numeric/file-count claim. Provide a rationale that includes a digit (e.g. "touches 2 files") or the word "file".
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Do not proceed with this task.
|
|
84
|
+
|
|
85
|
+
#### Rule 3 — epic scope requires explicit human approval
|
|
86
|
+
|
|
87
|
+
If `execution_scope` is `"epic"`, the task must also have `epic_scope_approval: true` set explicitly in its frontmatter. A missing `epic_scope_approval` field and a value of `false` are both rejection conditions.
|
|
88
|
+
|
|
89
|
+
If `epic_scope_approval` is absent or is not exactly `true`, halt with:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
VALIDATION ERROR [<task_id>]: execution_scope=epic requires epic_scope_approval: true (set by human operator). This field must be added manually — it is never assigned autonomously.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Do not proceed with this task.
|
|
96
|
+
|
|
97
|
+
#### Rule 4 — crucial_level: locked forces execution_scope: inline
|
|
98
|
+
|
|
99
|
+
If the task frontmatter contains `crucial_level: locked` (per `templates/SCRUM_BOARD_SCHEMA.md`'s Crucial Flag Fields), `execution_scope` for that task MUST be `inline` — only the current foreground/inline session can pause mid-run for a live confirmation; a backgrounded subagent has no live channel back to the user.
|
|
100
|
+
|
|
101
|
+
This rule **auto-corrects and continues**; unlike Rules 1-3, it never halts.
|
|
102
|
+
|
|
103
|
+
- If `execution_scope` is present and its value is anything other than `inline`, correct it to `inline` directly in the task file, and record a logged note of the correction by appending to that task's `override_justification` frontmatter field (the auditable mechanism for this rule — do not use `events.json` or any other location) a line of the form:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
override_justification: "Rule 4 auto-correction <date>: execution_scope forced from '<previous_value>' to 'inline' because crucial_level: locked."
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then emit (non-fatally — do not halt):
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
AUTO-CORRECTION [<task_id>]: crucial_level=locked requires execution_scope=inline; corrected from "<previous_value>" to "inline".
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- If `execution_scope` is absent entirely, set it to `inline` directly in the task frontmatter. Do **not** fall through to the Backward-compatibility default of `execution_scope: task` documented immediately below — a `locked` item overrides that default even when no other execution-scope fields are present. No `override_justification` note is required in this case, since there is no prior value being overridden.
|
|
116
|
+
|
|
117
|
+
Proceed to the next rule (or the next phase, if this was the last applicable rule) after applying the correction.
|
|
118
|
+
|
|
119
|
+
#### Backward compatibility — legacy tasks
|
|
120
|
+
|
|
121
|
+
If the task frontmatter contains **none** of `execution_scope`, `scope_rationale`, `needs_docs`, `jenga_assigned`, or `override_justification`, treat the task as a legacy task:
|
|
122
|
+
|
|
123
|
+
- Assume `execution_scope: task`
|
|
124
|
+
- Assume `needs_docs: true`
|
|
125
|
+
- Skip all three rules above and proceed without error.
|
|
126
|
+
|
|
127
|
+
#### Validation success
|
|
128
|
+
|
|
129
|
+
If all applicable rules pass (or the task is a legacy task), proceed to the next phase for that task without any error output.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Phase 0.75 — Entry Mode Resolution
|
|
134
|
+
|
|
135
|
+
This phase determines **how `/jenga` was invoked** and, for two of the three entry modes, produces a **scoped set** — a confirmed list of board IDs (epics/stories/tasks) that Phases 1-4 must restrict themselves to. All board scanning, ID parsing, cascade expansion, and rendering used by this phase already live in `skills/jenga/scripts/` per this repo's "Scripts Over Inline Logic" principle — this phase never re-implements any of that logic inline. The executing agent's job here is limited to: invoking the right script with the right arguments, relaying its STDOUT verbatim to the user when the contract calls for that, capturing the `STATE_FILE:` line from STDERR for the next turn, and forwarding the user's raw reply back into the next invocation unmodified.
|
|
136
|
+
|
|
137
|
+
**Determine the invocation form** from the raw argument (if any) passed to `/jenga`:
|
|
138
|
+
|
|
139
|
+
- No argument at all → **bare branch**.
|
|
140
|
+
- The argument is the literal string `*` → **wildcard branch**.
|
|
141
|
+
- Any other non-empty argument → **scoped branch** (treat the whole argument as the comma-separated raw ID list).
|
|
142
|
+
|
|
143
|
+
#### Wildcard branch (`/jenga *`)
|
|
144
|
+
|
|
145
|
+
Skip both the picker and the confirmation step entirely. There is no scoped set — proceed to Phase 1 unrestricted, exactly as `/jenga` behaved before this phase existed.
|
|
146
|
+
|
|
147
|
+
#### Bare branch (`/jenga`)
|
|
148
|
+
|
|
149
|
+
1. Invoke `skills/jenga/scripts/render-picker.sh` with no arguments (start mode). Relay its STDOUT (the numbered checklist) to the user verbatim — no summarizing, no reformatting. Capture the `STATE_FILE:` path from STDERR.
|
|
150
|
+
2. Wait for the user's chat reply, then invoke `skills/jenga/scripts/render-picker.sh <state_file> "<raw_reply>"` (continue mode).
|
|
151
|
+
- **Error turn** (plain text on STDOUT, state file retained) — relay verbatim and return to step 2 for another reply.
|
|
152
|
+
- **Cancellation** — relay the cancellation acknowledgement and halt the entire `/jenga` run; do not proceed to any later phase.
|
|
153
|
+
- **Resolved** (JSON object on STDOUT, state file removed) — extract `resolved_ids_csv` and continue to the shared confirmation step below.
|
|
154
|
+
|
|
155
|
+
#### Scoped branch (`/jenga <ids>`)
|
|
156
|
+
|
|
157
|
+
1. Invoke `skills/jenga/scripts/resolve-id.sh "<raw argument>"` directly — the picker is skipped entirely in this branch.
|
|
158
|
+
2. Parse the JSON array response, one object per comma-delimited input segment.
|
|
159
|
+
- If **every** segment has `status: "resolved"`, collect their `resolved_id` values into a comma-separated list and continue to the shared confirmation step below.
|
|
160
|
+
- If **any** segment has `status: "rejected"`, halt this phase (do not proceed to confirmation or Phase 1) and report each rejected segment's `input` and `reason` to the user verbatim, per `resolve-id.sh`'s own contract — a partial or ambiguous ID is never guessed. The user must re-invoke `/jenga <ids>` with corrected input.
|
|
161
|
+
|
|
162
|
+
#### Shared confirmation step (bare and scoped branches only)
|
|
163
|
+
|
|
164
|
+
1. Invoke `skills/jenga/scripts/render-confirmation.sh "<comma-separated resolved ids from whichever branch above>"` (start mode). Relay STDOUT (the confirmation tree) to the user verbatim. Capture the `STATE_FILE:` path from STDERR.
|
|
165
|
+
2. Wait for the user's chat reply, then invoke `skills/jenga/scripts/render-confirmation.sh <state_file> "<raw_reply>"` (continue mode).
|
|
166
|
+
- **Toggle or error turn** (plain text on STDOUT, state file retained) — relay verbatim and return to step 2 for another reply.
|
|
167
|
+
- **Cancellation** — relay the cancellation acknowledgement and halt the entire `/jenga` run; do not proceed to any later phase.
|
|
168
|
+
- **Confirmed** (JSON object on STDOUT, state file removed) — this is the final **scoped set**. Take `resolved_ids` (or `resolved_ids_csv`) as the exact set of board IDs Phases 1-4 restrict themselves to for the rest of this run.
|
|
169
|
+
3. **Handoff to cascade resolution** — do not invoke `cascade-resolve.sh` again here. `render-confirmation.sh` already invoked it internally to build the tree, and the CONFIRMED JSON's own `undecomposed` field is that same result already scoped down to the checked-only set. Use that `undecomposed` field directly to identify which epics/stories in the scoped set still need Phase 1/2 decomposition.
|
|
170
|
+
|
|
171
|
+
After this phase completes (bare and scoped branches via confirmation, wildcard branch immediately), proceed to Phase 1.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
49
175
|
### Phase 1 — Decompose Epics into Stories
|
|
50
176
|
|
|
51
|
-
|
|
177
|
+
If Phase 0.75 produced a scoped set, restrict this phase to epics that are members of that set (directly selected, or flagged in its `undecomposed` list). Under `/jenga *`, this phase is unrestricted, exactly as before.
|
|
178
|
+
|
|
179
|
+
Read all files in `project/board/epics/`. For each in-scope 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.
|
|
52
180
|
|
|
53
|
-
Repeat until every Epic has at least one Story on the board.
|
|
181
|
+
Repeat until every in-scope Epic has at least one Story on the board.
|
|
54
182
|
|
|
55
183
|
### Phase 2 — Decompose Stories into Tasks
|
|
56
184
|
|
|
57
|
-
|
|
185
|
+
If Phase 0.75 produced a scoped set, restrict this phase to stories that are members of that set (directly selected, expanded from an in-scope epic, or flagged in its `undecomposed` list). Under `/jenga *`, this phase is unrestricted, exactly as before.
|
|
58
186
|
|
|
59
|
-
|
|
187
|
+
Read all files in `project/board/stories/`. For each in-scope Story that has no corresponding task files in `project/board/tasks/` (i.e. no files whose name starts with that Story's ID), invoke `/do` via a **scrum-master sub-agent** to break it down into Tasks.
|
|
188
|
+
|
|
189
|
+
Repeat until every in-scope Story has at least one Task on the board.
|
|
60
190
|
|
|
61
191
|
### Phase 3 — Queue all Tasks into `todo.md`
|
|
62
192
|
|
|
63
|
-
|
|
193
|
+
If Phase 0.75 produced a scoped set, restrict this phase to tasks that are members of that set (directly selected, or expanded from an in-scope epic/story). Under `/jenga *`, this phase is unrestricted, exactly as before.
|
|
194
|
+
|
|
195
|
+
Read all files in `project/board/tasks/`. For every in-scope Task not already listed in `project/todo.md`, append its ID (and title as a comment) to `project/todo.md`.
|
|
64
196
|
|
|
65
|
-
After this phase, `todo.md` reflects the full set of work
|
|
197
|
+
After this phase, `todo.md` reflects the full set of in-scope work (or the full board, under `*`).
|
|
66
198
|
|
|
67
199
|
### Phase 3.5 — Story-bundle detection
|
|
68
200
|
|
|
69
201
|
Before dispatching individual tasks in Phase 4, check each story for bundle eligibility. This phase runs once after Phase 3 completes.
|
|
70
202
|
|
|
71
|
-
|
|
203
|
+
If Phase 0.75 produced a scoped set, restrict this phase to stories that are members of that set (directly selected, or expanded from an in-scope epic) — a story with tasks sitting in `todo.md` from an earlier, differently-scoped run but that is **not** a member of the current run's scoped set is skipped entirely by this phase (not considered for bundling, and not dispatched via the bundle path) so that Phase 4's own scoped-set exclusion is never bypassed by a bundle call issued here. Under `/jenga *`, this phase is unrestricted, exactly as before.
|
|
204
|
+
|
|
205
|
+
For each in-scope story that has one or more tasks listed in `todo.md`:
|
|
72
206
|
|
|
73
207
|
1. **Read the story file** — parse the `tasks:` frontmatter array to get the ordered list of task IDs.
|
|
74
208
|
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
209
|
3. **Read each task file** — for every task ID in the `tasks:` list, read the corresponding task file from `project/board/tasks/`.
|
|
76
210
|
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. **
|
|
78
|
-
6. **
|
|
211
|
+
5. **Guard: locked-task disqualifier (defense-in-depth)** — for each task file already read in step 3, also read `crucial_level` (per `templates/SCRUM_BOARD_SCHEMA.md`'s Crucial Flag Fields). If **any** task in the story's `tasks:` list has `crucial_level: locked`, this story is **not** eligible for the bundle path — skip to per-task dispatch in Phase 4 for this story, **regardless of that task's `execution_scope` value**, even if it already reads `inline`. This check is defense-in-depth alongside Phase 0.5's Rule 4 (which forces a locked task's own `execution_scope` to `inline` when Rule 4 processes it): it exists for the race window where Rule 4 hasn't (yet) corrected the task — e.g. the task was added to the story's `tasks:` list after Rule 4 last ran, or the file was edited by hand after validation. It is not a replacement for Rule 4.
|
|
212
|
+
6. **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.
|
|
213
|
+
7. **Route bundle candidates** — if all tasks in the story are `execution_scope: story` and the list is non-empty:
|
|
79
214
|
a. Emit:
|
|
80
215
|
```
|
|
81
216
|
BUNDLE DETECTED: story <E##_S##> — <N> story-scoped tasks will execute as a bundle.
|
|
@@ -83,17 +218,19 @@ For each story that has one or more tasks listed in `todo.md`:
|
|
|
83
218
|
where `<E##_S##>` is the story ID and `<N>` is the count of tasks in the list.
|
|
84
219
|
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
220
|
c. **Mark these tasks as bundled** — record their task IDs so Phase 4 skips individual dispatch for them.
|
|
86
|
-
|
|
221
|
+
8. **Non-bundle stories** — stories with a mixed scope, a zero-length task list, any task missing `execution_scope: story`, or any task with `crucial_level: locked` (step 5) use the normal per-task dispatch in Phase 4 without any change.
|
|
87
222
|
|
|
88
223
|
### Phase 4 — Execute
|
|
89
224
|
|
|
90
225
|
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.
|
|
91
226
|
|
|
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 `
|
|
227
|
+
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**. If Phase 0.75 produced a scoped set, also exclude any item not a member of that set — execution never runs outside the confirmed/resolved scope. Under `/jenga *`, no such exclusion applies. A dependency is resolved if the blocking item's status is at least `In Progress` or `Passed`.
|
|
93
228
|
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.
|
|
94
229
|
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.
|
|
95
|
-
4. **Mark
|
|
96
|
-
5. **Wait and loop** — once all active background agents
|
|
230
|
+
4. **Mark In Progress** — update `status: In Progress` in each launched item's board file (YAML front-matter) immediately after launch.
|
|
231
|
+
5. **Wait, drain, and loop** — once all active background agents in the wave have completed:
|
|
232
|
+
a. **Drain the scrum triggers queue** — invoke the `## Drain Scrum Triggers Queue` procedure from `agents/scrum-master.md` against `project/queue/scrum_triggers.jsonl`. `/jenga`'s orchestrating agent is the scrum-master, and this is the same session-start procedure applied mid-run: process any `rapport_review`, `status_review`, and `story_rollup` triggers written by the tester sub-sessions that just completed, then clear the file. This ensures rollups become visible on the board (story/epic status updates) before the next wave is collected, instead of sitting unprocessed until some future scrum-master session start.
|
|
233
|
+
b. **Return to step 1** of this phase to pick up any newly unblocked items — including items unblocked by the rollups just processed in (a).
|
|
97
234
|
|
|
98
235
|
### Exit condition
|
|
99
236
|
|
|
@@ -108,10 +245,16 @@ When no eligible candidates remain in Phase 4, exit and output:
|
|
|
108
245
|
- **Epic with no stories after breakdown** — log a warning and continue to the next Epic; do not block the pipeline.
|
|
109
246
|
- **Story with no tasks after breakdown** — log a warning and continue to the next Story.
|
|
110
247
|
- **Task already in `todo.md`** — skip; do not duplicate.
|
|
111
|
-
- **All tasks in `todo.md` already
|
|
112
|
-
- **Unresolved dependencies** — item is skipped in Phase 4 until its blockers are at least `
|
|
248
|
+
- **All tasks in `todo.md` already In Progress/Passed** — exits cleanly with the completion message.
|
|
249
|
+
- **Unresolved dependencies** — item is skipped in Phase 4 until its blockers are at least `In Progress`.
|
|
113
250
|
- **`/do` failure (background agent)** — treated as a skip; mark the item's status back to `Pending` and continue the loop with remaining candidates.
|
|
114
251
|
- **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
252
|
- **Story with mixed `execution_scope` values** — falls back entirely to per-task dispatch in Phase 4; no partial bundling occurs.
|
|
116
253
|
- **Task file missing `execution_scope` field** — treated as not story-scoped; the containing story is disqualified from the bundle path.
|
|
254
|
+
- **Story containing a `crucial_level: locked` task** — disqualified from the bundle path at Phase 3.5 step 5, independent of that task's `execution_scope`; falls back to per-task dispatch in Phase 4, where `/do` Section 4.2's locked-task dispatch guard (E39_S03_T04) provides the second enforcement layer before any worktree or subagent is created.
|
|
117
255
|
- **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.
|
|
256
|
+
- **Picker cancelled (bare branch)** — the entire `/jenga` run halts immediately after relaying the cancellation acknowledgement; no phase past 0.75 runs, and nothing on the board is modified.
|
|
257
|
+
- **Confirmation cancelled (bare or scoped branch)** — same as picker cancellation: the entire `/jenga` run halts immediately; no scoped set is produced and no later phase runs.
|
|
258
|
+
- **`resolve-id.sh` rejects one or more segments (scoped branch)** — the whole invocation halts at Phase 0.75 with the rejected segments' reasons reported verbatim; no partial scope is assembled from the segments that did resolve, and no fallback guess is made for the rejected ones. The user must re-invoke `/jenga <ids>` with corrected input.
|
|
259
|
+
- **`/jenga *` (wildcard branch)** — never produces a scoped set; Phases 1-4 run fully unrestricted over the entire board, identical to `/jenga`'s behavior before Phase 0.75 existed.
|
|
260
|
+
- **Stale out-of-scope story queued in `todo.md` from an earlier run (scoped run only)** — Phase 3.5's scoped-set guard skips it entirely (not considered for bundling), so it cannot be dispatched via a bundle `/do <E##_S##>` call that would otherwise bypass Phase 4's own scoped-set exclusion; it remains untouched in `todo.md` until a future run's scope includes it.
|