j.jenga
Skill0 starsChanged 22 days agoApache-2.0View on GitHub
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.
In this repository151 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- j.brainstormSkill
- j.breakSkill
- j.btwSkill
- j.check-inSkill
- j.clearifySkill
- j.close-storySkill
- j.cloud-connectSkill
- j.commitSkill
- j.connectSkill
- j.continueSkill
- j.conventionsSkill
- j.dashboard-shareSkill
- j.dashboardSkill
- j.deep-diveSkill
- j.distributeSkill
- j.docSkill
- j.doc-syncSkill
- j.doooSkill
- j.doSkill
- j.jenga-configSkill
- j.jenga-permission-levelSkill
- j.errorSkill
- j.evaluateSkill
- j.examplifySkill
- j.gitignoreSkill
- j.helpSkill
- j.ideaSkill
- j.improveSkill
- j.initSkill
- j.jbpSkill
- j.lgtmSkill
- j.pi-planSkill
- j.playbook-newSkill
- j.playbookSkill
- j.proceedSkill
- j.publishSkill
- j.reconcile-originSkill
- j.reconcileSkill
- j.redoSkill
- j.skillifySkill
- j.smoke-testSkill
- j.spinoffSkill
- j.statusSkill
- j.testSkill
- j.todoSkill
- j.toolsSkill
- j.unchartedSkill
- j.wtfSkill
- skill-nameSkill
- j.brainstormSkill
- j.breakSkill
- j.btwSkill
- j.check-inSkill
- j.clearifySkill
- j.close-storySkill
- j.cloud-connectSkill
- j.commitSkill
- j.connectSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
25 rules. About 0 are common, 10 are distinctive, and 15 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈10
Distinctive · estimate
This author's own choices. Worth a look.
15
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 38
If the file does not exist, emit:
- Distinctive: line 44
If the file is not valid JSON, emit:
- Distinctive: line 60
Before accepting any task for decomposition or execution, the executing agent must validate the task's scope fields.
- Very specific: line 66
If the task frontmatter contains an execution_scope field, its value must be one of: task, story, epic, inline.
- Distinctive: line 68
If the value is anything else, halt immediately with:
- Distinctive: line 74
Do not proceed with this task.
- Very specific: line 78
If execution_scope is present, scope_rationale must also be present and must contain at least one digit (0–9) or the word "file" …
- Very specific: line 80
If scope_rationale is absent, or present but contains no digit and does not contain the word "file", halt with:
- Very specific: line 90
If execution_scope is "epic", the task must also have epic_scope_approval: true set explicitly in its frontmatter.
- Very specific: line 92
If epic_scope_approval is absent or is not exactly true, halt with:
- Very specific: line 102
If the task frontmatter contains crucial_level: locked (per $([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo …
- Very specific: line 124
If the task frontmatter contains none of execution_scope, scope_rationale, needs_docs, jenga_assigned, or override_justification, treat the …
- Distinctive: line 132
If all applicable rules pass (or the task is a legacy task), proceed to the next phase for that task without any error output.
- Distinctive: line 164
No argument at all → bare branch.
- Very specific: line 177
Wait for the user's chat reply, then invoke skills/jenga/scripts/render-picker.sh <state_file> "<raw_reply>" (continue mode).
- Distinctive: line 323
After this phase completes (bare and scoped branches via confirmation, wildcard branch immediately), proceed to Phase 1.
- Distinctive: line 349
Every wave dispatched by Phase 4 step 3, across every loop-back at Phase 4 step 5c.
- Very specific: line 362
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 …
- Very specific: line 364
Read all files in project/board/epics/.
- Very specific: line 370
If Phase 0.75 produced a scoped set, restrict this phase to stories that are members of that set (directly selected, expanded from an …
- Very specific: line 372
Read all files in project/board/stories/.
- Very specific: line 378
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 …
- Very specific: line 380
Read all files in project/board/tasks/.
- Very specific: line 382
After this phase, todo.md reflects the full set of in-scope work (or the full board, under *).
- Distinctive: line 386
Before dispatching individual tasks in Phase 4, check each story for bundle eligibility.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Jenga — Auto-Implementation Orchestrator
Purpose
/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:
- Bare
/jenga(no argument) — renders a numbered picker of the full board, then an editable confirmation tree, before anything executes. /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./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/jengaruns with no user prompts at all.
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.
Instructions
Phase 0 — Load threshold config
Read project/configs/scope-thresholds.json.
If the file does not exist, emit:
ERROR: project/configs/scope-thresholds.json not found. Cannot proceed.and halt. Do not fall back to any default values.
If the file is not valid JSON, emit:
ERROR: project/configs/scope-thresholds.json is malformed (invalid JSON). Cannot proceed.and halt.
Extract the following named values for use throughout this skill:
inline_max_files— maximum files a task may touch to qualify for inline execution scopeinline_max_lines— maximum total lines changed for inline scopestory_max_files— maximum files a task may touch to qualify for story-scope bundlingbundle_lock_ttl_minutes— time-to-live in minutes for a story-scope bundle lock
These values must be read fresh on each invocation. Never use hardcoded fallbacks.
Phase 0.5 — Pre-flight Validation
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.
For each task read from the board, apply the following checks in order:
Rule 1 — Valid execution_scope value
If the task frontmatter contains an execution_scope field, its value must be one of: task, story, epic, inline.
If the value is anything else, halt immediately with:
VALIDATION ERROR [<task_id>]: execution_scope "<value>" is not a valid scope. Allowed: task, story, epic, inline.Do not proceed with this task.
Rule 2 — scope_rationale must contain a measurable claim
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).
If scope_rationale is absent, or present but contains no digit and does not contain the word "file", halt with:
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".Do not proceed with this task.
Rule 3 — epic scope requires explicit human approval
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.
If epic_scope_approval is absent or is not exactly true, halt with:
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.Do not proceed with this task.
Rule 4 — crucial_level: locked forces execution_scope: inline
If the task frontmatter contains crucial_level: locked (per $([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/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.
This rule auto-corrects and continues; unlike Rules 1-3, it never halts.
If
execution_scopeis present and its value is anything other thaninline, correct it toinlinedirectly in the task file, and record a logged note of the correction by appending to that task'soverride_justificationfrontmatter field (the auditable mechanism for this rule — do not useevents.jsonor any other location) a line of the form:override_justification: "Rule 4 auto-correction <date>: execution_scope forced from '<previous_value>' to 'inline' because crucial_level: locked."Then emit (non-fatally — do not halt):
AUTO-CORRECTION [<task_id>]: crucial_level=locked requires execution_scope=inline; corrected from "<previous_value>" to "inline".- If
execution_scopeis absent entirely, set it toinlinedirectly in the task frontmatter. Do not fall through to the Backward-compatibility default ofexecution_scope: taskdocumented immediately below — alockeditem overrides that default even when no other execution-scope fields are present. Nooverride_justificationnote is required in this case, since there is no prior value being overridden.
Proceed to the next rule (or the next phase, if this was the last applicable rule) after applying the correction.
Backward compatibility — legacy tasks
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:
- Assume
execution_scope: task - Assume
needs_docs: true - Skip all three rules above and proceed without error.
Validation success
If all applicable rules pass (or the task is a legacy task), proceed to the next phase for that task without any error output.
Phase 0.75 — Entry Mode Resolution
This phase determines how /jenga was invoked and, for two of the four 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.
First, check for the --enrich flag (E53_S13_T01, ported from the now-retired /route's board
- docs enrichment — see the Natural-language branch's step 3 and the Skill Matching & Invocation
Contract's Report format below for what it actually does). If the raw argument passed to
/jengabegins with the exact leading token--enrichfollowed by at least one space, strip that token (and the single space after it) before anything else runs, and rememberenrichment_requested = truefor the remainder of this invocation. The remaining text — never the original argument with the flag still attached — is what every step below (includingdetect-nl-intent.sh) treats as "the raw argument passed to/jenga"; this is what keeps the flag from ever being visible todetect-nl-intent.sh'sall_resolved/nl_intent/mixedclassification. If--enrichis not present as a leading token,enrichment_requested = falseand the argument is used as-is — byte-for-byte the same behavior as before this flag existed.
--enrich alone (nothing after it, once whitespace is stripped) reduces to an empty remaining
argument — treat this exactly like no argument at all (bare branch); the flag has nothing to
enrich without free-form text and bare /jenga never does skill matching. --enrich * reduces to
the wildcard branch the same way (remaining text is the literal *); the wildcard branch also
never does skill matching, so enrichment_requested is simply never consulted there. Concretely,
enrichment_requested is only ever read in the natural-language branch below, and only once
that branch's own step 3 confirms a single-skill match — it is inert everywhere else. Passing
--enrich on a scoped (<ids>) argument is likewise a no-op: the scoped branch does not perform
skill matching either, so there is nothing for the flag to attach to.
Then, determine the invocation form from the remaining argument (if any) passed to /jenga:
- No argument at all → bare branch.
- The argument is the literal string
*→ wildcard branch. - Any other non-empty argument → invoke
skills/jenga/scripts/detect-nl-intent.sh "<remaining argument>"(E53_S01_T01) and branch on itsclassificationfield:all_resolvedormixed→ scoped branch (below) — this is the same branch as before; only its internal mechanics changed (see below).nl_intent→ natural-language branch (below) — new for E53_S01, no new sigil or entry point, purely a new outcome of this same argument-shape detection.
Wildcard branch (/jenga *)
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.
Bare branch (/jenga)
- Invoke
skills/jenga/scripts/render-picker.shwith no arguments (start mode). Relay its STDOUT (the numbered checklist) to the user verbatim — no summarizing, no reformatting. Capture theSTATE_FILE:path from STDERR. - Wait for the user's chat reply, then invoke
skills/jenga/scripts/render-picker.sh <state_file> "<raw_reply>"(continue mode).- Error turn (plain text on STDOUT, state file retained) — relay verbatim and return to step 2 for another reply.
- Cancellation — relay the cancellation acknowledgement and halt the entire
/jengarun; do not proceed to any later phase. - Resolved (JSON object on STDOUT, state file removed) — extract
resolved_ids_csvand continue to the shared confirmation step below.
Scoped branch (/jenga <ids>)
This branch is entered when detect-nl-intent.sh (invoked above) classifies the argument as all_resolved or mixed — the picker is skipped entirely in this branch. detect-nl-intent.sh has already invoked resolve-id.sh internally and reduced its per-segment output to one of these two shapes; skills/jenga/SKILL.md never parses resolve-id.sh's raw array itself (see detect-nl-intent.sh's own header comment for the full classification contract, E53_S01_T01).
- On
all_resolved, take theresolved_ids(orresolved_ids_csv) field directly fromdetect-nl-intent.sh's output and continue to the shared confirmation step below. - On
mixed, halt this phase (do not proceed to confirmation or Phase 1) and report each entry indetect-nl-intent.sh'srejectedarray — itsinputandreason— to the user verbatim; a partial or ambiguous ID is never guessed. The user must re-invoke/jenga <ids>with corrected input.
Natural-language branch (/jenga <free-form text>)
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.
- Load the catalog — invoke
skills/jenga/scripts/load-nl-catalog.shwith no arguments (E53_S01_T02). Its stdout is the full skill catalog (name/description/keywords/examples/prefered_agentper skill), sourced exclusively fromlib/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-scanningskills/inline. - Match — run this section's own Skill Matching & Invocation Contract (below) — the three-pass keyword → example-similarity → description match, including its tie-break and no-match handling — against this catalog, treating
detect-nl-intent.sh'sraw_argumentfield as the prompt. Confident single match — if
enrichment_requestedistrue(the--enrichflag was passed, per Phase 0.75's preamble above), first invokeskills/jenga/scripts/enrich-nl-prompt.sh "<raw_argument>"(E53_S13_T01, ported from/route's Steps 3-5) and assemble the enriched composite message from its JSON output, in this exact order:- Part A — Matched skill (full content) — the matched skill's full
SKILL.mdbody (everything after its YAML front-matter), wrapped as<!-- SKILL: /<matched-skill-name> --> ... <!-- END SKILL -->. - Part B — Board & documentation context — a
## 📋 Relevant Board Contextlist (one line perboard_itemsentry:- [<status>] **<id>** — <title> (\<file>`)) followed by a## 📄 Relevant Documentationlist (one line perdocsentry: `` -<path>` —<summary>``). Omit either sub-list entirely (not an empty heading) when its array is empty. - Part C — Original prompt —
## 🗣 Original Promptfollowed by the raw prompt, verbatim, in a blockquote.
Then report the routing decision using the Skill Matching & Invocation Contract's Report format (including its
Board items found/Docs foundlines, populated fromenrich-nl-prompt.sh'sboard_items_found/docs_foundfields, sinceenrichment_requestedistruehere), and invoke the matched skill exactly as the Invoke rule already does — but deliver the enriched composite message as the working input instead of the raw prompt when enrichment ran. Whenenrichment_requestedisfalse(the default, unflagged path — unchanged from before this flag existed), skipenrich-nl-prompt.shentirely: report using the Report format's default (noBoard items found/Docs foundlines) and invoke the matched skill directly with the raw prompt, exactly as before. Either way: loadagents/<prefered_agent>.mdwhen the matched skill specifiesmetadata.prefered_agent, otherwise execute the skill instructions directly. The matched skill's own execution takes over from here — do not continue into this/jengainvocation's Phase 1.- Part A — Matched skill (full content) — the matched skill's full
- No match, or an ambiguous multi-way tie (single-skill match) — before surfacing the Skill Matching & Invocation Contract'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 onmatch-playbook.sh'sclassificationfield:playbook_match→ continue to step 5 (Playbook proposal and execution) below.ambiguousorno_match→ continue to step 6 (Fall through to the Skill Matching & Invocation Contract's disambiguation) below — the exact behavior this branch already had before E53_S02, unchanged.
- Playbook proposal and execution — entered only on a
playbook_matchresult from step 4. A proposed playbook is an ordered chain of skills (e.g. the canonicalbrainstorm -> j.todo -> j.do -> j.dev-done -> j.mirror-publicchain defined inskills/jenga/playbooks/brainstorm-to-mirror.json) that must be confirmed, editable, and confirmable perCLAUDE.md's Interaction Pattern before any step executes — the same confirm-before-execute posture/jengaalready applies to the bare/scoped branches viarender-confirmation.sh. a. Resolve conditional metadata (E53_S04_T02/T04) — before rendering, inspect the matched playbook's ownstepsarray (as returned byload-playbooks.sh's catalog, not the flattened string list) for any StepObject carrying aconditional: {"depends_on": "<name>", "predicate": "..."}field. Build a JSON object mapping each such step's name to itsdepends_onstep's name only (e.g.{"stepC": "stepA"}— the confirmation display needs no predicate detail, only which step to point at). If no step carries aconditional, this object is empty/omitted. Also resolve composition origin metadata (E53_S05_T01/T03) — inspect the SAMEstepsarray (match-playbook.sh'sstepsfield is already load-playbooks.sh's fully FLATTENED, composition-resolved catalog output — composition is invisible tomatch-playbook.shitself; there is nothing further to "flatten" at this point, only to read) for any StepObject carrying_origin_playbook/_origin_depth(present only on steps whose_origin_depthis greater than 1 — seeload-playbooks.sh's header "COMPOSITION RESOLUTION"). Build a JSON object mapping each such step's name to{"playbook_id": "<_origin_playbook>", "depth": <_origin_depth>}. If no step in this playbook came from a composed/nested playbook, this object is empty/omitted. b. Render and confirm the chain — invokeskills/jenga/scripts/render-playbook-confirmation.sh "<playbook_id>" "<name>" "<comma-separated steps>" ["<json-conditionals from 5a>" ["<json-origins from 5a>"]](start mode, usingmatch-playbook.sh'splaybook_id/name/stepsfields verbatim; the 4th argument is omitted entirely when 5a produced no conditionals, and the 5th argument is omitted entirely when 5a produced no composition-origin metadata — omitting both reproduces the exact pre-E53_S043-arg call, and omitting only the 5th reproduces the exact pre-E53_S054-arg call). Relay STDOUT (the numbered chain + instructions, now marking any conditional step with "(may be skipped depending on step N's result)" and any composed/nested step with indentation plus "(from playbook:<id>, depth N)") to the user verbatim. Capture theSTATE_FILE:path from STDERR. c. Wait for the user's chat reply, then invokeskills/jenga/scripts/render-playbook-confirmation.sh <state_file> "<raw_reply>"(continue mode).- Toggle or error turn (plain text on STDOUT, state file retained) — relay verbatim and return to step 5c for another reply. This loops exactly as the existing bare/scoped confirmation flow's own toggle/error turns already do.
- Cancellation — relay the cancellation acknowledgement and halt the entire
/jengainvocation immediately, with no step executed — identical posture to the existing picker/confirmation cancellation edge cases already documented for the bare/scoped branches (see "## Edge Cases" below). Confirmed (JSON object on STDOUT, state file removed) — take the confirmed
stepsarray (checked-only, in original playbook order) and continue to step 5d. d. Initialize the sequential runner — re-derive the<json-conditionals>object forrun-playbook-step.sh initfrom the same StepObject data as 5a, but ininit's own richer shape ({"<step>": {"depends_on": "...", "predicate": "..."}},E53_S04_T02), and apply one filtering rule first: drop any conditional whosedepends_onstep is not present in the CONFIRMED step list from 5c. The user may have unchecked the depended-on step at confirmation;initwould otherwise reject such a conditional outright (its existence check requiresdepends_onto be an earlier step in the run), and the more sensible fallback than a hard failure over the user's own edit is to treat that now-conditionless step as always-running for this particular execution. Also re-derive the composition origin metadata (E53_S05_T04) forinit's optional 5th argument, from the same_origin_playbook/_origin_depthdata as 5a, filtered the same way (drop any entry for a step the user unchecked at confirmation —initrequires every key to be present in the confirmed step list). Invokeskills/jenga/scripts/run-playbook-step.sh init "<playbook_id>" "<name>" "<comma-separated confirmed steps>" ["<json-conditionals, filtered>" ["<json-origins, filtered>"]](the 4th argument is omitted when empty, and the 5th is omitted when empty — omitting both reproduces the exact pre-E53_S043-arg call, and omitting only the 5th reproduces the exact pre-E53_S054-arg call). Itsstep_readyresult names the first step to invoke. e. Execute steps in a loop — for the step named by the runner's most recentstep_readyresult: i. Evaluate whether this step should run (E53_S04_T02) — invokeskills/jenga/scripts/run-playbook-step.sh should-skip <state_file>.{"skip": true, ...}— do not invoke the step. Callskills/jenga/scripts/run-playbook-step.sh advance <state_file> skippeddirectly (neverpassed/failedfor a step that never ran), then handle its result exactly as iv-vi below (step_ready/complete/halted) — skip step 5e-ii and 5e-iii entirely for this step, since it never runs. This is non-blocking and non-failing — it never halts the chain, and is not narrated to the user turn-by-turn (it is folded into the finalcompleted/skippedsummary reported atcomplete/halted, the same posture already given to individualpassedsteps, which also aren't separately narrated mid-chain).{"skip": false, ...}— proceed to step 5e-ii and invoke the step normally. ii. Resolveforward_fromandresolve, if the step declares either (E53_S03, runtime-wired byE53_S04_T02;resolveruntime behavior added byE53_S06_T01) — invokeskills/jenga/scripts/run-playbook-step.sh get-output <state_file> <named source step>before invoking this step. On{"status": "found", "value": "..."}, use that value as this step's actual invocation input (per the design's forwarding semantics). On{"status": "unavailable", "reason": "step_skipped"}or"reason": "not_captured"— the named source produced no usable value (it was itself skipped, or never captured one) — do not guess a fallback value; treat this exactly like a step failure: calladvance <state_file> failed "forward_from source '<name>' unavailable (<reason>)"and follow thehaltedhandling in 5e-vi below, without ever invoking this step. This is the documented, non-silent failure mode for aforward_fromnaming a skipped step (E53_S04_T06's fixture coverage).
Then, if the step also declares a non-empty
resolvefield (E53_S06_T01) — this is a documented, named, scoped exception toCLAUDE.md's "Skill Implementation Principle — Scripts Over Inline Logic": open-ended reshaping/filtering/type-bridging genuinely needs the agent's own LLM judgment, which a deterministic script cannot provide, andresolveis never used for anything else in this codebase's playbook mechanism — in particular, never to pre-authorize a downstream confirmation, a combinationload-playbooks.shalready rejects outright at load time (seeproject/documentation/skill-authoring.md's "Theresolve/ confirmation-gate rule"):- No-op without
forward_from— a step carryingresolvebut noforward_from(or one that resolved to no forwardable value) has nothing to reshape. This is a defined, non-crashing runtime behavior, not an error: theresolvefield is simply ignored for that step, and invocation proceeds exactly as it would with noresolvefield at all. - Apply the transform — when both
forward_from(successfully resolved immediately above) andresolveare present, use your own LLM judgment to reshape/filter/type-bridge the forwarded value perresolve'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. - 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 formatresolve 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 thehaltedhandling in 5e-vi below exactly as any other step failure — immediately stop executing further steps, reportfailed_step/failed_note/completed/skipped/never_runverbatim.
Then invoke the step exactly as this section's Skill Matching & Invocation Contract's Invoke rule already does for a single matched skill: load
agents/<prefered_agent>.mdwhen that step's ownSKILL.mdspecifiesmetadata.prefered_agent, otherwise execute its instructions directly. iii. After a normally-invoked step's execution concludes, callskills/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 itsoutput_types, if it produced one) or... advance <state_file> failed "<short failure note>"(the step failed). iv. On astep_readyresult, repeat step 5e for the newly-named step. v. On acompleteresult, report the full lists ofcompletedANDskippedsteps to the user and stop — the playbook run is finished; do not continue into this/jengainvocation's Phase 1. vi. On ahaltedresult, immediately stop executing any further steps — no silent skip-ahead. Reportfailed_step,failed_note,completed,skipped(steps that already finished or were skipped), andnever_run(steps that never got a chance to run) to the user verbatim from the halt report. Do not continue into this/jengainvocation's Phase 1.
- Fall through to the Skill Matching & Invocation Contract's disambiguation — entered when step 4 found no playbook match (
ambiguousorno_match). Surface the same disambiguation options this section's Skill Matching & Invocation Contract already defines for these cases (browse/help, create a new skill via/btw, or proceed with the raw prompt). Halt this/jengainvocation once the user picks an option; none of Phase 0.75's remaining steps or Phases 1-4 run for this branch.
Skill Matching & Invocation Contract
/jenga is the sole owner of this contract (E53_S13_T02). /route — which previously carried
an independent, hand-synced copy of this same three-pass matching/tie-break/report logic in its own
Step 2/6/7 — has been retired outright (hard break, no shim; see E53_S13's story). There is no
other copy of this contract anywhere in the codebase to keep in sync with; if you change the matching
logic here, this section is the only place that needs updating.
Matching (three passes, stop at first confident match):
- Pass 1 — Keyword Match — check whether any phrase from a skill's
keywordslist appears verbatim (case-insensitive) in the prompt. - Pass 2 — Example Similarity — compare the prompt against each skill's
exampleslist as a semantic similarity check; pick the skill whose examples most closely reflect the intent of the prompt. - Pass 3 — Description Match — if no clear winner has emerged, compare the prompt against each
skill's
descriptionfield; pick the skill whose description best captures what the user is trying to do.
Tie-break (two or more skills score equally) — present the top candidates and ask the user to choose:
More than one skill matches your prompt. Which should I apply?
1. /<skill-a> — <one-line description>
2. /<skill-b> — <one-line description>
3. Neither — describe what you needNo match — inform the user and offer:
No matching skill found for: "<prompt>"
Would you like to:
1. Browse all available skills (/help)
2. Create a new skill for this use case (/btw)
3. Proceed without a skill (raw prompt)Invoke — deliver to the appropriate agent: if the matched skill specifies
metadata.prefered_agent, load that agent's definition from agents/<prefered_agent>.md and pass it
the full context; otherwise execute the skill's instructions directly. Do not summarise or restate the
matched skill's instructions — deliver them as-is.
Report — before invoking, briefly inform the user:
Routing to: /<matched-skill-name>
Reason: <one sentence explaining why this skill was chosen>Board items found/Docs found (opt-in, E53_S13_T01) — these two lines are appended to the
Report above, in this order, only when the natural-language branch's enrichment_requested is
true (i.e. the caller passed --enrich, per Phase 0.75's preamble):
Board items found: <count>
Docs found: <count><count> is enrich-nl-prompt.sh's board_items_found/docs_found field respectively (the total
match count before the top-5/top-3 cap, not the number of items actually listed in the enriched
prompt's Part B). When enrichment_requested is false — the default path, unchanged from before
this flag existed — neither line is emitted; the Report is exactly the two lines above and nothing
more.
Then proceed immediately — do not wait for user confirmation unless the match was ambiguous (the tie-break above already handled that).
Shared confirmation step (bare and scoped branches only)
- 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 theSTATE_FILE:path from STDERR. - Wait for the user's chat reply, then invoke
skills/jenga/scripts/render-confirmation.sh <state_file> "<raw_reply>"(continue mode).- Toggle or error turn (plain text on STDOUT, state file retained) — relay verbatim and return to step 2 for another reply.
- Cancellation — relay the cancellation acknowledgement and halt the entire
/jengarun; do not proceed to any later phase. - Confirmed (JSON object on STDOUT, state file removed) — this is the final scoped set. Take
resolved_ids(orresolved_ids_csv) as the exact set of board IDs Phases 1-4 restrict themselves to for the rest of this run.
- Handoff to cascade resolution — do not invoke
cascade-resolve.shagain here.render-confirmation.shalready invoked it internally to build the tree, and the CONFIRMED JSON's ownundecomposedfield is that same result already scoped down to the checked-only set. Use thatundecomposedfield directly to identify which epics/stories in the scoped set still need Phase 1/2 decomposition.
After this phase completes (bare and scoped branches via confirmation, wildcard branch immediately), proceed to Phase 1.
Phase 0.9 — Generate the Run's Shared orchestrator_session_id
This phase runs for every entry mode that reaches Phase 1 at all — bare, scoped, and wildcard (*) —
immediately after Phase 0.75 completes and before Phase 1 begins. It does not run for the
natural-language branch's playbook/single-skill-match outcomes (E53_S01/E53_S02), since those hand off
entirely to another skill or playbook and never reach Phase 1-4 of this document.
Generate exactly one orchestrator_session_id for this entire /jenga run, here and only here.
Both Phase 3.5's bundle dispatch and Phase 4's first (and every later) wave dispatch need this same
value, which is why it is generated once, early, before either call site runs.
Format. Generate it as jenga-<UTC ISO 8601 basic-format timestamp> (e.g.
jenga-20260917T162349Z) — a short prefix naming the orchestrating skill, plus a UTC timestamp, with no
/ or .. characters. This follows the same session-id string convention this repo already uses for
other orchestrator-minted session ids (e.g. /do's own dispatch sessions), and stays compatible with
scripts/acquire-concurrency-slot.sh's own validation, which refuses a <session_id> argument
containing a path separator or traversal sequence, because the value is used verbatim in filenames such
as project/queue/concurrency-slots-<session_id>.json.
Generate once, reuse everywhere in this run — never regenerate. This value MUST be reused, byte-for-byte unchanged, across:
- Phase 3.5 step 7b's bundle
/do <E##_S##>call. - Every wave dispatched by Phase 4 step 3, across every loop-back at Phase 4 step 5c. The loop-back never mints a new value — it only ever references the one value generated here.
This is what allows E32_S15's per-session max_concurrent_developers/max_concurrent_testers cap
(skills/j-do/SKILL.md's ### 4.4) to actually contend across an entire /jenga run's dispatches,
instead of every spawned /do sub-agent minting its own private orchestrator_session_id and getting
its own private, uncontended project/queue/concurrency-slots-<id>.json counter file — the root-cause
defect this story (E32_S16) fixes.
Phase 1 — Decompose Epics into Stories
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.
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.
Repeat until every in-scope Epic has at least one Story on the board.
Phase 2 — Decompose Stories into Tasks
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.
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.
Repeat until every in-scope Story has at least one Task on the board.
Phase 3 — Queue all Tasks into todo.md
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.
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.
After this phase, todo.md reflects the full set of in-scope work (or the full board, under *).
Phase 3.5 — Story-bundle detection
Before dispatching individual tasks in Phase 4, check each story for bundle eligibility. This phase runs once after Phase 3 completes.
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.
For each in-scope story that has one or more tasks listed in todo.md:
- Read the story file — parse the
tasks:frontmatter array to get the ordered list of task IDs. - 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. - Read each task file — for every task ID in the
tasks:list, read the corresponding task file fromproject/board/tasks/. - Collect
execution_scope— extract theexecution_scopefield from each task's YAML frontmatter. If the field is absent or has any value other thanstory, treat that task as not story-scoped. - Guard: locked-task disqualifier (defense-in-depth) — for each task file already read in step 3, also read
crucial_level(per$([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/templates/SCRUM_BOARD_SCHEMA.md)'s Crucial Flag Fields). If any task in the story'stasks:list hascrucial_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'sexecution_scopevalue, even if it already readsinline. This check is defense-in-depth alongside Phase 0.5's Rule 4 (which forces a locked task's ownexecution_scopetoinlinewhen 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'stasks:list after Rule 4 last ran, or the file was edited by hand after validation. It is not a replacement for Rule 4. - Apply the all-or-nothing rule — a story qualifies for the bundle path only if every task in its
tasks:list hasexecution_scope: story. A single task with a different scope (or a missing field) disqualifies the entire story. - Route bundle candidates — if all tasks in the story are
execution_scope: storyand the list is non-empty: a. Emit:
whereBUNDLE DETECTED: story <E##_S##> — <N> story-scoped tasks will execute as a bundle.<E##_S##>is the story ID and<N>is the count of tasks in the list. b. Call/do <E##_S##>once (with the story ID, not individual task IDs), passing this run's sharedorchestrator_session_id(generated once in Phase 0.9) into the bundle's sender object as itssession_idfield — a caller-supplied session id perskills/j-do/SKILL.md's standalone-vs-caller-supplied contract (### 4.4/### 5,E32_S16_T02), so the bundle path's own### 4.4slot 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. c. Mark these tasks as bundled — record their task IDs so Phase 4 skips individual dispatch for them. - Non-bundle stories — stories with a mixed scope, a zero-length task list, any task missing
execution_scope: story, or any task withcrucial_level: locked(step 5) use the normal per-task dispatch in Phase 4 without any change.
Phase 4 — Execute
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.
Break entry check (E44_S02). A hard break set before this run started must stop it before the first wave; the check in step 5 only fires after a wave completes. Once, before the first pass of step 1, and without consuming anything:
bash "$([ -f skills/j-break/scripts/check-break.sh ] && echo skills/j-break/scripts/check-break.sh || echo node_modules/@jenga-ai/agent/skills/j-break/scripts/check-break.sh)" --peekA mode=hard record (exit 10) → collect nothing, dispatch nothing, and print the break exit message from the Exit condition section with "nothing was started". Exit 0, a mode=soft record, or any other exit → continue. A soft break armed before the run is deliberately left in place and is honoured by the check in step 5 after the first wave. Like the rest of Phase 4 this prompts for nothing.
- Collect eligible items — from
todo.md, find all items whose board file hasstatus: Pendingand 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 leastIn ProgressorPassed. Also exclude every item in this run's deferred set (built in step 5b; empty on the first wave) — a unit/dodeferred at its pre-flight gate is back toPending, so without this exclusion it would be re-collected and re-dispatched every wave. A deferreditem_idmay be a task id or, for a Story-Bundle, a story id: for a story id, exclude the story itself and every task belonging to that story (its member tasks must not be re-collected individually either). This exclusion is run-scoped: it applies on every wave of this run, not only the next one, and is never cleared mid-run. - 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.
- Invoke
/doin 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 sharedorchestrator_session_id(generated once in Phase 0.9, unchanged across every wave) into each sub-agent's sender object as itssession_idfield, perskills/j-do/assets/sender_template.json's existing shape andskills/j-do/SKILL.md's caller-supplied-session-id case (### 4.4/### 5,E32_S16_T02) — this is what letsE32_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. - Mark In Progress — update
status: In Progressin each launched item's board file (YAML front-matter) immediately after launch. Wait, drain, and loop — once all active background agents in the wave have completed: a. Drain the scrum triggers queue — invoke the
## Drain Scrum Triggers Queueprocedure fromagents/scrum-master.mdagainstproject/queue/scrum_triggers.jsonl./jenga's orchestrating agent is the scrum-master, and this is the same session-start procedure applied mid-run: process anyrapport_review,status_review, andstory_rolluptriggers 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. a.1. Task-completion cleanup (E37_S05_T02) — Phase 4 dispatches its background agents directly and never passes through/do's own step 7, so nothing else removes a finished task's entry fromproject/todo.md. For each task dispatched in this wave by step 3, read its board file; if itsstatusis terminal (Passed,Passed with remarksorFailed), run the Task-Completion Cleanup Procedure (skills/j-do/SKILL.md### 7.1,E37_S05_T01) with that task's title exactly as its entry appears intodo.md, from this orchestrating checkout, immediately — the tester has already written the status by the time the wave completes, so do not defer this to a later wave or session. Rules:- Only terminal statuses. A task still
Pending(for example a pre-flight deferral recorded in (b)),In Progress,BlockedorRejectedkeeps itstodo.mdentry. - Tasks bundled in Phase 3.5 are out of scope here — they were not dispatched by step 3 (the bundle path is wired separately by
E37_S05_T03). - "Already removed" is benign.
todo_manager.sh removeexits non-zero when no line matches, and a wave sub-agent that ran/doend to end may already have run its own step 7. Treat that no-match as success and still run the procedure'steardownstep. Any other non-zero exit is surfaced to the user as a warning; it does not stop the run. - This reuses
scripts/todo_manager.shthrough the shared procedure only — no new queue file, lock or script — and leaves/do's own standalone loop unchanged. b. Record pre-flight deferrals — before the loop-back in step c below, runskills/jenga/scripts/list-preflight-deferred.sh <orchestrator_session_id>with this run's sharedorchestrator_session_idand add every id it prints to the run-scoped deferred set (a set, so re-adding is harmless; start the run with it empty). The script lists theitem_idof eachpreflight_deferredevent/dostep 4.1.2 appended for this session — a unit whose pre-flight checklist needs a user decision that a background sub-agent cannot give. Do not retry such a unit and do not count waves: a deferral needs a human, and re-dispatching cannot resolve it, so step 1 simply stops collecting it for the rest of the run. Run this on every wave, immediately after (a) and before (c), not only the first, so a deferral that appears in a later wave is excluded from the wave after it. A non-zero exit from the script is a genuine error (for example a malformedevents.json): surface its stderr message to the user and stop the run rather than looping on an unknown set. b.1. Break check (E44_S02) — the wave's agents have all completed, the queue drain in (a) has recorded its rollups, and this wave's deferrals are recorded, so the board reflects reality at the moment of any stop. Order is explicit: drain (a), then clean uptodo.md(a.1), then record deferrals (b), then check (b.1), then loop (c). Run:
bash "$([ -f skills/j-break/scripts/check-break.sh ] && echo skills/j-break/scripts/check-break.sh || echo node_modules/@jenga-ai/agent/skills/j-break/scripts/check-break.sh)"- Exit
0— no break. Continue to (c), unchanged. - Exit
10— a break was requested. The record is on stdout asmode=<soft|hard> with_status=<true|false> requested_at=<ts> requested_by=<who>; asoftbreak was consumed by that call, ahardbreak remains. Do not collect the next batch and do not return to step 1. Print the break exit message from the Exit condition section and stop. - Any other exit — the checker failed, which is not a stop request. Print its stderr as a warning and continue to (c).
The hands-free contract holds.
/jengatakes no user input by design; a break halts and reports. It does not prompt, offer options or wait for a decision. Never read the flag file here — branch on the exit code and use the record the script prints.c. Return to step 1 of this phase — the final sub-step of the wave, reached only after steps b and b.1 above have recorded this wave's deferrals and found no break — 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_idgenerated once in Phase 0.9 for every subsequent wave's/dodispatches — it is never regenerated here or anywhere else in this phase.- Only terminal statuses. A task still
Exit condition
There are two distinct exits. Do not confuse them: the first means the board is exhausted, the second means work remains.
Break exit (E44_S02) — entered from Phase 4's entry check or step 5's b.1 when a break was requested. It must not claim completion. Print:
⏹ Jenga stopped at a requested break (<soft|hard>). Work remains.
Completed in the last wave: <ids, or "none — nothing was started">
Eligible, not started: <ids, or "none">
Resume: re-run /jenga (soft break — already cleared)
/break clear, then /jenga (hard break — still in place)"Completed in the last wave" is the items that finished in the wave that just ended; "Eligible, not started" is what step 1 would have collected next. When the record says with_status=true, append the scoped rapport by running bash "$([ -f skills/j-break/scripts/render-execution-status.sh ] && echo skills/j-break/scripts/render-execution-status.sh || echo node_modules/@jenga-ai/agent/skills/j-break/scripts/render-execution-status.sh)" and printing its output verbatim; if it fails, say it is unavailable and why — never improvise one from the board. If the run-scoped deferred set is non-empty, append the same ⏸ Deferred at the pre-flight gate block shown below. This exit prompts for nothing.
Completion exit — when no eligible candidates remain in Phase 4, exit and output:
✅ Jenga complete. All eligible tasks have been started.First run Phase 4 step 5b's script and add every id it prints to the run-scoped deferred set — once, even if no Phase 4 wave ever ran — so a deferral from the final wave, or from Phase 3.5's bundle /do call (a Story-Bundle deferred at Phase 3.5 step 7b, with Phase 4 dispatching nothing), is included. If the run-scoped deferred set (Phase 4 step 5b) is then empty, that is the whole output, unchanged. If it is non-empty, append one block after that line listing each deferred unit (a task id, or a story id for a Story-Bundle) and why:
⏸ Deferred at the pre-flight gate (not started): <id>[, <id>...]
Each needs a user decision on a pre-task checklist item. Resolve it, then re-run /jenga to pick it up again.Edge Cases
- Epic with no stories after breakdown — log a warning and continue to the next Epic; do not block the pipeline.
- Story with no tasks after breakdown — log a warning and continue to the next Story.
- Task already in
todo.md— skip; do not duplicate. - All tasks in
todo.mdalready In Progress/Passed — exits cleanly with the completion message. - Unresolved dependencies — item is skipped in Phase 4 until its blockers are at least
In Progress. /dodefers a unit at its pre-flight gate (E67_S03_T06) — the unit is back toPendingwith apreflight_deferredevent logged. Phase 4 step 5b adds itsitem_id(a story id for a Story-Bundle, which also excludes that story's member tasks) to the run-scoped deferred set, and step 1 excludes it on every later wave, so it is dispatched at most once per run. The Exit condition lists it; it is not an error and does not trigger the/dofailure handling below./dofailure (background agent) — treated as a skip; mark the item's status back toPendingand continue the loop with remaining candidates.- Story with zero tasks (empty
tasks:list) — does not enter the bundle path in Phase 3.5; tasks (if any appear intodo.mdindependently) are dispatched normally in Phase 4. - Story with mixed
execution_scopevalues — falls back entirely to per-task dispatch in Phase 4; no partial bundling occurs. - Task file missing
execution_scopefield — treated as not story-scoped; the containing story is disqualified from the bundle path. - Story containing a
crucial_level: lockedtask — disqualified from the bundle path at Phase 3.5 step 5, independent of that task'sexecution_scope; falls back to per-task dispatch in Phase 4, where/doSection 4.2's locked-task dispatch guard (E39_S03_T04) provides the second enforcement layer before any worktree or subagent is created. - Bundle
/docall failure — treated as a skip for the entire bundle; mark all bundled tasks' status back toPendingand continue Phase 4 with remaining non-bundled candidates. - Picker cancelled (bare branch) — the entire
/jengarun halts immediately after relaying the cancellation acknowledgement; no phase past 0.75 runs, and nothing on the board is modified. - Confirmation cancelled (bare or scoped branch) — same as picker cancellation: the entire
/jengarun halts immediately; no scoped set is produced and no later phase runs. detect-nl-intent.shclassifies the argument asmixed(scoped branch) — the whole invocation halts at Phase 0.75 with each rejected segment'sinput/reasonreported verbatim, perdetect-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.detect-nl-intent.shclassifies the argument asnl_intent, no confident single-skill match, andmatch-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 the Skill Matching & Invocation Contract's 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.detect-nl-intent.shclassifies the argument asnl_intent, no confident single-skill match, andmatch-playbook.shreturns an ambiguous multi-way tie between playbooks — treated the same as the no-playbook-match case above: falls through to the Skill Matching & Invocation Contract's 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 ownambiguousresult — a tie between playbooks — is intentionally not given its own separate disambiguation UI; it is treated identically tono_matchand routed to the same fallback prose, which already has its own tie-break handling.)match-playbook.shreturnsplaybook_matchand the user confirms the full chain, and every step succeeds — the Natural-language branch's step 5e reports the fullcompletedANDskippedsteps 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).match-playbook.shreturnsplaybook_matchbut the user cancels at the chain confirmation step (step 5c) — identical posture to the existing picker/confirmation cancellation cases above: the entire/jengarun halts immediately after relaying the cancellation acknowledgement, with NO step of the chain executed; nothing on the board is modified by this invocation.match-playbook.shreturnsplaybook_match, the user confirms, and a step mid-chain fails — the Natural-language branch's step 5e(vi) halts immediately onrun-playbook-step.sh'shaltedresult: 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.- A step's conditional predicate evaluates false (
E53_S04_T02) — the Natural-language branch's step 5e(i) never invokes that step at all; it callsadvance <state_file> skippeddirectly, the step is recorded under the run'sskippedlist (nevercompleted, neverfailed), and the chain continues to the next step exactly as it would after apassedstep — a skipped step never halts the chain and is not narrated to the user as a separate turn, only reflected in the finalcompleted/skippedsummary (or thehaltedreport'sskippedfield, if a later step fails). - A
skippedstep is later named by aforward_from— the Natural-language branch's step 5e(ii) callsget-outputfor the named source before invoking the dependent step; a skipped source returns{"status": "unavailable", "reason": "step_skipped"}. This is the documented, non-silent failure mode (E53_S04_T06's fixture coverage): the calling agent does NOT guess a fallback value or silently forward an empty string — it callsadvance <state_file> failed "forward_from source '<name>' unavailable (<reason>)"and the chain halts with ahaltedreport naming the dependent step asfailed_step, exactly as any other step failure would. - A
resolvestep carries noforward_from(E53_S06_T01) — the Natural-language branch's step 5e(ii) treats this as a defined no-op: there is no forwarded value to reshape, so theresolvefield is simply ignored and the step is invoked normally with whatever input it would otherwise have received. This is never surfaced to the user as an error or a warning. - A
resolvestep's transform cannot cleanly produce a usable result (E53_S06_T01) — the Natural-language branch's step 5e(ii) never invokes the target step and never guesses or passes through a differently-shaped value; it callsadvance <state_file> failed "<note>"with a note that includes the raw pre-transform value, and the chain halts exactly as any other step failure would (5e-vi) — no silent skip-ahead, no fallback value. - A playbook step carries a
conditional, shown at confirmation (E53_S04_T04) — the Natural-language branch's step 5b relaysrender-playbook-confirmation.sh's rendered chain, which visibly marks that step's line with "(may be skipped depending on step N's result)" — confirming a chain with a conditional step never hides its real conditional structure from the user, even though checking/unchecking that step still works exactly like any other step (the marker is display-only; the actual runtime skip decision belongs entirely toshould-skip, independent of what the user checks or unchecks here). - The user unchecks, at confirmation, the specific step a later step's conditional depends on — the Natural-language branch's step 5d drops that conditional before calling
init(itsdepends_onstep is no longer in the confirmed list, andinitwould otherwise reject the conditional outright as naming a nonexistent earlier step). The dependent step becomes unconditional for this run and always executes — a deliberate, documented fallback rather than a hard failure over the user's own edit. - A playbook contains a cyclic
{"playbook": "<id>"}reference (E53_S05_T01) — rejected entirely atload-playbooks.shload time, before/jengaever runs: the whole cyclic playbook is dropped from the catalog with a stderr warning naming the cycle. It is never surfaced as amatch-playbook.shcandidate at all (a dropped playbook simply doesn't exist in the catalogmatch-playbook.shmatches against) — there is no runtime-visible error for this case, only a load-time one a human reviewing stderr output would see. - A playbook's composition nests deeper than the configured
max_composition_depth(E53_S05_T01, default 3) — same posture as the cyclic-reference case above: dropped atload-playbooks.shload time with a stderr warning, never surfaced as amatch-playbook.shcandidate, no runtime-visible error. - A
forward_fromcrosses a composition boundary (E53_S05_T02) — transparent in both directions with no special handling required anywhere in this SKILL.md:load-playbooks.sh's composition resolution runs before itsforward_from/conditionalvalidation, so by the time a playbook reachesmatch-playbook.sh's catalog, itsstepsarray is already fully flattened — a step from a composed/nested playbook forwarding from (or being forwarded into by) a step outside it behaves exactly like any otherforward_fromrelationship in step 5e(ii); the Natural-language branch never needs to know or care which originating playbook a step came from. - A playbook step carries composition origin metadata (depth > 1), shown at confirmation (
E53_S05_T03) — the Natural-language branch's step 5b relaysrender-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. /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.- Stale out-of-scope story queued in
todo.mdfrom 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 intodo.mduntil a future run's scope includes it. orchestrator_session_idreuse 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/docall and into every wave's/dosub-agent sender objects dispatched by Phase 4, for the entire duration of a single/jengarun; Phase 4 step 5c'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.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

