@jenga-ai/agent 3.5.0 → 3.6.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.
Files changed (94) hide show
  1. package/README.md +85 -78
  2. package/agents/developer.md +1 -1
  3. package/agents/scrum-master.md +20 -2
  4. package/agents/tester.md +3 -3
  5. package/hooks/on_session_end.sh +5 -5
  6. package/lib/generate-agent-context.js +2 -2
  7. package/lib/generate-copilot-hooks.js +1 -1
  8. package/lib/generate-skill-allow-list.js +37 -3
  9. package/lib/mirror.js +1 -1
  10. package/lib/postinstall-manifest.js +1 -1
  11. package/lib/skill-allow-list.json +2 -2
  12. package/mcp/help/index.js +8 -17
  13. package/mcp/help/scan.js +73 -0
  14. package/package.json +6 -1
  15. package/project/app/api/parsers/knowledge-graph.js +100 -9
  16. package/project/app/api/routes/health.js +36 -0
  17. package/project/app/api/scripts/capture-snapshot.js +9 -6
  18. package/project/app/ui/dist/assets/{index-CdK3Qrep.css → index-BVR_7Owg.css} +1 -1
  19. package/project/app/ui/dist/assets/index-CtU2xLQm.js +104 -0
  20. package/project/app/ui/dist/index.html +2 -2
  21. package/project/app/ui/package.json +4 -0
  22. package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
  23. package/scripts/acquire-concurrency-slot.sh +1 -1
  24. package/scripts/apply-j-prefix.sh +46 -5
  25. package/scripts/audit-twin-divergence.sh +73 -5
  26. package/scripts/build-pages-site.sh +1 -1
  27. package/scripts/check-public-playbook-steps.sh +158 -52
  28. package/scripts/check-publicignore-match.sh +2 -2
  29. package/scripts/compute-deploy-reconcile.sh +5 -5
  30. package/scripts/delete-bare-skill-dirs.sh +330 -0
  31. package/scripts/generate-legacy-shipped-paths.js +2 -2
  32. package/scripts/idea_manager.sh +258 -3
  33. package/scripts/mark-deployed.sh +2 -2
  34. package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
  35. package/scripts/populate-knowledge-graph.js +213 -5
  36. package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
  37. package/scripts/postinstall.js +1 -1
  38. package/scripts/repoint-skill-refs.sh +539 -0
  39. package/scripts/todo_manager.sh +1 -1
  40. package/scripts/verify-legacy-seed-reconcile.sh +10 -10
  41. package/scripts/verify-postinstall-reconcile.sh +7 -7
  42. package/scripts/write-context-digest.sh +1 -1
  43. package/skills/j-clearify/SKILL.md +2 -2
  44. package/skills/j-close-story/scripts/check-privatized.sh +4 -4
  45. package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
  46. package/skills/j-do/SKILL.md +101 -17
  47. package/skills/j-doc-sync/SKILL.md +1 -0
  48. package/skills/j-gitignore/SKILL.md +157 -0
  49. package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
  50. package/skills/j-gitignore/scripts/_catalog.sh +105 -0
  51. package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
  52. package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
  53. package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
  54. package/skills/j-idea/SKILL.md +78 -6
  55. package/skills/j-idea/assets/idea_template.md +1 -1
  56. package/skills/j-improve/SKILL.md +1 -1
  57. package/skills/j-init/SKILL.md +3 -3
  58. package/skills/j-init/assets/scope-thresholds_template.json +5 -2
  59. package/skills/j-init/scripts/apply-scaffold-visibility.sh +1 -1
  60. package/skills/j-init/scripts/init.sh +4 -4
  61. package/skills/j-playbook/SKILL.md +1 -1
  62. package/skills/j-publish/SKILL.md +1 -1
  63. package/skills/j-publish/adapters/npm-ci.md +6 -1
  64. package/skills/j-publish/adapters/npm.md +1 -1
  65. package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
  66. package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
  67. package/skills/j-reconcile/SKILL.md +2 -2
  68. package/skills/j-reconcile/scripts/detect-unlinked-code.sh +11 -11
  69. package/skills/j-redo/SKILL.md +1 -1
  70. package/skills/j-spinoff/SKILL.md +1 -1
  71. package/skills/j-status/SKILL.md +15 -0
  72. package/skills/j-todo/SKILL.md +3 -1
  73. package/skills/j-uncharted/SKILL.md +1 -1
  74. package/skills/j-uncharted/scripts/detect-dependencies.sh +1 -1
  75. package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
  76. package/skills/j-uncharted/scripts/elicitation-state.sh +1 -1
  77. package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
  78. package/skills/j-wtf/SKILL.md +1 -1
  79. package/skills/jenga/SKILL.md +43 -9
  80. package/skills/jenga/playbooks/board-hygiene.json +32 -0
  81. package/skills/jenga/playbooks/schema.json +73 -6
  82. package/skills/jenga/playbooks/understand-then-commit.json +19 -0
  83. package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
  84. package/skills/jenga/scripts/load-playbooks.sh +1 -1
  85. package/skills/jenga/scripts/match-playbook.sh +1 -1
  86. package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
  87. package/templates/SKILL_TEMPLATE.md +12 -0
  88. package/templates/permission-levels/level-4-elevated.json +1 -1
  89. package/templates/permission-levels/level-5-unrestricted.json +1 -1
  90. package/templates/playbook-types.json +34 -6
  91. package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
  92. package/scripts/generate-j-alias.sh +0 -333
  93. package/skills/j-dev-done/SKILL.md +0 -53
  94. package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
@@ -37,7 +37,7 @@
37
37
  # SIGNAL A IS BORROWED, NOT REBUILT
38
38
  # ---------------------------------------------------------------------------
39
39
  # The board-linkage question is answered by
40
- # `skills/uncharted/scripts/resolve-segment-target.sh` (E40_S02_T01) through its
40
+ # `skills/j-uncharted/scripts/resolve-segment-target.sh` (E40_S02_T01) through its
41
41
  # documented batch interface:
42
42
  #
43
43
  # git ls-files | resolve-segment-target.sh --paths-from -
@@ -70,7 +70,7 @@
70
70
  #
71
71
  # This is deliberate. `run-engine.sh` currently renders the out-of-repo
72
72
  # `not_checked` case as `unlinked` in its understanding document, which asserts
73
- # a verified absence that was never verified. `skills/uncharted/SKILL.md` names
73
+ # a verified absence that was never verified. `skills/j-uncharted/SKILL.md` names
74
74
  # Step 1 (`resolve-segment-target.sh`) authoritative on linkage where the two
75
75
  # disagree, so this script passes the resolver's status through unchanged and
76
76
  # does not repeat that conflation.
@@ -104,8 +104,8 @@
104
104
  # Classifying files and then reporting directories would assert about a
105
105
  # directory something that was only ever verified about its contents. Because
106
106
  # the resolver's match is a path-boundary test, a board item naming
107
- # `skills/convert/` links THAT DIRECTORY without linking
108
- # `skills/convert/convert_cli.py`. Both facts are true, and the directory-level
107
+ # `skills/j-convert/` links THAT DIRECTORY without linking
108
+ # `skills/j-convert/convert_cli.py`. Both facts are true, and the directory-level
109
109
  # one is what decides whether a segment is worth investigating -- offering
110
110
  # `/uncharted segment` there would duplicate a board item that already exists.
111
111
  #
@@ -125,7 +125,7 @@
125
125
  # MIRROR SPELLINGS. Per CLAUDE.md the canonical file lives in the root tree and
126
126
  # `.agents/`, `.claude/` are generated build outputs -- but older board items
127
127
  # were often written against the mirror path. E17_S05 owns `/reconcile-origin`
128
- # and names it `.agents/skills/reconcile-origin/SKILL.md`, so a match on the
128
+ # and names it `.agents/skills/j-reconcile-origin/SKILL.md`, so a match on the
129
129
  # root path alone misses a board item that plainly owns the directory. Each
130
130
  # group directory is therefore asked about under its own name and under both
131
131
  # mirror prefixes, and `directory_linkage.matched_as` records which spelling
@@ -187,14 +187,14 @@
187
187
  # "not_checked_paths": N,
188
188
  # "groups": N, "covered_groups": N },
189
189
  # "groups": [
190
- # { "directory": "skills/skillify",
190
+ # { "directory": "skills/j-skillify",
191
191
  # "unlinked_count": N, // files keyed to THIS group
192
192
  # "subtree_candidates": N, // all candidates under the directory
193
193
  # "subtree_unlinked": N, // all unlinked under the directory
194
194
  # "fully_unlinked": true,
195
195
  # "directory_linkage": { "status": "unlinked", "reason": "...",
196
196
  # "items": [], "match_count": 0,
197
- # "matched_as": "skills/skillify" },
197
+ # "matched_as": "skills/j-skillify" },
198
198
  # "files": ["..."], // capped by --limit
199
199
  # "files_truncated": N }
200
200
  # ],
@@ -295,7 +295,7 @@ REPO_ROOT=$(cd -- "$REPO_ROOT" && pwd -P)
295
295
  [ -n "$RESOLVER" ] || RESOLVER="$SCRIPT_DIR/../../uncharted/scripts/resolve-segment-target.sh"
296
296
  [ -f "$RESOLVER" ] || die 4 "board-linkage checker not found: $RESOLVER
297
297
  This script deliberately has no fallback implementation -- see the header. Restore
298
- skills/uncharted/scripts/resolve-segment-target.sh or pass --resolver <path>."
298
+ skills/j-uncharted/scripts/resolve-segment-target.sh or pass --resolver <path>."
299
299
  [ -x "$RESOLVER" ] || die 4 "board-linkage checker is not executable: $RESOLVER"
300
300
  RESOLVER=$(cd -- "$(dirname -- "$RESOLVER")" && pwd -P)/$(basename -- "$RESOLVER")
301
301
 
@@ -572,8 +572,8 @@ for path, status in sorted(status_of.items()):
572
572
  # --- second pass: is the GROUP DIRECTORY itself on the board? ---------------------------------
573
573
  # Reporting a directory while only ever having checked the files inside it asserts an absence
574
574
  # that was never verified -- the same error this script is careful to avoid for `not_checked`.
575
- # The resolver's match is a path-boundary test, so a board item naming `skills/convert/` links
576
- # that directory without linking `skills/convert/convert_cli.py`. Both facts are true and the
575
+ # The resolver's match is a path-boundary test, so a board item naming `skills/j-convert/` links
576
+ # that directory without linking `skills/j-convert/convert_cli.py`. Both facts are true and the
577
577
  # directory-level one is the one that decides whether a segment is worth investigating.
578
578
  #
579
579
  # Same borrowed checker, same batch interface, one extra call. No new linkage logic -- the only
@@ -582,7 +582,7 @@ for path, status in sorted(status_of.items()):
582
582
  # MIRROR SPELLINGS. Per CLAUDE.md the canonical file lives in the root tree and `.agents/` and
583
583
  # `.claude/` are generated build outputs, but plenty of older board items were written against
584
584
  # the mirror path -- E17_S05 owns `/reconcile-origin` and names it as
585
- # `.agents/skills/reconcile-origin/SKILL.md`. A boundary match on the root path alone therefore
585
+ # `.agents/skills/j-reconcile-origin/SKILL.md`. A boundary match on the root path alone therefore
586
586
  # misses a board item that plainly owns the directory. So each directory is asked about under its
587
587
  # own name and under both mirror prefixes, and a hit on any spelling is board provenance. This
588
588
  # adds path spellings to the QUESTION; it does not add a second answer to it.
@@ -60,7 +60,7 @@ Compare the user's redo description against the original implementation to deter
60
60
  Summarise scope findings to the user in a brief list before continuing.
61
61
 
62
62
  ### 3. Plan the redo
63
- Populate `skills/todo/assets/todo_handoff_template.md` with the following pre-collected context:
63
+ Populate `skills/j-todo/assets/todo_handoff_template.md` with the following pre-collected context:
64
64
  - **Mission title**: a short name for the redo work
65
65
  - **Goal / objective**: the redo objective — what is changing and the desired outcome
66
66
  - **Affected files or scope**: code, tests, and documentation files identified in step 2
@@ -44,7 +44,7 @@ This file is generated/synced by `scripts/generate-j-alias.sh spinoff` from `ski
44
44
 
45
45
  4. **Run /brainstorm (if chosen)** — Invoke the `/brainstorm` skill, passing the diverging topic and collected context as the opening prompt. After `/brainstorm` completes, use the refined output as the idea description.
46
46
 
47
- 5. **Save via `/idea`** — Populate `skills/idea/assets/idea_handoff_template.md` with the context collected so far:
47
+ 5. **Save via `/idea`** — Populate `skills/j-idea/assets/idea_handoff_template.md` with the context collected so far:
48
48
  - **Mission title**: the diverging topic name (as confirmed in step 1)
49
49
  - **Goal / objective**: what the diverging topic aims to achieve
50
50
  - **Affected files or scope**: any files or modules identified during the conversation
@@ -36,6 +36,21 @@ This file is generated/synced by `scripts/generate-j-alias.sh status` from `skil
36
36
 
37
37
  6. **Check the queue** — If `project/queue/scrum_triggers.jsonl` is non-empty, note the number of pending triggers awaiting the scrum master.
38
38
 
39
+ 6.5. **Run the deploy-reconcile pass** (`E51_S05`) before printing the summary — `/status` has no
40
+ `scripts/` directory of its own comparable to `/self-sync`'s, so invoke the shared pipeline
41
+ directly rather than adding a third skill-local wrapper script:
42
+ ```
43
+ bash scripts/mark-deployed.sh
44
+ ```
45
+ This defaults to invoking its own sibling `scripts/compute-deploy-reconcile.sh`, which
46
+ discovers any not-yet-reconciled `vX.Y.Z-stage`/`vX.Y.Z` tags on the public `jenga-npm` repo
47
+ (unauthenticated read — no credential required) and promotes matching `Publicized`/
48
+ `Deployed to Stage` tickets to `Deployed to Stage`/`Deployed to Prod` (writing
49
+ `date_deployed_prod` on a Prod promotion) by commit ancestry, before this skill re-scans the
50
+ board in steps 2-4 above. This step is **non-fatal**: a failure anywhere in the pipeline
51
+ (including an unreachable public repo) is logged as a warning only and never prevents `/status`
52
+ from printing whatever board state it already has, and never causes a non-zero exit.
53
+
39
54
  7. **Print the summary** following the layout and icon conventions in `assets/output_format.md`.
40
55
 
41
56
  8. If no epics exist, print: `No board items found. Run /pi-plan to define epics or /todo to add items.`
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: j.todo
3
3
  description: Polyfill alias of the todo skill under a collision-safe directory name. Identical behavior to /todo — Add missions to the project todo list (project/todo.md), optionally linking them to epics and stories. Loops until the user is done, then optionally executes the list. Use when the bare /todo form is shadowed by another tool's own built-in command of the same name.
4
+ output_types: id_list
5
+ input_types: id_list
4
6
  keywords:
5
7
  - todo
6
8
  - add task
@@ -29,7 +31,7 @@ This file is generated/synced by `scripts/generate-j-alias.sh todo` from `skills
29
31
 
30
32
  When `--trivial` is present, the mission is written as a **fully-formed task board file immediately** (not just a raw `todo.md` line deferred to `/do`'s own breakdown pass) with `execution_scope: inline` forced unconditionally — no threshold computation is consulted for the scope value itself. See step 4.5 below for the mechanics.
31
33
 
32
- **Human-only override.** `--trivial` is invoked by a human typing `/todo --trivial ...` — it is never applied by the scrum-master to itself during autonomous story/epic breakdown elsewhere (e.g. `/jenga`'s Phase 0.5, or `/do`'s own scrum-master decomposition step in `skills/do/SKILL.md` step 3). Those paths keep using the normal heuristic-only `execution_scope` assignment documented in `agents/scrum-master.md`'s Execution Scope Assignment section, unmodified by this flag.
34
+ **Human-only override.** `--trivial` is invoked by a human typing `/todo --trivial ...` — it is never applied by the scrum-master to itself during autonomous story/epic breakdown elsewhere (e.g. `/jenga`'s Phase 0.5, or `/do`'s own scrum-master decomposition step in `skills/j-do/SKILL.md` step 3). Those paths keep using the normal heuristic-only `execution_scope` assignment documented in `agents/scrum-master.md`'s Execution Scope Assignment section, unmodified by this flag.
33
35
 
34
36
  **Fallback on failure is out of scope here.** If a `--trivial`-forced inline run fails the smoke-harness or shows scope creep at dispatch time, `/do`'s own `--trivial` handling (a separate task, E32_S14_T02) is responsible for falling back to the full `task` pipeline — this skill only ever writes the initial forced-inline task.
35
37
 
@@ -860,7 +860,7 @@ sections of `project/PROJECT_SUMMARY.md`:
860
860
 
861
861
  **Step B — Check both sections for existing content before proposing anything.** A section counts
862
862
  as a **stub** only if its body is empty, whitespace-only, or is (or is limited to) the literal
863
- placeholder text from `skills/init/assets/PROJECT_SUMMARY_template.md` — `_To be completed._`.
863
+ placeholder text from `skills/j-init/assets/PROJECT_SUMMARY_template.md` — `_To be completed._`.
864
864
  Anything else — a sentence, a partial list, a paragraph someone already wrote by hand — is real,
865
865
  non-stub content, however short, and is never silently overwritten. Check the Overview and
866
866
  Architecture & Structure sections independently; one can be a stub while the other is not.
@@ -97,7 +97,7 @@ Options:
97
97
  -h, --help Show this help and exit.
98
98
 
99
99
  Examples:
100
- $(basename "$0") skills/reconcile/
100
+ $(basename "$0") skills/j-reconcile/
101
101
  $(basename "$0") scripts/board_resolver.sh
102
102
  EOF
103
103
  }
@@ -104,7 +104,7 @@ Options:
104
104
  -h, --help Show this help and exit.
105
105
 
106
106
  Examples:
107
- $(basename "$0") skills/reconcile/
107
+ $(basename "$0") skills/j-reconcile/
108
108
  $(basename "$0") scripts/board_resolver.sh
109
109
  EOF
110
110
  }
@@ -183,7 +183,7 @@ fi
183
183
  # and .agents/ — scripts/ (which owns with-lock.sh) is never copied there, so
184
184
  # this script — itself shipped under skills/j-uncharted/scripts/ and mirrored
185
185
  # alongside it — cannot assume "$REPO_ROOT/scripts/with-lock.sh" exists.
186
- # Mirrors skills/init/scripts/init.sh's PKG_ROOT fallback: prefer a monorepo
186
+ # Mirrors skills/j-init/scripts/init.sh's PKG_ROOT fallback: prefer a monorepo
187
187
  # checkout's sibling scripts/ dir, else fall back to the installed npm
188
188
  # package under node_modules/@jenga-ai/agent.
189
189
  if [ -f "$SCRIPT_DIR/../../../scripts/with-lock.sh" ]; then
@@ -53,7 +53,7 @@ REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
53
53
  # and .agents/ — scripts/ (which owns both validators) is never copied there,
54
54
  # so this script — itself shipped under skills/j-uncharted/scripts/ and mirrored
55
55
  # alongside it — cannot assume "$REPO_ROOT/scripts/..." exists. Mirrors
56
- # skills/init/scripts/init.sh's PKG_ROOT fallback (same pattern already
56
+ # skills/j-init/scripts/init.sh's PKG_ROOT fallback (same pattern already
57
57
  # applied to this skill's elicitation-state.sh WITH_LOCK resolution): prefer
58
58
  # a monorepo checkout's sibling scripts/ dir, else fall back to the installed
59
59
  # npm package under node_modules/@jenga-ai/agent.
@@ -24,4 +24,4 @@ This file is generated/synced by `scripts/generate-j-alias.sh wtf` from `skills/
24
24
 
25
25
  ## Instructions
26
26
 
27
- `/wtf` is an alias of `/clearify`. Follow `skills/clearify/SKILL.md` in full — do not duplicate or reimplement its ambiguity-detection logic here. Read that file's `## Instructions` section and execute it exactly as written, using whatever prompt or conversation context is attached to this `/wtf` invocation.
27
+ `/wtf` is an alias of `/clearify`. Follow `skills/j-clearify/SKILL.md` in full — do not duplicate or reimplement its ambiguity-detection logic here. Read that file's `## Instructions` section and execute it exactly as written, using whatever prompt or conversation context is attached to this `/wtf` invocation.
@@ -169,8 +169,8 @@ This branch is entered when `detect-nl-intent.sh` (invoked above) classifies the
169
169
  This branch is entered when `detect-nl-intent.sh` classifies the argument as `nl_intent` — every comma-delimited segment failed the ID grammar, so the raw argument is treated as natural-language intent rather than a malformed ID list. This is purely a new *outcome* of the same argument-shape detection above — no new sigil, trigger prefix, or separate entry point is introduced.
170
170
 
171
171
  1. **Load the catalog** — invoke `skills/jenga/scripts/load-nl-catalog.sh` with no arguments (E53_S01_T02). Its stdout is the full skill catalog (`name`/`description`/`keywords`/`examples`/`prefered_agent` per skill), sourced exclusively from `lib/generate-skill-allow-list.js`'s generated inventory — see the script's own header for the full contract. Never re-derive this catalog by re-scanning `skills/` inline.
172
- 2. **Match** — run `skills/route/SKILL.md`'s **Step 2 — Match the Prompt to a Skill** (the three-pass keyword → example-similarity → description match, including its tie-break and no-match handling) against this catalog, treating `detect-nl-intent.sh`'s `raw_argument` field as the prompt. Reuse that section's matching logic by reference — do not re-author its prose here.
173
- 3. **Confident single match** — report the routing decision using `skills/route/SKILL.md`'s **Step 7 — Report Routing Decision** format (substitute `/jenga` for `/route` as the invoking command named in the report), then invoke the matched skill exactly as `skills/route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does: load `agents/<prefered_agent>.md` when the matched skill specifies `metadata.prefered_agent`, otherwise execute the skill instructions directly. The matched skill's own execution takes over from here — do not continue into this `/jenga` invocation's Phase 1.
172
+ 2. **Match** — run `skills/j-route/SKILL.md`'s **Step 2 — Match the Prompt to a Skill** (the three-pass keyword → example-similarity → description match, including its tie-break and no-match handling) against this catalog, treating `detect-nl-intent.sh`'s `raw_argument` field as the prompt. Reuse that section's matching logic by reference — do not re-author its prose here.
173
+ 3. **Confident single match** — report the routing decision using `skills/j-route/SKILL.md`'s **Step 7 — Report Routing Decision** format (substitute `/jenga` for `/route` as the invoking command named in the report), then invoke the matched skill exactly as `skills/j-route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does: load `agents/<prefered_agent>.md` when the matched skill specifies `metadata.prefered_agent`, otherwise execute the skill instructions directly. The matched skill's own execution takes over from here — do not continue into this `/jenga` invocation's Phase 1.
174
174
  4. **No match, or an ambiguous multi-way tie (single-skill match)** — before surfacing `/route`'s generic disambiguation options, attempt a **playbook fallback** (E53_S02): invoke `skills/jenga/scripts/match-playbook.sh "<raw_argument>"`. This step only ever runs when step 3 above did NOT already commit to a confident single-skill match — a confident single-skill match always wins outright and this playbook fallback is never even invoked in that case. Branch on `match-playbook.sh`'s `classification` field:
175
175
  - `playbook_match` → continue to **step 5 (Playbook proposal and execution)** below.
176
176
  - `ambiguous` or `no_match` → continue to **step 6 (Fall through to `/route`'s disambiguation)** below — the exact behavior this branch already had before E53_S02, unchanged.
@@ -208,12 +208,12 @@ This branch is entered when `detect-nl-intent.sh` classifies the argument as `nl
208
208
  - **Apply the transform** — when both `forward_from` (successfully resolved immediately above) and `resolve` are present, use your own LLM judgment to reshape/filter/type-bridge the forwarded value per `resolve`'s natural-language instructions (e.g. "pick the first three items", "convert this file_list to a text summary"). The transformed value — never the raw forwarded value — becomes this step's actual invocation input.
209
209
  - **Hard-fail, never silent pass-through** — if the transform cannot cleanly produce a usable, type-compatible result (the instructions don't plausibly apply to the actual value, the value is empty/malformed for what's being asked, or the result would not plausibly satisfy the target step's expected input shape), do **not** invoke this step and do **not** guess or pass through a differently-shaped value. Instead call `skills/jenga/scripts/run-playbook-step.sh advance <state_file> failed "<note>"`, where `<note>` follows the format `resolve failed on step '<step name>': could not apply "<resolve text>" to raw value <raw pre-transform value> — <short reason>` (the raw pre-transform value is always included, for debugging). Then follow the `halted` handling in 5e-vi below exactly as any other step failure — immediately stop executing further steps, report `failed_step`/`failed_note`/`completed`/`skipped`/`never_run` verbatim.
210
210
 
211
- Then invoke the step exactly as `skills/route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does for a single matched skill: load `agents/<prefered_agent>.md` when that step's own `SKILL.md` specifies `metadata.prefered_agent`, otherwise execute its instructions directly.
211
+ Then invoke the step exactly as `skills/j-route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does for a single matched skill: load `agents/<prefered_agent>.md` when that step's own `SKILL.md` specifies `metadata.prefered_agent`, otherwise execute its instructions directly.
212
212
  iii. After a normally-invoked step's execution concludes, call `skills/jenga/scripts/run-playbook-step.sh advance <state_file> passed ["<typed-output-value>"]` (the step completed successfully — supply the step's declared typed output, per its `output_types`, if it produced one) or `... advance <state_file> failed "<short failure note>"` (the step failed).
213
213
  iv. On a `step_ready` result, repeat step 5e for the newly-named step.
214
214
  v. On a `complete` result, report the full lists of `completed` AND `skipped` steps to the user and stop — the playbook run is finished; do not continue into this `/jenga` invocation's Phase 1.
215
215
  vi. On a `halted` result, **immediately stop executing any further steps** — no silent skip-ahead. Report `failed_step`, `failed_note`, `completed`, `skipped` (steps that already finished or were skipped), and `never_run` (steps that never got a chance to run) to the user verbatim from the halt report. Do not continue into this `/jenga` invocation's Phase 1.
216
- 6. **Fall through to `/route`'s disambiguation** — entered when step 4 found no playbook match (`ambiguous` or `no_match`). Surface the same disambiguation options `skills/route/SKILL.md`'s **Step 2** already defines for these cases (browse `/help`, create a new skill via `/btw`, or proceed with the raw prompt) by reference to that section — do not re-copy its prose. Halt this `/jenga` invocation once the user picks an option; none of Phase 0.75's remaining steps or Phases 1-4 run for this branch.
216
+ 6. **Fall through to `/route`'s disambiguation** — entered when step 4 found no playbook match (`ambiguous` or `no_match`). Surface the same disambiguation options `skills/j-route/SKILL.md`'s **Step 2** already defines for these cases (browse `/help`, create a new skill via `/btw`, or proceed with the raw prompt) by reference to that section — do not re-copy its prose. Halt this `/jenga` invocation once the user picks an option; none of Phase 0.75's remaining steps or Phases 1-4 run for this branch.
217
217
 
218
218
  #### Shared confirmation step (bare and scoped branches only)
219
219
 
@@ -228,6 +228,39 @@ After this phase completes (bare and scoped branches via confirmation, wildcard
228
228
 
229
229
  ---
230
230
 
231
+ ### Phase 0.9 — Generate the Run's Shared `orchestrator_session_id`
232
+
233
+ This phase runs for every entry mode that reaches Phase 1 at all — bare, scoped, and wildcard (`*`) —
234
+ immediately after Phase 0.75 completes and before Phase 1 begins. It does not run for the
235
+ natural-language branch's playbook/single-skill-match outcomes (E53_S01/E53_S02), since those hand off
236
+ entirely to another skill or playbook and never reach Phase 1-4 of this document.
237
+
238
+ Generate exactly **one** `orchestrator_session_id` for this entire `/jenga` run, here and only here.
239
+ Both Phase 3.5's bundle dispatch and Phase 4's first (and every later) wave dispatch need this same
240
+ value, which is why it is generated once, early, before either call site runs.
241
+
242
+ **Format.** Generate it as `jenga-<UTC ISO 8601 basic-format timestamp>` (e.g.
243
+ `jenga-20260917T162349Z`) — a short prefix naming the orchestrating skill, plus a UTC timestamp, with no
244
+ `/` or `..` characters. This follows the same session-id string convention this repo already uses for
245
+ other orchestrator-minted session ids (e.g. `/do`'s own dispatch sessions), and stays compatible with
246
+ `scripts/acquire-concurrency-slot.sh`'s own validation, which refuses a `<session_id>` argument
247
+ containing a path separator or traversal sequence, because the value is used verbatim in filenames such
248
+ as `project/queue/concurrency-slots-<session_id>.json`.
249
+
250
+ **Generate once, reuse everywhere in this run — never regenerate.** This value MUST be reused,
251
+ byte-for-byte unchanged, across:
252
+ - Phase 3.5 step 7b's bundle `/do <E##_S##>` call.
253
+ - Every wave dispatched by Phase 4 step 3, across every loop-back at Phase 4 step 5b. The loop-back
254
+ never mints a new value — it only ever references the one value generated here.
255
+
256
+ This is what allows `E32_S15`'s per-session `max_concurrent_developers`/`max_concurrent_testers` cap
257
+ (`skills/j-do/SKILL.md`'s `### 4.4`) to actually contend across an entire `/jenga` run's dispatches,
258
+ instead of every spawned `/do` sub-agent minting its own private `orchestrator_session_id` and getting
259
+ its own private, uncontended `project/queue/concurrency-slots-<id>.json` counter file — the root-cause
260
+ defect this story (`E32_S16`) fixes.
261
+
262
+ ---
263
+
231
264
  ### Phase 1 — Decompose Epics into Stories
232
265
 
233
266
  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.
@@ -272,7 +305,7 @@ For each in-scope story that has one or more tasks listed in `todo.md`:
272
305
  BUNDLE DETECTED: story <E##_S##> — <N> story-scoped tasks will execute as a bundle.
273
306
  ```
274
307
  where `<E##_S##>` is the story ID and `<N>` is the count of tasks in the list.
275
- 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.
308
+ b. Call `/do <E##_S##>` once (with the story ID, not individual task IDs), passing this run's shared `orchestrator_session_id` (generated once in Phase 0.9) into the bundle's sender object as its `session_id` field — a caller-supplied session id per `skills/j-do/SKILL.md`'s standalone-vs-caller-supplied contract (`### 4.4`/`### 5`, `E32_S16_T02`), so the bundle path's own `### 4.4` slot acquire shares this run's one counter file too, rather than minting a private one. This invokes the bundle execution path in `/do` (implemented in E32_S05_T02), which runs all tasks sequentially in one shared worktree.
276
309
  c. **Mark these tasks as bundled** — record their task IDs so Phase 4 skips individual dispatch for them.
277
310
  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.
278
311
 
@@ -282,11 +315,11 @@ Loop through `todo.md` and execute all eligible items, running independent ones
282
315
 
283
316
  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`.
284
317
  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.
285
- 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.
318
+ 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. Pass this run's shared `orchestrator_session_id` (generated once in Phase 0.9, unchanged across every wave) into each sub-agent's sender object as its `session_id` field, per `assets/sender_template.json`'s existing shape and `skills/j-do/SKILL.md`'s caller-supplied-session-id case (`### 4.4`/`### 5`, `E32_S16_T02`) — this is what lets `E32_S15`'s per-session developer/tester concurrency cap actually contend across every sub-agent dispatched within (and across) this run's waves, instead of each one minting its own private session id and its own private, uncontended counter file.
286
319
  4. **Mark In Progress** — update `status: In Progress` in each launched item's board file (YAML front-matter) immediately after launch.
287
320
  5. **Wait, drain, and loop** — once all active background agents in the wave have completed:
288
321
  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.
289
- 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).
322
+ 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). This loop-back reuses the SAME `orchestrator_session_id` generated once in Phase 0.9 for every subsequent wave's `/do` dispatches — it is never regenerated here or anywhere else in this phase.
290
323
 
291
324
  ### Exit condition
292
325
 
@@ -312,8 +345,8 @@ When no eligible candidates remain in Phase 4, exit and output:
312
345
  - **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.
313
346
  - **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.
314
347
  - **`detect-nl-intent.sh` classifies the argument as `mixed` (scoped branch)** — the whole invocation halts at Phase 0.75 with each rejected segment's `input`/`reason` reported verbatim, per `detect-nl-intent.sh`'s own classification contract (E53_S01_T01); 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.
315
- - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` (E53_S02) also finds no playbook match** — the natural-language branch's step 4 attempts the playbook fallback first (see the Natural-language branch's step 4/6), and only THEN surfaces `skills/route/SKILL.md`'s Step 2 no-match disambiguation options (browse `/help`, create a new skill via `/btw`, proceed with the raw prompt) instead of guessing; no phase past 0.75 runs until the user picks one.
316
- - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` returns an ambiguous multi-way tie between playbooks** — treated the same as the no-playbook-match case above: falls through to `skills/route/SKILL.md`'s Step 2 tie-break prompt (top candidates + a "neither, describe what you need" option) instead of guessing; no phase past 0.75 runs until the user picks one. (`match-playbook.sh`'s own `ambiguous` result — a tie between playbooks — is intentionally not given its own separate disambiguation UI; it is treated identically to `no_match` and routed to the same `/route` Step 2 fallback prose, which already has its own tie-break handling.)
348
+ - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` (E53_S02) also finds no playbook match** — the natural-language branch's step 4 attempts the playbook fallback first (see the Natural-language branch's step 4/6), and only THEN surfaces `skills/j-route/SKILL.md`'s Step 2 no-match disambiguation options (browse `/help`, create a new skill via `/btw`, proceed with the raw prompt) instead of guessing; no phase past 0.75 runs until the user picks one.
349
+ - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` returns an ambiguous multi-way tie between playbooks** — treated the same as the no-playbook-match case above: falls through to `skills/j-route/SKILL.md`'s Step 2 tie-break prompt (top candidates + a "neither, describe what you need" option) instead of guessing; no phase past 0.75 runs until the user picks one. (`match-playbook.sh`'s own `ambiguous` result — a tie between playbooks — is intentionally not given its own separate disambiguation UI; it is treated identically to `no_match` and routed to the same `/route` Step 2 fallback prose, which already has its own tie-break handling.)
317
350
  - **`match-playbook.sh` returns `playbook_match` and the user confirms the full chain, and every step succeeds** — the Natural-language branch's step 5e reports the full `completed` AND `skipped` steps lists to the user and stops; `/jenga`'s own Phase 1 never runs for this invocation (execution was already fully handled by the playbook's own steps, e.g. `j.do`/`j.dev-done`).
318
351
  - **`match-playbook.sh` returns `playbook_match` but the user cancels at the chain confirmation step (step 5c)** — identical posture to the existing picker/confirmation cancellation cases above: the entire `/jenga` run halts immediately after relaying the cancellation acknowledgement, with NO step of the chain executed; nothing on the board is modified by this invocation.
319
352
  - **`match-playbook.sh` returns `playbook_match`, the user confirms, and a step mid-chain fails** — the Natural-language branch's step 5e(vi) halts immediately on `run-playbook-step.sh`'s `halted` result: no step after the failed one runs (no silent skip-ahead), and the user is shown exactly which steps already completed or were skipped, which step failed (with its note), and which steps never ran.
@@ -329,3 +362,4 @@ When no eligible candidates remain in Phase 4, exit and output:
329
362
  - **A playbook step carries composition origin metadata (depth > 1), shown at confirmation (`E53_S05_T03`)** — the Natural-language branch's step 5b relays `render-playbook-confirmation.sh`'s rendered chain, which now visibly indents and labels that step's line with "(from playbook: <id>, depth N)" — composing another playbook's steps into a chain never hides where one playbook ends and another begins from the user, even though the whole chain is still ONE numbered, editable, confirmable list (never a separate confirmation per nested playbook) and checking/unchecking a composed step still works exactly like any other step.
330
363
  - **`/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.
331
364
  - **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.
365
+ - **`orchestrator_session_id` reuse across waves and phases (`E32_S16_T01`)** — the value generated once in Phase 0.9 is the exact same value threaded into Phase 3.5's bundle `/do` call and into every wave's `/do` sub-agent sender objects dispatched by Phase 4, for the entire duration of a single `/jenga` run; Phase 4 step 5b's loop-back never mints a new one. A run that halts before Phase 0.9 completes (e.g. cancelled at Phase 0.75's picker/confirmation) never reaches Phase 3.5 or Phase 4, so the value it would have generated is simply never used.
@@ -0,0 +1,32 @@
1
+ {
2
+ "id": "board-hygiene",
3
+ "name": "Board Hygiene",
4
+ "description": "Reconciles the scrum board against what is actually implemented, captures whatever drift that turns up as follow-up todos, and reports the resulting board state -- a read-only triage chain that writes no code and produces no commit of its own.",
5
+ "keywords": [
6
+ "board hygiene",
7
+ "board triage",
8
+ "board health",
9
+ "tidy the board",
10
+ "board audit"
11
+ ],
12
+ "examples": [
13
+ "reconcile the board, capture whatever drift you find as todos, then show me the status",
14
+ "do a full board hygiene pass -- sync it with what's really implemented, queue follow-ups for anything off, and report where we stand",
15
+ "check the board against reality, turn any discrepancies into todo items, and give me an overview at the end",
16
+ "the board feels out of sync -- triage it, capture the fixes, and summarise the state afterwards",
17
+ "audit the board end to end and only queue follow-up work if something is actually wrong"
18
+ ],
19
+ "steps": [
20
+ "j-reconcile",
21
+ {
22
+ "skill": "j-todo",
23
+ "forward_from": "j-reconcile",
24
+ "conditional": {
25
+ "depends_on": "j-reconcile",
26
+ "predicate": "non_empty"
27
+ },
28
+ "instruction": "capture each reconciliation finding as a follow-up todo"
29
+ },
30
+ "j-status"
31
+ ]
32
+ }
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://jenga.local/schemas/jenga-playbook.schema.json",
4
4
  "title": "Jenga Multi-Skill Playbook",
5
- "description": "Schema for a single multi-skill playbook definition consumed by skills/jenga/scripts/load-playbooks.sh (E53_S02_T01). A playbook is a dedicated, versionable data file describing an ORDERED chain of skills that /jenga's natural-language branch may propose (as an editable, confirmable numbered list -- see skills/jenga/scripts/render-playbook-confirmation.sh, E53_S02_T03) when free-text intent spans more than one skill and does not cleanly resolve to a single one via skills/route/SKILL.md's Step 2 matching. This file itself (schema.json) is never treated as a playbook -- load-playbooks.sh explicitly excludes it by filename when scanning skills/jenga/playbooks/*.json.",
5
+ "description": "Schema for a single multi-skill playbook definition consumed by skills/jenga/scripts/load-playbooks.sh (E53_S02_T01). A playbook is a dedicated, versionable data file describing an ORDERED chain of skills that /jenga's natural-language branch may propose (as an editable, confirmable numbered list -- see skills/jenga/scripts/render-playbook-confirmation.sh, E53_S02_T03) when free-text intent spans more than one skill and does not cleanly resolve to a single one via skills/j-route/SKILL.md's Step 2 matching. This file itself (schema.json) is never treated as a playbook -- load-playbooks.sh explicitly excludes it by filename when scanning skills/jenga/playbooks/*.json.",
6
6
  "type": "object",
7
7
  "required": ["id", "name", "description", "keywords", "examples", "steps"],
8
8
  "additionalProperties": false,
@@ -18,25 +18,92 @@
18
18
  },
19
19
  "description": {
20
20
  "type": "string",
21
- "description": "One-sentence explanation of what this playbook accomplishes end-to-end, used as the lowest-priority match signal (description match, same as skills/route/SKILL.md's Step 2 Pass 3) when keywords/examples don't produce a confident match."
21
+ "description": "One-sentence explanation of what this playbook accomplishes end-to-end, used as the lowest-priority match signal (description match, same as skills/j-route/SKILL.md's Step 2 Pass 3) when keywords/examples don't produce a confident match."
22
22
  },
23
23
  "keywords": {
24
24
  "type": "array",
25
- "description": "Short phrases (1-3 words) for verbatim, case-insensitive keyword matching against the raw natural-language prompt -- the highest-priority match signal (Pass 1), mirroring skills/route/SKILL.md's Step 2 Pass 1 semantics exactly, but scoped to this playbook's catalog rather than the single-skill catalog.",
25
+ "description": "Short phrases (1-3 words) for verbatim, case-insensitive keyword matching against the raw natural-language prompt -- the highest-priority match signal (Pass 1), mirroring skills/j-route/SKILL.md's Step 2 Pass 1 semantics exactly, but scoped to this playbook's catalog rather than the single-skill catalog.",
26
26
  "items": { "type": "string" },
27
27
  "minItems": 1
28
28
  },
29
29
  "examples": {
30
30
  "type": "array",
31
- "description": "Natural-language example prompts a user might type that should resolve to this playbook. Used for the semantic similarity match (Pass 2), mirroring skills/route/SKILL.md's Step 2 Pass 2 semantics. At least one example must plausibly span the full breadth of this playbook's steps (not just its first step) so it is distinguishable from a plain single-skill match.",
31
+ "description": "Natural-language example prompts a user might type that should resolve to this playbook. Used for the semantic similarity match (Pass 2), mirroring skills/j-route/SKILL.md's Step 2 Pass 2 semantics. At least one example must plausibly span the full breadth of this playbook's steps (not just its first step) so it is distinguishable from a plain single-skill match.",
32
32
  "items": { "type": "string" },
33
33
  "minItems": 1
34
34
  },
35
35
  "steps": {
36
36
  "type": "array",
37
- "description": "Ordered list of canonical skill DIRECTORY names (the directory under skills/<dir>/SKILL.md, e.g. \"j-brainstorm\", not the \"j.brainstorm\" frontmatter/invocation form and not the retired bare \"brainstorm\") that make up this playbook's chain, in the exact execution order. Per E50_S10's canonical naming contract the canonical directory is skills/j-<name>/, so a step value carries the j- prefix; the three permanent exceptions (jenga, jenga-permission-level, index) keep their bare directory names and are written bare. Each entry MUST resolve to an existing skills/<dir>/SKILL.md at load time -- load-playbooks.sh skips (with a stderr warning) any playbook referencing a nonexistent skill rather than silently including a broken chain in the catalog.",
38
- "items": { "type": "string" },
37
+ "description": "Ordered list of steps making up this playbook's chain, in the exact execution order. A step is either a BARE STRING or a STEPOBJECT (E53_S03; composition added by E53_S05). A bare string is shorthand for {\"skill\": \"<name>\"} -- \"bare\" describes the JSON shape, never a bare skill NAME. A skill name is always a canonical skill DIRECTORY name (the directory under skills/<dir>/SKILL.md, e.g. \"j-brainstorm\", not the \"j.brainstorm\" frontmatter/invocation form and not the retired bare \"brainstorm\"); per E50_S10's canonical naming contract the canonical directory is skills/j-<name>/, so a step value carries the j- prefix, while the three permanent exceptions (jenga, jenga-permission-level, index) keep their bare directory names and are written bare. Each skill name MUST resolve to an existing skills/<dir>/SKILL.md at load time -- load-playbooks.sh skips (with a stderr warning) any playbook referencing a nonexistent skill rather than silently including a broken chain in the catalog. NOTE: this schema documents SHAPE only. The cross-step rules -- forward_from naming an EARLIER step whose skill declares a non-empty output_types, conditional.depends_on naming an EARLIER step, composition cycle/depth limits -- are not expressible here and are enforced by load-playbooks.sh, which remains the single source of truth for validation.",
38
+ "items": {
39
+ "oneOf": [
40
+ {
41
+ "type": "string",
42
+ "description": "Bare-string step: shorthand for {\"skill\": \"<name>\"}, unchanged original behavior. An all-bare-string playbook loads byte-for-byte the same as it did before E53_S03, and a bare-string step is never rewritten into object form in the loader's output."
43
+ },
44
+ { "$ref": "#/definitions/stepObject" }
45
+ ]
46
+ },
39
47
  "minItems": 2
40
48
  }
49
+ },
50
+ "definitions": {
51
+ "stepObject": {
52
+ "type": "object",
53
+ "description": "A StepObject step (E53_S03_T01). Carries EXACTLY ONE of the mutually exclusive target fields `skill` or `playbook` -- a step naming both, or naming neither, is rejected by load-playbooks.sh.",
54
+ "additionalProperties": false,
55
+ "oneOf": [
56
+ { "required": ["skill"], "not": { "required": ["playbook"] } },
57
+ { "required": ["playbook"], "not": { "required": ["skill"] } }
58
+ ],
59
+ "properties": {
60
+ "skill": {
61
+ "type": "string",
62
+ "description": "Invokes a single skill, same as a bare string. Value is a canonical skill directory name (see the `steps` description above)."
63
+ },
64
+ "playbook": {
65
+ "type": "string",
66
+ "description": "Composes another playbook by ID (E53_S05). Resolved and flattened in place at load time, so the emitted catalog's `steps` array never contains a raw playbook-type entry. Existence validation, cycle detection, and a configurable nesting-depth limit (project/configs/playbook-config.json's max_composition_depth, default 3) all apply."
67
+ },
68
+ "instruction": {
69
+ "type": "string",
70
+ "description": "Static natural-language text appended to this step's own invocation message."
71
+ },
72
+ "forward_from": {
73
+ "type": "string",
74
+ "description": "Names an EARLIER step in the same playbook (by that step's skill name) whose typed output becomes this step's actual invocation input. The named source's SKILL.md must declare a non-empty output_types -- a skill declaring nothing can never be a forward source. Transparent across composition boundaries, since composition resolution runs before this validation."
75
+ },
76
+ "resolve": {
77
+ "type": "string",
78
+ "description": "Natural-language instructions for reshaping/filtering/type-bridging the value forwarded into this step (e.g. \"pick the first three items\"). A step carrying both `resolve` and `playbook` is rejected at load time: resolve may never pre-authorize a downstream confirmation gate (E53_S07's safety review, permanently rejected)."
79
+ },
80
+ "conditional": {
81
+ "type": "object",
82
+ "description": "Runs this step only when the predicate holds against a named earlier step's captured output; otherwise the step is marked `skipped` and the chain continues without halting (E53_S04_T02). Unlike forward_from, this does NOT require the depended-on step to declare output_types.",
83
+ "additionalProperties": false,
84
+ "required": ["depends_on", "predicate"],
85
+ "properties": {
86
+ "depends_on": {
87
+ "type": "string",
88
+ "description": "Names an EARLIER skill step in this playbook whose captured output the predicate is evaluated against.",
89
+ "minLength": 1
90
+ },
91
+ "predicate": {
92
+ "type": "string",
93
+ "description": "The recognized predicate grammar, whose single source of truth is skills/jenga/scripts/run-playbook-step.sh: non_empty | empty | equals:<value> | not_equals:<value>.",
94
+ "pattern": "^(non_empty|empty|equals:.+|not_equals:.+)$"
95
+ }
96
+ }
97
+ },
98
+ "version": {
99
+ "type": "string",
100
+ "description": "RESERVED, currently a no-op. Accepted and passed through unchanged; not acted upon by anything yet. Exists so a future schema revision has a place to declare itself without a retroactive migration of every existing playbook."
101
+ },
102
+ "schema_version": {
103
+ "type": "string",
104
+ "description": "Alternate key name for `version`. Same reserved no-op semantics."
105
+ }
106
+ }
107
+ }
41
108
  }
42
109
  }
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "understand-then-commit",
3
+ "name": "Understand Then Build",
4
+ "description": "Investigates existing or unfamiliar code first, then runs the full idea-to-commit pipeline on top of what was learned -- a composed chain built from the canonical idea-to-committed playbook, terminating at the commit.",
5
+ "keywords": [
6
+ "understand then build",
7
+ "investigate then build",
8
+ "explore then implement",
9
+ "onboard and build",
10
+ "learn the codebase first"
11
+ ],
12
+ "examples": [
13
+ "help me understand this codebase first, then plan a change here, build it, and commit it",
14
+ "investigate this existing code, then take an idea through planning, implementation, and a commit",
15
+ "look into what's already here, then brainstorm a fix, build it, and commit the work",
16
+ "onboard me to this project and then walk a new idea from planning through to a committed change"
17
+ ],
18
+ "steps": ["j-uncharted", {"playbook": "idea-to-committed"}]
19
+ }
@@ -46,7 +46,7 @@ fi
46
46
 
47
47
  # Resolve the jenga-agent PACKAGE root (where lib/generate-skill-allow-list.js and the canonical
48
48
  # skills/ tree actually live) — same monorepo-checkout vs. installed-npm-package detection used
49
- # by skills/init/scripts/init.sh's PKG_ROOT resolution.
49
+ # by skills/j-init/scripts/init.sh's PKG_ROOT resolution.
50
50
  if [ -d "$SCRIPT_DIR/../../../templates" ]; then
51
51
  PKG_ROOT="$SCRIPT_DIR/../../.."
52
52
  elif [ -d "$JENGA_PROJECT_DIR/node_modules/@jenga-ai/agent/templates" ]; then
@@ -474,7 +474,7 @@ else
474
474
 
475
475
  # Resolve the jenga-agent PACKAGE root (where the canonical skills/ tree actually lives) — same
476
476
  # monorepo-checkout vs. installed-npm-package detection used by
477
- # skills/jenga/scripts/load-nl-catalog.sh's PKG_ROOT resolution and skills/init/scripts/init.sh.
477
+ # skills/jenga/scripts/load-nl-catalog.sh's PKG_ROOT resolution and skills/j-init/scripts/init.sh.
478
478
  if [ -d "$SCRIPT_DIR/../../../templates" ]; then
479
479
  PKG_ROOT="$SCRIPT_DIR/../../.."
480
480
  PROJECT_DIR="$JENGA_PROJECT_DIR"
@@ -3,7 +3,7 @@
3
3
  # skills/jenga/scripts/match-playbook.sh
4
4
  #
5
5
  # Deterministic PLAYBOOK matcher for `/jenga`'s natural-language branch (E53_S02_T02). Runs the
6
- # same three-pass matching *philosophy* as `skills/route/SKILL.md`'s Step 2 (keyword ->
6
+ # same three-pass matching *philosophy* as `skills/j-route/SKILL.md`'s Step 2 (keyword ->
7
7
  # example similarity -> description), but scoped to the playbook catalog produced by
8
8
  # `load-playbooks.sh` (E53_S02_T01) instead of the single-skill catalog `load-nl-catalog.sh`
9
9
  # produces for `/route`/`/jenga`'s existing single-skill matching.
@@ -297,7 +297,7 @@ currently gates on its presence.
297
297
 
298
298
  ### `[SPIKE]` — Bounded Research
299
299
 
300
- **Scope:** story and task level only.
300
+ **Scope:** story and task level, **and rapports** (extended by `E35_S03_T01`, see below).
301
301
 
302
302
  **Meaning:** a time-boxed research or exploration effort whose output is a decision, a design note,
303
303
  or an answered question — **not** shippable implementation code. Existing usage (`E06_S03_spike-editable-board.md`,
@@ -312,6 +312,19 @@ or an answered question — **not** shippable implementation code. Existing usag
312
312
  `[SPIKE]` predates this document; this section formalizes an existing informal convention rather
313
313
  than introducing new behavior.
314
314
 
315
+ **Rapport scope extension (`E35_S03_T01`):** `[SPIKE]` also applies to a problem rapport (see
316
+ `project/rapports/problems/`) via the same title-text convention — a `[SPIKE] <Topic>` prefix on
317
+ the rapport's own title/header field, not a frontmatter key (rapports don't carry board
318
+ frontmatter at all). This marks a rapport as itself surfacing a bounded research question or a
319
+ distinct idea worth tracking, independent of whatever problem the rapport was originally filed
320
+ to report. In practice this happens as part of the idea-lifecycle outcome-record mechanism
321
+ (`E35_S03`): when a `project/ideas.md` entry that originated from a rapport (see
322
+ `skills/idea/SKILL.md`'s "Source-Rapport Link Convention") is later promoted or rejected, the
323
+ outcome-record step updates that rapport with the decision and prefixes its title `[SPIKE]` if
324
+ it isn't already tagged. As with the story/task usage above, this is advisory only — nothing
325
+ validates or enforces the prefix's presence, and a rapport's normal `Type:`/`Related
326
+ Epic/Story/Task` fields (per `templates/PROBLEM_RAPPORT_TEMPLATE.md`) are unaffected by it.
327
+
315
328
  ### `[ARCH]` — Durable Architectural Inventory
316
329
 
317
330
  **Scope:** epic, story, and task level. This is the **first tag extended to epic level** — `[SPIKE]`
@@ -3,8 +3,20 @@ name: <skill-name>
3
3
  description: <One-sentence description of what this skill does and when to use it.>
4
4
  metadata:
5
5
  prefered_agent: <agent-name> # optional — remove if not applicable
6
+ output_types: <type> # optional — forwardable output type(s) this skill produces; a single
7
+ # type string or a list of {when, type} entries. Declare only if this
8
+ # skill genuinely produces output a playbook step could consume
9
+ # (honesty over breadth, E53_S11). Remove if not applicable.
10
+ input_types: <type> # optional — forwarded input type(s) this skill accepts; same two
11
+ # shapes as output_types. May be broadly declared — honesty over
12
+ # breadth is scoped to output_types only (E62_S01_T03).
13
+ # Remove if not applicable.
6
14
  ---
7
15
 
16
+ > Type values come from the canonical vocabulary in `templates/playbook-types.json`. See
17
+ > `docs/skill-authoring.md`'s `output_types` and `input_types` sections for the full contract,
18
+ > including the `text` rule and the normalize-versus-convert boundary.
19
+
8
20
  # <Skill Title> — <Short tagline>
9
21
 
10
22
  ## Instructions
@@ -6,7 +6,7 @@
6
6
  "autoMode": {
7
7
  "allow": [
8
8
  "$defaults",
9
- "Bash(bash skills/mirror-public/scripts/mirror.sh*)"
9
+ "Bash(bash skills/j-mirror-public/scripts/mirror.sh*)"
10
10
  ]
11
11
  },
12
12
  "permissions": {
@@ -6,7 +6,7 @@
6
6
  "autoMode": {
7
7
  "allow": [
8
8
  "$defaults",
9
- "Bash(bash skills/mirror-public/scripts/mirror.sh*)"
9
+ "Bash(bash skills/j-mirror-public/scripts/mirror.sh*)"
10
10
  ]
11
11
  },
12
12
  "permissions": {