@jenga-ai/agent 3.4.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.
- package/README.md +85 -78
- package/agents/developer.md +1 -1
- package/agents/scrum-master.md +20 -2
- package/agents/tester.md +3 -3
- package/hooks/on_session_end.sh +5 -5
- package/lib/generate-agent-context.js +2 -2
- package/lib/generate-copilot-hooks.js +1 -1
- package/lib/generate-skill-allow-list.js +37 -3
- package/lib/mirror.js +1 -1
- package/lib/postinstall-manifest.js +1 -1
- package/lib/skill-allow-list.json +2 -2
- package/mcp/help/index.js +8 -17
- package/mcp/help/scan.js +73 -0
- package/package.json +6 -1
- package/project/app/api/parsers/knowledge-graph.js +100 -9
- package/project/app/api/routes/health.js +36 -0
- package/project/app/api/scripts/capture-snapshot.js +9 -6
- package/project/app/ui/dist/assets/{index-CdK3Qrep.css → index-BVR_7Owg.css} +1 -1
- package/project/app/ui/dist/assets/index-CtU2xLQm.js +104 -0
- package/project/app/ui/dist/index.html +2 -2
- package/project/app/ui/package.json +4 -0
- package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
- package/scripts/acquire-concurrency-slot.sh +1 -1
- package/scripts/apply-j-prefix.sh +46 -5
- package/scripts/audit-twin-divergence.sh +73 -5
- package/scripts/build-pages-site.sh +1 -1
- package/scripts/check-public-playbook-steps.sh +158 -52
- package/scripts/check-publicignore-match.sh +2 -2
- package/scripts/compute-deploy-reconcile.sh +5 -5
- package/scripts/delete-bare-skill-dirs.sh +330 -0
- package/scripts/generate-legacy-shipped-paths.js +2 -2
- package/scripts/idea_manager.sh +258 -3
- package/scripts/mark-deployed.sh +2 -2
- package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
- package/scripts/populate-knowledge-graph.js +213 -5
- package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
- package/scripts/postinstall.js +1 -1
- package/scripts/repoint-skill-refs.sh +539 -0
- package/scripts/todo_manager.sh +1 -1
- package/scripts/verify-legacy-seed-reconcile.sh +10 -10
- package/scripts/verify-postinstall-reconcile.sh +7 -7
- package/scripts/write-context-digest.sh +1 -1
- package/skills/j-clearify/SKILL.md +2 -2
- package/skills/j-close-story/scripts/check-privatized.sh +4 -4
- package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
- package/skills/j-do/SKILL.md +101 -17
- package/skills/j-doc-sync/SKILL.md +1 -0
- package/skills/j-gitignore/SKILL.md +157 -0
- package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
- package/skills/j-gitignore/scripts/_catalog.sh +105 -0
- package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
- package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
- package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
- package/skills/j-idea/SKILL.md +78 -6
- package/skills/j-idea/assets/idea_template.md +1 -1
- package/skills/j-improve/SKILL.md +1 -1
- package/skills/j-init/SKILL.md +53 -14
- package/skills/j-init/assets/.gitignore_template +1 -2
- package/skills/j-init/assets/scope-thresholds_template.json +5 -2
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
- package/skills/j-init/scripts/init.sh +22 -8
- package/skills/j-playbook/SKILL.md +1 -1
- package/skills/j-publish/SKILL.md +1 -1
- package/skills/j-publish/adapters/npm-ci.md +6 -1
- package/skills/j-publish/adapters/npm.md +1 -1
- package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
- package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
- package/skills/j-reconcile/SKILL.md +2 -2
- package/skills/j-reconcile/scripts/detect-unlinked-code.sh +11 -11
- package/skills/j-redo/SKILL.md +1 -1
- package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
- package/skills/j-spinoff/SKILL.md +1 -1
- package/skills/j-status/SKILL.md +15 -0
- package/skills/j-todo/SKILL.md +3 -1
- package/skills/j-uncharted/SKILL.md +55 -8
- package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
- package/skills/j-uncharted/scripts/detect-dependencies.sh +1 -1
- package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
- package/skills/j-uncharted/scripts/elicitation-state.sh +46 -8
- package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
- package/skills/j-wtf/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +43 -9
- package/skills/jenga/playbooks/board-hygiene.json +32 -0
- package/skills/jenga/playbooks/schema.json +73 -6
- package/skills/jenga/playbooks/understand-then-commit.json +19 -0
- package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
- package/skills/jenga/scripts/load-playbooks.sh +23 -11
- package/skills/jenga/scripts/match-playbook.sh +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
- package/templates/SKILL_TEMPLATE.md +12 -0
- package/templates/permission-levels/level-4-elevated.json +1 -1
- package/templates/permission-levels/level-5-unrestricted.json +1 -1
- package/templates/playbook-types.json +34 -6
- package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
- package/scripts/generate-j-alias.sh +0 -333
- package/skills/j-dev-done/SKILL.md +0 -53
- package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
# <producer> | write-context-digest.sh --agent <agent> --session-id <id> --task-id <id>
|
|
33
33
|
#
|
|
34
34
|
# If neither --content nor --content-file is given, digest content is read
|
|
35
|
-
# from stdin (mirrors skills/uncharted/scripts/import-source.sh's snippet
|
|
35
|
+
# from stdin (mirrors skills/j-uncharted/scripts/import-source.sh's snippet
|
|
36
36
|
# convention).
|
|
37
37
|
#
|
|
38
38
|
# Options:
|
|
@@ -26,7 +26,7 @@ This skill is a literal-directory-name duplicate of `skills/clearify/`. It exist
|
|
|
26
26
|
|
|
27
27
|
This file is generated/synced by `scripts/generate-j-alias.sh clearify` from `skills/clearify/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
|
|
28
28
|
|
|
29
|
-
> **Note on the `alias: wtf` frontmatter field:** this repo has no runtime mechanism that reads an `alias` key to route slash commands — no existing `SKILL.md` implements one, and `project/configs/workflow.json` has no alias registry. The field here documents the intended relationship only. `/wtf` is made invocable as a working alias by the companion skill folder at `skills/wtf/SKILL.md`, which delegates to these same instructions.
|
|
29
|
+
> **Note on the `alias: wtf` frontmatter field:** this repo has no runtime mechanism that reads an `alias` key to route slash commands — no existing `SKILL.md` implements one, and `project/configs/workflow.json` has no alias registry. The field here documents the intended relationship only. `/wtf` is made invocable as a working alias by the companion skill folder at `skills/j-wtf/SKILL.md`, which delegates to these same instructions.
|
|
30
30
|
|
|
31
31
|
## Instructions
|
|
32
32
|
|
|
@@ -56,4 +56,4 @@ This file is generated/synced by `scripts/generate-j-alias.sh clearify` from `sk
|
|
|
56
56
|
## Examples
|
|
57
57
|
- `/clearify` — Clarify the most recent user message / relevant conversation context
|
|
58
58
|
- `/clearify <pasted text>` — Clarify the attached text directly
|
|
59
|
-
- `/wtf` — Alias for `/clearify`, invoked via the companion skill in `skills/wtf/`
|
|
59
|
+
- `/wtf` — Alias for `/clearify`, invoked via the companion skill in `skills/j-wtf/`
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
# Static (run-independent) `.publicignore` membership check for a single
|
|
6
6
|
# board ticket, invoked by /close-story at both task granularity (Step 2's
|
|
7
7
|
# per-task loop) and story granularity (Step 4's finalization), per
|
|
8
|
-
# E51_S03_T03. Unlike `Merged` (skills/self-sync/scripts/mark-merged.sh) and
|
|
9
|
-
# `Publicized` (skills/mirror-public/scripts/mark-publicized.sh, E51_S03_T02),
|
|
8
|
+
# E51_S03_T03. Unlike `Merged` (skills/j-self-sync/scripts/mark-merged.sh) and
|
|
9
|
+
# `Publicized` (skills/j-mirror-public/scripts/mark-publicized.sh, E51_S03_T02),
|
|
10
10
|
# which both react to a specific run's file diff, `Privatized` has NO
|
|
11
11
|
# dependency on any run ever having occurred -- it is a pure blocklist
|
|
12
12
|
# membership test against the ticket's own derived touched-file list, per
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
# write target.
|
|
26
26
|
#
|
|
27
27
|
# Touched-file derivation -- IDENTICAL technique to
|
|
28
|
-
# skills/self-sync/scripts/mark-merged.sh (reused, not reinvented, per this
|
|
28
|
+
# skills/j-self-sync/scripts/mark-merged.sh (reused, not reinvented, per this
|
|
29
29
|
# task's own description):
|
|
30
30
|
# 1. git log --all --no-merges --extended-regexp \
|
|
31
31
|
# --grep="<id>([^0-9_]|$)" --pretty=format:'%H'
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
# 4. Zero matched commits -> nothing to check -> UNCHANGED (not an error).
|
|
42
42
|
#
|
|
43
43
|
# .publicignore matching -- ported (not sourced) from
|
|
44
|
-
# skills/mirror-public/scripts/mirror.sh's `_publicignore_rule_for` helper:
|
|
44
|
+
# skills/j-mirror-public/scripts/mirror.sh's `_publicignore_rule_for` helper:
|
|
45
45
|
# directory-prefix match for trailing-"/" lines, glob match against the full
|
|
46
46
|
# relative path and the basename for everything else, skipping comments,
|
|
47
47
|
# blank lines, and "+"-prefixed include lines (which never block anything).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# jenga.config.json — Schema Reference
|
|
2
2
|
|
|
3
|
-
This document is the canonical reference for the `jenga.config.json` file written into **consuming projects** during framework distribution. The file is created and maintained by `distribute-changes.sh`, with the `project_files_visibility` field written by `skills/init/scripts/apply-project-visibility.sh` during `/init`; it should not be edited by hand.
|
|
3
|
+
This document is the canonical reference for the `jenga.config.json` file written into **consuming projects** during framework distribution. The file is created and maintained by `distribute-changes.sh`, with the `project_files_visibility` field written by `skills/j-init/scripts/apply-project-visibility.sh` during `/init` and the `scaffold_visibility` field written by `skills/j-init/scripts/apply-scaffold-visibility.sh`, also during `/init`; it should not be edited by hand.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -28,7 +28,8 @@ This document is the canonical reference for the `jenga.config.json` file writte
|
|
|
28
28
|
"updated_at": "2026-08-11",
|
|
29
29
|
"last_distributed": "2026-08-11T10:00:00Z",
|
|
30
30
|
"source": "private",
|
|
31
|
-
"project_files_visibility": "visible"
|
|
31
|
+
"project_files_visibility": "visible",
|
|
32
|
+
"scaffold_visibility": "visible"
|
|
32
33
|
}
|
|
33
34
|
```
|
|
34
35
|
|
|
@@ -44,7 +45,8 @@ This document is the canonical reference for the `jenga.config.json` file writte
|
|
|
44
45
|
| `updated_at` | string (ISO 8601 date) | yes | — | Date of the last successful distribution, in `YYYY-MM-DD` format. Does **not** include a time component. |
|
|
45
46
|
| `last_distributed` | string (ISO 8601 datetime) | yes | — | Full UTC timestamp of the last successful distribution, in `YYYY-MM-DDTHH:MM:SSZ` format. Provides more precision than `updated_at` and is useful for audit and ordering purposes. |
|
|
46
47
|
| `source` | string | yes | `"private"` | Distribution channel. Always `"private"` for projects that receive updates via the filesystem distribution mechanism. Distinguishes these projects from any future npm-installed consumers. Do not change this value manually. |
|
|
47
|
-
| `project_files_visibility` | string (enum) | no | `"visible"` | How Jenga AI's own working files appear in the consuming project. Exactly one of `visible` or `ignored` — no other value is accepted. Written by `/init`, not by distribution. See [Project files visibility](#project-files-visibility) below. |
|
|
48
|
+
| `project_files_visibility` | string (enum) | no | `"visible"` | How Jenga AI's own working files (`project/`) appear in the consuming project. Exactly one of `visible` or `ignored` — no other value is accepted. Written by `/init`, not by distribution. See [Project files visibility](#project-files-visibility) below. |
|
|
49
|
+
| `scaffold_visibility` | string (enum) | no | `"visible"` | How the distributed `.claude`/`.agents` framework scaffold appears in the consuming project. Exactly one of `visible` or `ignored` — no other value is accepted. A **distinct** flag from `project_files_visibility`, written by `/init`, not by distribution. See [Scaffold visibility](#scaffold-visibility) below. |
|
|
48
50
|
|
|
49
51
|
---
|
|
50
52
|
|
|
@@ -96,12 +98,87 @@ The default is **`visible`**, used whenever `/init` runs non-interactively or th
|
|
|
96
98
|
|
|
97
99
|
### Who writes it
|
|
98
100
|
|
|
99
|
-
The field is written during `/init` by `skills/init/scripts/apply-project-visibility.sh`, which also performs the corresponding on-disk change. The script merges the field into any existing `jenga.config.json` rather than overwriting the file, since `/init` normally runs before the first `/distribute` has created it.
|
|
101
|
+
The field is written during `/init` by `skills/j-init/scripts/apply-project-visibility.sh`, which also performs the corresponding on-disk change. The script merges the field into any existing `jenga.config.json` rather than overwriting the file, since `/init` normally runs before the first `/distribute` has created it.
|
|
100
102
|
|
|
101
103
|
Changing the value after the initial `/init` is not currently supported — there is no toggle or migration path. Re-running the applier with a different mode is not a supported upgrade route.
|
|
102
104
|
|
|
103
105
|
---
|
|
104
106
|
|
|
107
|
+
## Scaffold visibility
|
|
108
|
+
|
|
109
|
+
`scaffold_visibility` controls how the **distributed framework scaffold** — `.claude/` and
|
|
110
|
+
`.agents/`, the mirrored skill and agent definitions that make Jenga AI's own commands
|
|
111
|
+
(`/init`, `/do`, `/commit`, etc.) available inside a consuming project — appears in that
|
|
112
|
+
project's git history.
|
|
113
|
+
|
|
114
|
+
### Why this is a separate flag from `project_files_visibility`
|
|
115
|
+
|
|
116
|
+
This field was filed as new, adjacent scope under `E31_S07`, from a problem rapport
|
|
117
|
+
(`project/rapports/problems/E31_S05-consumer-scaffold-committed-despite-ignored-visibility.md`)
|
|
118
|
+
reporting that a consumer who selected `project_files_visibility: ignored` still had 342
|
|
119
|
+
files under `.claude/`/`.agents/` swept into their `/init` commit — `project_files_visibility`
|
|
120
|
+
never covered that tree in the first place.
|
|
121
|
+
|
|
122
|
+
`E31_S05`'s own Background section explicitly scoped `project_files_visibility` to the
|
|
123
|
+
`project/` working tree — the scrum board, `todo.md`, `queue/`, `rapports/`, and `logs/` —
|
|
124
|
+
and explicitly said this was **not** about "the distributed skill/agent definitions
|
|
125
|
+
themselves." Two ways to close the resulting gap were considered when `E31_S07_T01` made
|
|
126
|
+
this decision:
|
|
127
|
+
|
|
128
|
+
- **(a) Extend** `project_files_visibility`'s `ignored` mode to also gitignore `.claude/`
|
|
129
|
+
and `.agents/`. Rejected: this erases the documented boundary from `E31_S05` — a single
|
|
130
|
+
flag would then silently govern two trees with different lifecycles (`project/`
|
|
131
|
+
accumulates as the user works; `.claude/`/`.agents/` are overwritten wholesale by every
|
|
132
|
+
`/distribute` run or npm upgrade), and a consumer reading `ignored` for one tree would
|
|
133
|
+
have no way to tell it also silently applies to the other.
|
|
134
|
+
- **(b) Introduce a distinct, separately-named flag** (`scaffold_visibility`), chosen. Costs
|
|
135
|
+
one extra config field and one extra `/init` prompt, but keeps each flag's semantics
|
|
136
|
+
unambiguous and independently selectable — a consumer can, for example, keep `project/`
|
|
137
|
+
visible (to track their own board in git) while still excluding the vendored
|
|
138
|
+
`.claude/`/`.agents/` scaffold, or vice versa.
|
|
139
|
+
|
|
140
|
+
`scaffold_visibility` reuses `project_files_visibility`'s exact two-value enum and
|
|
141
|
+
semantics (same mental model, different target directories) rather than inventing new
|
|
142
|
+
vocabulary.
|
|
143
|
+
|
|
144
|
+
### Allowed values
|
|
145
|
+
|
|
146
|
+
Exactly two values are accepted. Any other value is rejected with a non-zero exit code.
|
|
147
|
+
|
|
148
|
+
| Value | On-disk effect |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `visible` | `.claude/`/`.agents/` are tracked and committed normally — unchanged from `/init`'s behavior before this field existed. |
|
|
151
|
+
| `ignored` | `.claude/` and `.agents/` are appended to the project's `.gitignore` (whichever of the two exist, or will later be created by `/distribute` or an npm install), so they exist on disk but are never committed. |
|
|
152
|
+
|
|
153
|
+
### Default
|
|
154
|
+
|
|
155
|
+
The default is **`visible`**, used whenever `/init` runs non-interactively or the user is
|
|
156
|
+
not prompted — this is an additive capability, not a change to the previously-unconditional
|
|
157
|
+
commit behavior. `visible` reproduces exactly what every `/init` run did before this field
|
|
158
|
+
existed: the scaffold is committed regardless of `project_files_visibility`'s value. An
|
|
159
|
+
absent field (a `jenga.config.json` written before this field existed) is also read as
|
|
160
|
+
`visible`.
|
|
161
|
+
|
|
162
|
+
### Who writes it
|
|
163
|
+
|
|
164
|
+
The field is written during `/init` by `skills/j-init/scripts/apply-scaffold-visibility.sh`,
|
|
165
|
+
which also performs the corresponding `.gitignore` change. Like
|
|
166
|
+
`apply-project-visibility.sh`, it merges the field into any existing `jenga.config.json`
|
|
167
|
+
rather than overwriting the file, and gitignores `.claude/`/`.agents/` unconditionally
|
|
168
|
+
(not only when they already exist on disk), so a scaffold created by a later
|
|
169
|
+
`/distribute` run or npm install is covered too — not only one already present at `/init`
|
|
170
|
+
time.
|
|
171
|
+
|
|
172
|
+
`init.sh` applies both `project_files_visibility` and `scaffold_visibility` before its
|
|
173
|
+
final `git add -A && git commit` step, so any `.gitignore` entries from either flag are
|
|
174
|
+
already in place — and therefore respected by `git add -A` — before the first commit is
|
|
175
|
+
made.
|
|
176
|
+
|
|
177
|
+
Changing the value after the initial `/init` is not currently supported, matching
|
|
178
|
+
`project_files_visibility`'s own limitation above.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
105
182
|
## First distribution
|
|
106
183
|
|
|
107
184
|
When `distribute-changes.sh` runs against a project for the first time and no `jenga.config.json` exists in the project root, the script creates the file from scratch. All six fields are populated using:
|
|
@@ -115,7 +192,7 @@ When `distribute-changes.sh` runs against a project for the first time and no `j
|
|
|
115
192
|
|
|
116
193
|
The directory referenced by `target_dir` is created if it does not already exist.
|
|
117
194
|
|
|
118
|
-
> **Known gap:** `distribute-changes.sh` rebuilds `jenga.config.json` from scratch on every run and emits only the six fields above, so a `project_files_visibility` value written by `/init` is dropped by the next `/distribute`. Making the rebuild preserve fields it does not own is tracked as follow-up work; until then, treat the on-disk layout (not the config field) as the source of truth for which mode a project is in.
|
|
195
|
+
> **Known gap:** `distribute-changes.sh` rebuilds `jenga.config.json` from scratch on every run and emits only the six fields above, so a `project_files_visibility` or `scaffold_visibility` value written by `/init` is dropped by the next `/distribute`. Making the rebuild preserve fields it does not own is tracked as follow-up work; until then, treat the on-disk layout (not the config field) as the source of truth for which mode a project is in.
|
|
119
196
|
|
|
120
197
|
---
|
|
121
198
|
|
package/skills/j-do/SKILL.md
CHANGED
|
@@ -13,19 +13,28 @@ examples:
|
|
|
13
13
|
- "implement the login feature"
|
|
14
14
|
- "work on the API endpoint"
|
|
15
15
|
- "j-do"
|
|
16
|
-
metadata:
|
|
16
|
+
metadata:
|
|
17
17
|
prefered_agent: developer
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# Do — Execute Scrum Board Tasks
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
`skills/j-do/` is the **canonical, hand-edited** directory for this skill, per CLAUDE.md's "The
|
|
23
|
+
Canonical Naming Contract" (the `E50` reopening of 2026-09-09, which promoted `skills/j-<name>/`
|
|
24
|
+
from generated twin to sole canonical form). The `j-` prefix is there for collision safety — a real
|
|
25
|
+
directory under a distinct name, so a host tool shipping its own same-named built-in command cannot
|
|
26
|
+
shadow it (Claude Code's native skill resolution is a literal-string, directory-name-based match; see
|
|
27
|
+
`docs/skill-authoring.md`'s "Invocation Convention").
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
> ⚠️ **Do not run `scripts/generate-j-alias.sh do` against this directory.** This file was
|
|
30
|
+
> previously generated from `skills/do/SKILL.md`, and carried a banner saying so. That relationship
|
|
31
|
+
> is inverted under the contract above: edits land here first, and `skills/do/` is the copy awaiting
|
|
32
|
+
> deletion by `E50_S15`. Regenerating would overwrite this file from the stale bare directory.
|
|
33
|
+
> CLAUDE.md states the same prohibition in general terms; this is the concrete instance of it.
|
|
25
34
|
|
|
26
35
|
## `--trivial` Flag
|
|
27
36
|
|
|
28
|
-
**Syntax:** `/do <id> --trivial` — a dispatch-time override, distinct from `/todo --trivial` (a creation-time flag documented in `skills/todo/SKILL.md`). Where `/todo --trivial` writes a brand-new task straight to `execution_scope: inline`, `/do <id> --trivial` overrides an **already-existing** task's `execution_scope` — whatever it currently is, including absent (legacy tasks with no execution-scope fields at all) — to `inline` at the moment it's dispatched. See `### 4.1.5. \`--trivial\` Dispatch-Time Override` below for the full mechanics.
|
|
37
|
+
**Syntax:** `/do <id> --trivial` — a dispatch-time override, distinct from `/todo --trivial` (a creation-time flag documented in `skills/j-todo/SKILL.md`). Where `/todo --trivial` writes a brand-new task straight to `execution_scope: inline`, `/do <id> --trivial` overrides an **already-existing** task's `execution_scope` — whatever it currently is, including absent (legacy tasks with no execution-scope fields at all) — to `inline` at the moment it's dispatched. See `### 4.1.5. \`--trivial\` Dispatch-Time Override` below for the full mechanics.
|
|
29
38
|
|
|
30
39
|
**No softer fallback tier — hard fallback to the full pipeline instead.** Unlike `light` scope (which sits between `inline` and `task`), `--trivial` always forces `inline` directly, with no intermediate tier to fall back to first. To compensate, a `--trivial`-forced run that fails `scripts/smoke-harness.sh` or shows detected scope creep mid-run automatically re-routes to the full `task` pipeline (worktree + developer + tester) via the same `#### Fallback to Full Task-Scope Pipeline` procedure the `light`-tier fallback uses — see `### 4.2`'s failure-handling branches and the shared Fallback subsection under `### 4.3`.
|
|
31
40
|
|
|
@@ -145,7 +154,11 @@ After acquiring the epic lock and before writing the bundle manifest, scan all o
|
|
|
145
154
|
|
|
146
155
|
2. **Invoke rollback anchor**: write a bundle manifest at `project/queue/bundle-<E##_S##>.json` before the first task executes. See "### Bundle Manifest and Rollback Anchor" below for the full procedure.
|
|
147
156
|
|
|
148
|
-
3. **
|
|
157
|
+
3. **Acquire a developer concurrency slot, then spawn one developer subagent** in one shared worktree named `bundle-<E##_S##>`. This is the only developer subagent for the entire story bundle — do not spawn additional subagents per task, and correspondingly this bundle acquires **exactly one** developer slot for its entire lifetime, never one per task inside it.
|
|
158
|
+
|
|
159
|
+
a. **Acquire.** Follow `### 4.4. Developer Concurrency Slot Enforcement` below, with `<id>` = this bundle's story id (`<E##_S##>`).
|
|
160
|
+
b. **On a full cap (`capacity_blocked` outcome)**: do not spawn the shared subagent. Undo the bundle setup already performed above so no orphaned state survives this aborted attempt: delete the bundle manifest (`rm -f project/queue/bundle-<E##_S##>.json`) and release the epic lock (`rm -f project/queue/epic-lock-<E##>.json`) — the same two cleanup actions the failure path below performs, since from the epic lock's perspective an aborted-before-it-started bundle is indistinguishable from a failed one. None of the bundle's tasks have had their status touched yet (the per-task `status: In Progress` write in step 4a below hasn't run), so there is nothing to revert to `Pending` — they are still whatever they were before this bundle attempt (normally `Pending` already). Stop here; `/jenga` Phase 4's normal Pending-rescan picks the story back up on a later wave once a slot frees up.
|
|
161
|
+
c. **On success**: proceed to spawn the shared subagent as before. Release this slot exactly once, when the entire bundle concludes — step 6 (success) or step 4d (failure) below — never per individual task inside the bundle.
|
|
149
162
|
|
|
150
163
|
4. **Execute tasks sequentially** in `tasks:` list order, within the same shared developer subagent context:
|
|
151
164
|
|
|
@@ -181,7 +194,7 @@ After acquiring the epic lock and before writing the bundle manifest, scan all o
|
|
|
181
194
|
|
|
182
195
|
1. **Extract expected files** from the task's frontmatter field `scope_rationale` and from the task's `## Description` section. Use a best-effort prose heuristic: split the text on whitespace and punctuation, then retain any token that either (a) contains a `/` character or (b) matches the pattern `*.*` (a dot surrounded by non-dot characters on both sides, e.g. `SKILL.md`, `foo.json`). Collect all retained tokens into a set called `expected_files`. This is intentionally permissive — false positives (expected files that were never actually changed) are acceptable and produce no report.
|
|
183
196
|
|
|
184
|
-
2. **Compute unexpected files**: let `actual_files` = the array stored at `task_changed_files[<task_id>]` in the bundle manifest (from step c.1). Compute `unexpected = actual_files − expected_files` (set difference: files in `actual_files` that have no match in `expected_files`). Matching is case-sensitive and exact against the relative path or the basename of the path — a token like `SKILL.md` matches any actual file whose basename is `SKILL.md` (e.g. `skills/
|
|
197
|
+
2. **Compute unexpected files**: let `actual_files` = the array stored at `task_changed_files[<task_id>]` in the bundle manifest (from step c.1). Compute `unexpected = actual_files − expected_files` (set difference: files in `actual_files` that have no match in `expected_files`). Matching is case-sensitive and exact against the relative path or the basename of the path — a token like `SKILL.md` matches any actual file whose basename is `SKILL.md` (e.g. `skills/do/SKILL.md`).
|
|
185
198
|
|
|
186
199
|
3. **If `unexpected` is non-empty**:
|
|
187
200
|
a. Write a Markdown conflict report to `project/queue/conflict-<task_id>.md` with the following structure:
|
|
@@ -222,14 +235,14 @@ After acquiring the epic lock and before writing the bundle manifest, scan all o
|
|
|
222
235
|
2. Create the lock file: write the current ISO 8601 timestamp into `project/board/tasks/<task_id>_*.md.lock`.
|
|
223
236
|
3. Update the task frontmatter field `status: Failed`.
|
|
224
237
|
4. Delete the lock file immediately after the write completes.
|
|
225
|
-
Then invoke the rollback anchor cleanup procedure (see "### Bundle Manifest and Rollback Anchor" — failure path). After the rollback anchor completes, **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`. Mark the bundle as failed and **halt** — do not execute any remaining tasks in the sequence.
|
|
238
|
+
Then invoke the rollback anchor cleanup procedure (see "### Bundle Manifest and Rollback Anchor" — failure path). After the rollback anchor completes, **release the developer concurrency slot** acquired in step 3 above: `scripts/release-concurrency-slot.sh developer <E##_S##> <orchestrator_session_id>` (per `### 4.4` step 2 below). Then **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`. Mark the bundle as failed and **halt** — do not execute any remaining tasks in the sequence.
|
|
226
239
|
|
|
227
240
|
5. **Post-bundle verification** (runs only if all tasks completed without failure):
|
|
228
241
|
- Inspect each task in the bundle for `needs_docs: true` in its frontmatter.
|
|
229
242
|
- **If any task has `needs_docs: true`**: invoke the tester agent once for the full story. Pass the story ID, the list of all task IDs in the bundle, and the shared worktree path. The tester is responsible for updating individual task statuses.
|
|
230
243
|
- **If all tasks have `needs_docs: false`**: the developer agent self-verifies — reviews each task's implementation against its acceptance criteria without invoking the tester. After self-verification passes, write `status: Passed` and `date_completed: <YYYY-MM-DD>` (today's date) to each task file using the file-locking protocol (steps c.1–c.4 above). No tester invocation occurs.
|
|
231
244
|
|
|
232
|
-
6. **Cleanup**: on successful bundle completion, invoke the rollback anchor success path (see "### Bundle Manifest and Rollback Anchor" — success path) to delete the bundle manifest. Then **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`.
|
|
245
|
+
6. **Cleanup**: on successful bundle completion, invoke the rollback anchor success path (see "### Bundle Manifest and Rollback Anchor" — success path) to delete the bundle manifest. Then **release the developer concurrency slot** acquired in step 3 above: `scripts/release-concurrency-slot.sh developer <E##_S##> <orchestrator_session_id>` (per `### 4.4` step 2 below). Then **release the epic lock**: run `rm -f project/queue/epic-lock-<E##>.json`.
|
|
233
246
|
|
|
234
247
|
### Bundle Manifest and Rollback Anchor
|
|
235
248
|
|
|
@@ -349,7 +362,7 @@ After override validation (step 4.1) and before branching on `execution_scope` i
|
|
|
349
362
|
```
|
|
350
363
|
override_justification: "/do --trivial dispatch-time override on <date>: execution_scope forced from '<prior_tier>' to 'inline' by human operator."
|
|
351
364
|
```
|
|
352
|
-
- Also set (or append to) `scope_rationale`, mirroring the phrasing convention `skills/todo/scripts/add_trivial_task.sh` already uses for the creation-time flag, so both audit fields agree on the prior tier:
|
|
365
|
+
- Also set (or append to) `scope_rationale`, mirroring the phrasing convention `skills/j-todo/scripts/add_trivial_task.sh` already uses for the creation-time flag, so both audit fields agree on the prior tier:
|
|
353
366
|
```
|
|
354
367
|
scope_rationale: "forced inline via /do --trivial (dispatch-time override); prior execution_scope was '<prior_tier>'"
|
|
355
368
|
```
|
|
@@ -428,9 +441,9 @@ After resolving the task context (step 4), passing override validation (step 4.1
|
|
|
428
441
|
|
|
429
442
|
`light` sits between `inline` and `task`: unlike `inline`, it spawns a real developer subagent (so it can handle small branching logic that inline's main-session execution isn't suited for); unlike `task`, it does not create a dedicated worktree and does not invoke the tester as a separate step.
|
|
430
443
|
|
|
431
|
-
1. **
|
|
444
|
+
1. **Acquire a developer concurrency slot, then spawn a developer subagent.** Acquire first, per `### 4.4. Developer Concurrency Slot Enforcement` below (`<id>` = this task's id). On a full cap (`capacity_blocked` outcome), do not spawn anything — `### 4.4` already reverts this task's status to `Pending`, logs the `capacity_blocked` event, and tracks the consecutive-block count; stop here and let `/jenga` Phase 4 retry this task on a later wave once a slot frees up. On a successful acquire, spawn the subagent (Agent tool, `subagent_type: "developer"`) with the same sender object and context payload as step 5 would use, but with an explicit instruction added to the dispatch prompt: **do not create a worktree** — implement directly against the current checkout (the session's existing working tree), not an isolated `.claude/worktrees/<slug>` copy. This is the one concrete difference from the step-5 `task` path: everything else about how the subagent implements the task (reading the task file, following acceptance criteria, following repo conventions) is unchanged.
|
|
432
445
|
|
|
433
|
-
2. **After the developer subagent reports implementation complete**, run the smoke test harness using the same invocation convention as `### 4.2. Inline Execution Path`:
|
|
446
|
+
2. **After the developer subagent reports implementation complete**, first **release the developer concurrency slot** acquired in step 1: `scripts/release-concurrency-slot.sh developer <task_id> <orchestrator_session_id>` (per `### 4.4` step 2) — this subagent's session has ended, so the slot is released now regardless of what it reports, before the smoke test result is even known. Then run the smoke test harness using the same invocation convention as `### 4.2. Inline Execution Path`:
|
|
434
447
|
- Run `bash "$([ -f scripts/smoke-harness.sh ] && echo scripts/smoke-harness.sh || echo node_modules/@jenga-ai/agent/scripts/smoke-harness.sh)" <changed_file>...`, passing the paths the subagent changed. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
|
|
435
448
|
- If neither `scripts/smoke-harness.sh` nor `node_modules/@jenga-ai/agent/scripts/smoke-harness.sh` exists, log a warning and treat the result as a pass:
|
|
436
449
|
```
|
|
@@ -453,9 +466,10 @@ This is a self-contained, reusable procedure with two current callers — `### 4
|
|
|
453
466
|
|
|
454
467
|
1. **Do not mark the task `Failed`.** A smoke-harness failure (or detected scope creep) under a reduced-overhead scope means the scope was too small for the task, not that the task itself is unworkable — the correct response is to retry under full isolation, not to reject the work.
|
|
455
468
|
2. **Create a worktree** for the task, named `<E##_S##_T##-short-slug>` per standard Worktree Management conventions, if one does not already exist for this task. (A task dispatched under `light` scope, or forced `inline` via `--trivial`, never had one — both premises skip worktree creation — so this step always creates a fresh worktree in that case.)
|
|
456
|
-
3. **
|
|
457
|
-
4. **
|
|
458
|
-
5. **
|
|
469
|
+
3. **Acquire a developer concurrency slot** (per `### 4.4. Developer Concurrency Slot Enforcement` below, `<id>` = this task's id). This fallback spawn is a distinct developer-subagent lifecycle from whatever `light`/`trivial` attempt preceded it — that attempt's own slot, if any, was already acquired and released around it (`### 4.3` step 1/2, or no slot at all for a `--trivial`-forced inline attempt, which never spawns a subagent) — so this step always acquires its own fresh slot. On a full cap (`capacity_blocked` outcome), do not spawn anything here either: apply `### 4.4`'s Pending-revert/log/consecutive-block handling for this task id and stop the fallback. The task remains exactly as the reduced-overhead attempt left it (any commits already made by that attempt stay in the worktree/branch just created in step 2), and `/jenga` Phase 4 retries it on a later wave once a slot frees up.
|
|
470
|
+
4. **Spawn a developer subagent** in that worktree and have it pick up from the current state of the code (the changes already made by the reduced-overhead attempt are still present in the working tree / already committed, if any commit occurred — the subagent continues from there rather than starting over).
|
|
471
|
+
5. **Invoke the tester agent** per the normal `### 5. Invoke the developer agent` flow's contract — full sender object, commit SHAs, worktree path. The tester is responsible for the terminal status write, exactly as in the standard `task`-scope pipeline. Once this developer subagent's session ends — tester-verified, failed, or errored — **release the slot** acquired in step 3: `scripts/release-concurrency-slot.sh developer <task_id> <orchestrator_session_id>` (per `### 4.4` step 2).
|
|
472
|
+
6. **Emit a clear, non-fatal fallback notice** to the user/orchestrator, using the message matching the caller's origin:
|
|
459
473
|
- origin `light`:
|
|
460
474
|
```
|
|
461
475
|
LIGHT SCOPE FALLBACK [<task_id>]: smoke test failed; re-routing to full task-scope pipeline (worktree + developer + tester).
|
|
@@ -464,12 +478,80 @@ This is a self-contained, reusable procedure with two current callers — `### 4
|
|
|
464
478
|
```
|
|
465
479
|
TRIVIAL OVERRIDE FALLBACK [<task_id>]: smoke test failed (or scope creep detected); re-routing to full task-scope pipeline (worktree + developer + tester).
|
|
466
480
|
```
|
|
467
|
-
|
|
481
|
+
7. Resume normal `task`-scope processing (steps 6–8 below) once the tester returns a verdict.
|
|
482
|
+
|
|
483
|
+
### 4.4. Developer Concurrency Slot Enforcement (Shared Procedure)
|
|
484
|
+
|
|
485
|
+
This is a self-contained, reusable procedure with four current callers — the Story-Bundle Execution Mode's developer-subagent spawn (`### 1.5` step 3, one acquire for the whole bundle, never one per task inside it), the Light Execution Path's developer-subagent spawn (`### 4.3` step 1/2), the standard task-scope developer invocation (`### 5` below), and the `Fallback to Full Task-Scope Pipeline`'s developer-subagent spawn (step 3/5 above). Every one of these is a genuine "spawn a developer subagent" moment — `inline` scope (`### 4.2`, including a `--trivial`-forced run that hasn't yet fallen back) is the only path that never spawns a subagent and therefore never calls this procedure at all.
|
|
486
|
+
|
|
487
|
+
**`<id>`** is the task id (`E##_S##_T##`) for a single-task dispatch (the `### 5`, `### 4.3`, and Fallback call sites), or the story id (`E##_S##`) for the bundle path — one acquire call covers the whole bundle, never one per task inside it.
|
|
488
|
+
|
|
489
|
+
**`<orchestrator_session_id>`** is the `session_id` value already being populated into this dispatch's sender object per `assets/sender_template.json` (`### 5`'s "Sender object" step) — the same session identity already used for this invocation's sender object, handoff files, and event log entries. Reuse that exact value; do not fabricate a second, separate identifier for this call. This is what keeps every developer spawn issued by the same orchestrating run sharing one counter file (`project/queue/concurrency-slots-<orchestrator_session_id>.json`), regardless of which of the four call sites above triggered it.
|
|
490
|
+
|
|
491
|
+
**Where that value comes from — two cases (`E32_S16_T02`).** `### 5`'s "Sender object" step says to populate `session_id`, but does not by itself say where that value originates. It is always exactly one of the following two cases, decided once at the top of this `/do` invocation, before any of the four call sites above ever runs:
|
|
492
|
+
|
|
493
|
+
- **(a) Standalone invocation.** `/do` was invoked directly — by a human, or by another skill that did not pass along a session identity of its own (e.g. a bare `/do`, or `/do <id>` typed by a user). In this case `/do` generates its own `session_id`, exactly as it always has — this is no behavior change, only making the previously-implicit behavior explicit. Follow this repo's existing session-id string convention for an orchestrator-minted id (see `skills/jenga/SKILL.md` Phase 0.9's `jenga-<UTC ISO 8601 basic timestamp>` format for the same convention applied to an orchestrating skill) — a short prefix naming the invoking context plus a UTC timestamp, with no `/` or `..` characters, since the value is used verbatim in filenames such as this section's own counter file.
|
|
494
|
+
- **(b) Caller-supplied invocation.** `/do` was invoked by another orchestrator that already has its own session identity and passes it along as part of the dispatch context — for example `/jenga` Phase 4 threading its own `orchestrator_session_id` (generated once in `skills/jenga/SKILL.md` Phase 0.9) into each sub-agent's sender object, or Phase 3.5 step 7b's bundle `/do <E##_S##>` call doing the same. In this case `/do` **MUST reuse that caller-supplied value verbatim** as this invocation's `session_id` — it must never generate a new one. This is what lets every `/do` sub-agent a single `/jenga` run (or any other orchestrator) dispatches share one `project/queue/concurrency-slots-<id>.json` counter file, which is the entire point of `<orchestrator_session_id>` in this section.
|
|
495
|
+
|
|
496
|
+
Detecting which case applies is mechanical: if the dispatch context already contains a `session_id` populated by the calling skill/agent, this is case (b) and that value is used as-is; if it does not, this is case (a) and `/do` mints its own. Once decided, that single value is what gets copied into `assets/sender_template.json`'s `session_id` field in `### 5` below and referenced everywhere in this document as `<orchestrator_session_id>`.
|
|
497
|
+
|
|
498
|
+
1. **Acquire.** Before spawning the developer subagent (or the bundle's shared developer subagent), run:
|
|
499
|
+
```
|
|
500
|
+
scripts/acquire-concurrency-slot.sh developer <id> <orchestrator_session_id>
|
|
501
|
+
```
|
|
502
|
+
Per that script's own exit-code contract:
|
|
503
|
+
- **Exit `0`** — slot acquired. Proceed to spawn the subagent exactly as documented at the calling site, and carry out step 2 below (release) once that subagent's session ends.
|
|
504
|
+
- **Exit `3`** — the `developer` role is at/over its configured cap (`max_concurrent_developers`) after stale-reclaim; the counter file was **not** mutated. This, and only this exit code, is a **`capacity_blocked` outcome**. Do NOT spawn anything. Handle it as follows:
|
|
505
|
+
a. Revert the item's board status to `Pending`, using the same file-locking protocol already used elsewhere in this skill for status writes.
|
|
506
|
+
b. Read the current holders snapshot for the `developer` role from `project/queue/concurrency-slots-<orchestrator_session_id>.json` (`.developer.holders`). If the file does not exist yet, treat the snapshot as `{}`. Note this snapshot may still include not-yet-reclaimed stale (TTL-expired) entries, since a blocked acquire call never mutates the counter file — it is a best-effort audit snapshot, not a guaranteed-fresh one.
|
|
507
|
+
c. Read/update the per-item consecutive-block counter per step 3 below, and use the resulting count as this event's `wave` value.
|
|
508
|
+
d. Append one entry to `project/logs/events.json` (read the file's existing entries first to match its established array-of-objects shape):
|
|
509
|
+
```json
|
|
510
|
+
{
|
|
511
|
+
"event": "capacity_blocked",
|
|
512
|
+
"agent": "orchestrator",
|
|
513
|
+
"session_id": "<orchestrator_session_id>",
|
|
514
|
+
"role": "developer",
|
|
515
|
+
"item_id": "<id>",
|
|
516
|
+
"wave": <consecutive-block count from step 3, after incrementing>,
|
|
517
|
+
"holders": <the holders snapshot from (b)>,
|
|
518
|
+
"date": "<ISO 8601 UTC timestamp>"
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
e. Do not proceed to step 2 (release) — nothing was acquired. Report this outcome to the user/orchestrator as "deferred: developer capacity is currently full for this session", never as "failed".
|
|
522
|
+
- **Exit `1`, `2`, or `5`** — a usage error, a `with-lock.sh` timeout, or an environment error (missing `jq`, missing/invalid config). This is **not** a capacity block — the cap itself was never actually evaluated. Treat it as an ordinary `/do` error: do not spawn the subagent, do not write a `capacity_blocked` event, do not touch the consecutive-block counter, and fall through to the pre-existing failure→skip→`Pending` handling (`skills/jenga/SKILL.md`'s Edge Cases: "`/do` failure (background agent) — treated as a skip; mark the item's status back to `Pending`..."). Report the script's stderr so the underlying script/lock/environment problem is visible, rather than silently reporting it as a capacity issue.
|
|
523
|
+
|
|
524
|
+
2. **Release — on every subagent exit path.** Once the spawned developer subagent's session ends — whether it reports success, failure, or an error/exception — run:
|
|
525
|
+
```
|
|
526
|
+
scripts/release-concurrency-slot.sh developer <id> <orchestrator_session_id>
|
|
527
|
+
```
|
|
528
|
+
This call is unconditional and must be reached from every branch that follows a successful acquire in step 1: the success path, the ordinary `/do` failure→skip→`Pending` path, and any other error/exception branch that aborts the calling section early. `release-concurrency-slot.sh` is idempotent by its own contract (a no-op success if the holder entry is already absent), so calling it defensively — even in a code path where it is uncertain whether the acquire actually landed — is always safe and never an error.
|
|
529
|
+
|
|
530
|
+
3. **Per-item consecutive-block counter (across waves).** Maintain a small session-scoped state file at `project/queue/capacity-block-state-<orchestrator_session_id>.json`, shaped as a flat map from item id to its current consecutive-block count:
|
|
531
|
+
```json
|
|
532
|
+
{ "<id>": <int> }
|
|
533
|
+
```
|
|
534
|
+
This file lives alongside `project/queue/concurrency-slots-<orchestrator_session_id>.json` under the identical session-scoped naming convention — it belongs to exactly the one orchestrating session named in its filename and must never be read or written for a different `orchestrator_session_id`, for the same reason `scripts/acquire-concurrency-slot.sh`'s own header comment gives for its own counter file. Because `/do` is very often re-dispatched as a brand-new background subagent on every wave (per `/jenga` Phase 4's Pending-rescan loop), this state cannot rely on in-session memory alone — persisting it here is what lets the "3 consecutive blocked waves" count survive across those separate dispatches. Because multiple different items can each be blocked in the same wave, all writing to this one shared file, every read-modify-write against it must go through `scripts/with-lock.sh` (mirroring `project/queue/concurrency-slots-<session_id>.json`'s own locking discipline) rather than the bare tmp-file-rename pattern used for the (single-writer-at-a-time) bundle manifest elsewhere in this skill.
|
|
535
|
+
|
|
536
|
+
- **On a `capacity_blocked` outcome (step 1's exit-3 branch):** read this item's current count (default `0` if the key is absent), increment it by `1`, and write the incremented value back. Use this same incremented value as the `wave` field in the `capacity_blocked` event (step 1d above).
|
|
537
|
+
- **Escalation.** If the incremented count has reached `3`: append one line to `project/queue/scrum_triggers.jsonl`, matching the existing trigger shape documented in `templates/SCRUM_BOARD_SCHEMA.md`'s Queue Trigger Types section (the same `{"type": ..., ..., "sender": {...}, "message": "..."}` shape already used by `story_rollup` entries):
|
|
538
|
+
```json
|
|
539
|
+
{"type": "capacity_starvation", "item_id": "<id>", "wave": 3, "role": "developer", "date": "<ISO 8601 UTC timestamp>", "sender": {"agent": "orchestrator", "session_id": "<orchestrator_session_id>", "task_id": "<id>", "date": "<ISO 8601 UTC timestamp>"}, "message": "Item <id> has been capacity_blocked for 3 consecutive waves against the developer role cap (max_concurrent_developers)."}
|
|
540
|
+
```
|
|
541
|
+
Then **reset this item's count back to `0`** in the state file. This is a passive audit/escalation write only — it does not itself retry anything; retry keeps working purely via `/jenga` Phase 4's existing Pending-rescan-per-wave loop, unrelated to this trigger. `capacity_starvation` handling (surfacing a plain warning, no automatic remediation) is documented separately in `agents/scrum-master.md`'s Drain Scrum Triggers Queue procedure (`E32_S15_T06`) — out of scope for this file.
|
|
542
|
+
- **On a successful acquire (step 1's exit-0 branch):** if this item already has an entry in the state file, reset its count back to `0` (a successful dispatch clears the streak — the counter tracks *consecutive* blocked waves, not a cumulative total). If it has no entry, there is nothing to reset.
|
|
543
|
+
|
|
544
|
+
**`capacity_blocked` vs. the existing failure→skip→`Pending` path.** A `capacity_blocked` outcome and `/do`'s pre-existing failure handling (documented in `skills/jenga/SKILL.md`'s Edge Cases as "`/do` failure (background agent) — treated as a skip; mark the item's status back to `Pending` and continue the loop with remaining candidates.") arrive at the **same board-visible result** — the item ends up `Pending` again, picked back up by `/jenga` Phase 4's next wave rescan — but for a **different, never-conflated reason**:
|
|
545
|
+
- **Ordinary failure:** the developer subagent (or bundle subagent) was actually spawned and ran; either the implementation itself failed or an unexpected error occurred mid-run. No `capacity_blocked` event is written, and the consecutive-block counter above is untouched. Retry works purely because the item is `Pending` again — no concurrency accounting is involved.
|
|
546
|
+
- **`capacity_blocked`:** no subagent was ever spawned at all — the item never got a chance to run because the session's `developer` role was already at its configured cap (`max_concurrent_developers` in `project/configs/scope-thresholds.json`) at dispatch time. This is a passive audit write only: it does not itself drive retry, and it must never be reported to the user as "the task failed" — it is reported as "the task was deferred, developer capacity was full for this session."
|
|
468
547
|
|
|
469
548
|
### 5. Invoke the developer agent
|
|
470
|
-
Pass the following to the developer agent:
|
|
471
549
|
|
|
472
|
-
**
|
|
550
|
+
**Acquire a developer concurrency slot first.** Follow `### 4.4. Developer Concurrency Slot Enforcement` above with `<id>` = this task's id, before doing anything else in this section. On a full cap (`capacity_blocked` outcome), do not invoke the developer agent at all — `### 4.4` already reverts the task's status to `Pending`, logs the `capacity_blocked` event, and tracks the consecutive-block count for this task; simply stop here and let `/jenga` Phase 4 retry this task on a later wave once a slot frees up. This is a distinct outcome from the ordinary `/do` failure→skip→`Pending` path below — see `### 4.4`'s "capacity_blocked vs. the existing failure→skip→Pending path" note.
|
|
551
|
+
|
|
552
|
+
On a successful acquire, pass the following to the developer agent:
|
|
553
|
+
|
|
554
|
+
**Sender object**: Copy `assets/sender_template.json` and populate all known fields — `session_id` per `### 4.4`'s standalone-vs-caller-supplied cases immediately above (generate one if this is a standalone invocation, case (a); reuse the caller-supplied value verbatim if one was already passed into the dispatch context, case (b) — never regenerate in that case), plus `task_id`, `story_id`, `epic_id`, and the current ISO 8601 UTC date.
|
|
473
555
|
|
|
474
556
|
**Context payload** (plain text alongside the sender object):
|
|
475
557
|
- Full task/story file content (title, description, acceptance criteria)
|
|
@@ -482,6 +564,8 @@ The developer agent will:
|
|
|
482
564
|
- Implement, commit at milestones, and invoke the tester agent
|
|
483
565
|
- Return when the tester has verified the work
|
|
484
566
|
|
|
567
|
+
**Release the slot on every exit path.** Once the developer agent's session ends — whether it returns having implemented and been verified by the tester, fails outright, or errors — release the slot immediately: `scripts/release-concurrency-slot.sh developer <task_id> <orchestrator_session_id>` (per `### 4.4` step 2). This applies uniformly to the success path above, `/jenga`'s existing "`/do` failure (background agent)" skip-to-`Pending` path (`skills/jenga/SKILL.md`'s Edge Cases), and any other error/exception that aborts this section early.
|
|
568
|
+
|
|
485
569
|
### 5.1. Intent-vs-Diff Check (needs_docs: false only)
|
|
486
570
|
|
|
487
571
|
After the developer agent returns (or after inline execution completes), run the following check if the task has `needs_docs: false` in its frontmatter. If `needs_docs: true`, skip this check entirely — the full documentation lifecycle handles divergence detection for those tasks.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: j.doc-sync
|
|
3
3
|
description: Polyfill alias of the doc-sync skill under a collision-safe directory name. Identical behavior to /doc-sync — Compare the current state of a project with its documentation and update documentation to reflect changes. Accepts `update:`, `source:`, `exclude:`, and `minify:` arguments to control scope. Use when documentation may be out of date with implementation, or when the user asks to sync, refresh, update, or shrink docs. Use when the bare /doc-sync form is shadowed by another tool's own built-in command of the same name.
|
|
4
|
+
output_types: file_list
|
|
4
5
|
keywords:
|
|
5
6
|
- doc-sync
|
|
6
7
|
- documentation
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: j.gitignore
|
|
3
|
+
description: Retroactively repair an already-scaffolded project's Jenga gitignore state — strips the stray heredoc `EOF` line left by pre-fix /init scaffolds, adds or removes the Jenga-owned path entries via a managed block, and untracks those paths from git (and from origin) while leaving every file on disk.
|
|
4
|
+
keywords:
|
|
5
|
+
- gitignore
|
|
6
|
+
- fix gitignore
|
|
7
|
+
- untrack jenga files
|
|
8
|
+
- remove from origin
|
|
9
|
+
- stray EOF
|
|
10
|
+
- jenga files committed
|
|
11
|
+
- stop tracking scaffold
|
|
12
|
+
examples:
|
|
13
|
+
- "jenga files keep getting committed"
|
|
14
|
+
- "remove .claude and .agents from the repo but keep them locally"
|
|
15
|
+
- "my .gitignore has a stray EOF line"
|
|
16
|
+
- "untrack the jenga scaffold from origin"
|
|
17
|
+
- "j.gitignore"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# j.gitignore
|
|
21
|
+
|
|
22
|
+
Repairs the Jenga-related git state of a project that was **already scaffolded**. `/init` does this
|
|
23
|
+
correctly for new projects; this skill is the retroactive path for projects created before the fix,
|
|
24
|
+
or projects whose ignore rules have drifted.
|
|
25
|
+
|
|
26
|
+
## When to use this
|
|
27
|
+
|
|
28
|
+
- `.gitignore` contains a stray `EOF` line (the signature of the botched `cat > .gitignore <<EOF` in
|
|
29
|
+
pre-fix `/init` scaffolds).
|
|
30
|
+
- `.claude/`, `.agents/`, `jenga.config.json`, `.jenga-version` etc. are being committed even though
|
|
31
|
+
you expected Jenga to ignore them.
|
|
32
|
+
- You want the Jenga scaffold gone from the remote, but still working locally.
|
|
33
|
+
- You want to **reverse** any of the above and commit the scaffold deliberately.
|
|
34
|
+
|
|
35
|
+
## Why /init cannot do this
|
|
36
|
+
|
|
37
|
+
`skills/j-init/SKILL.md` hard-stops on `already-scaffolded`, and for good reason: `init.sh` does an
|
|
38
|
+
unconditional `cp "$ASSETS_DIR/.gitignore_template" .gitignore`, which would clobber the project's
|
|
39
|
+
existing `.gitignore`, and later steps overwrite `PROJECT_SUMMARY.md`, `workflow.json` and
|
|
40
|
+
`test-config.json` with fresh stubs. The upstream fixes (`E31_S07_T02`/`T03`, `E42_S06_T01`) repaired
|
|
41
|
+
the **template**, which is forward-only. Nothing repairs a project already on disk. That is this
|
|
42
|
+
skill's entire reason to exist.
|
|
43
|
+
|
|
44
|
+
`apply-project-visibility.sh` and `apply-scaffold-visibility.sh` remain the **init-time** path and are
|
|
45
|
+
not replaced. They only ever append `project/`, `.claude/` and `.agents/` as loose lines — they do not
|
|
46
|
+
remove a stray `EOF`, do not cover the file-level paths, and cannot untrack anything.
|
|
47
|
+
|
|
48
|
+
## Scripts
|
|
49
|
+
|
|
50
|
+
All three ship without the executable bit — invoke via `bash`. All are bash 3.2 compatible (macOS).
|
|
51
|
+
|
|
52
|
+
| Script | Role |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `scripts/audit-gitignore.sh` | Read-only. Reports stray terminators, per-path entry/ignored/tracked state, and what is present on origin. Exit `10` means findings, `0` means clean. |
|
|
55
|
+
| `scripts/repair-gitignore.sh` | Strips stray `EOF` lines, then adds (`ignored`) or removes (`visible`) the entries inside a managed block. Atomic write, verified after. |
|
|
56
|
+
| `scripts/untrack-jenga-files.sh` | `git rm -r --cached` — stops tracking, never deletes from disk. Optional `--commit`, opt-in `--push`. |
|
|
57
|
+
| `assets/jenga-paths.txt` | The single catalog of Jenga-owned paths, tiered. All three scripts read it. |
|
|
58
|
+
|
|
59
|
+
## Path tiers
|
|
60
|
+
|
|
61
|
+
`--tiers` selects what to act on. Default is `scaffold` — the safe set.
|
|
62
|
+
|
|
63
|
+
| Tier | Contents | Default |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `scaffold` | `.claude/`, `.agents/`, `.jenga/`, `.jenga-version`, `jenga.config.json`, `.github/hooks/jenga.json`, `J-CLAUDE.md`, `J-AGENTS.md` — entirely Jenga-owned, regenerated by postinstall/`/distribute`. | ✅ on |
|
|
66
|
+
| `hybrid` | `CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md` — Jenga writes a *managed block* and preserves your content around it. Ignoring these hides **your** prose too. | opt-in |
|
|
67
|
+
| `board` | `project/` — governed by `project_files_visibility`. Teams sharing a board want it tracked. | opt-in |
|
|
68
|
+
| `optional` | `.copilot/` — not generated by any current Jenga version. Defensive only. | opt-in |
|
|
69
|
+
|
|
70
|
+
## Procedure
|
|
71
|
+
|
|
72
|
+
### 1. Audit
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bash <skill>/scripts/audit-gitignore.sh . --tiers scaffold
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Present the three summary counts to the user. If `AUDIT_RESULT=clean`, say so and stop — there is
|
|
79
|
+
nothing to repair.
|
|
80
|
+
|
|
81
|
+
### 2. Confirm scope
|
|
82
|
+
|
|
83
|
+
The audit's per-path table shows what is tracked. Before changing anything, ask:
|
|
84
|
+
|
|
85
|
+
Which Jenga paths should this project ignore?
|
|
86
|
+
1. Scaffold only — .claude/, .agents/, jenga.config.json, .jenga-version, hook + J- files (recommended)
|
|
87
|
+
2. Scaffold + the board (project/) — adds your scrum board, todo.md, queue, rapports, logs
|
|
88
|
+
3. Scaffold + CLAUDE.md / AGENTS.md / copilot-instructions.md — note these may contain your own prose
|
|
89
|
+
4. Everything, including the defensive .copilot/ entry
|
|
90
|
+
5. Other (describe below)
|
|
91
|
+
|
|
92
|
+
Two warnings to surface if they are in scope:
|
|
93
|
+
|
|
94
|
+
- **`jenga.config.json`** holds `project_files_visibility` and `scaffold_visibility`. Ignoring it
|
|
95
|
+
means a fresh clone loses those settings and the next `/init`-style run re-prompts for them.
|
|
96
|
+
- **`hybrid` tier** files contain user-authored content outside Jenga's managed block. Ignoring them
|
|
97
|
+
removes that content from the repo too.
|
|
98
|
+
|
|
99
|
+
### 3. Repair the .gitignore
|
|
100
|
+
|
|
101
|
+
Always dry-run first and show the diff:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bash <skill>/scripts/repair-gitignore.sh ignored . --tiers <tiers> --dry-run
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then apply without `--dry-run`. This step is purely local and reversible (`visible` mode removes the
|
|
108
|
+
block again), so it does not need separate confirmation beyond the scope choice in step 2.
|
|
109
|
+
|
|
110
|
+
If the user genuinely wants to ignore a file literally named `EOF`, pass `--keep-eof`.
|
|
111
|
+
|
|
112
|
+
### 4. Untrack
|
|
113
|
+
|
|
114
|
+
`.gitignore` has no effect on files git already tracks, so step 3 alone changes nothing for anything
|
|
115
|
+
already committed. Dry-run first:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bash <skill>/scripts/untrack-jenga-files.sh . --tiers <tiers> --dry-run
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then choose how far to go:
|
|
122
|
+
|
|
123
|
+
The .gitignore is fixed. How far should I take the untracking?
|
|
124
|
+
1. Stage the removals only — I review `git status` and commit myself
|
|
125
|
+
2. Stage and commit locally — nothing reaches the remote until I push
|
|
126
|
+
3. Stage, commit, and push — removes them from origin's branch tip
|
|
127
|
+
4. Stop here — just the .gitignore change
|
|
128
|
+
5. Other (describe below)
|
|
129
|
+
|
|
130
|
+
Map to `--commit` / `--commit --push` accordingly. **Never pass `--push` without the user explicitly
|
|
131
|
+
choosing option 3** — it is the only outward-facing step and cannot be undone by re-running the skill.
|
|
132
|
+
|
|
133
|
+
### 5. Report
|
|
134
|
+
|
|
135
|
+
Re-run the audit to confirm `AUDIT_RESULT=clean`, then state plainly what changed, and always
|
|
136
|
+
include the history caveat below.
|
|
137
|
+
|
|
138
|
+
## What this does NOT do
|
|
139
|
+
|
|
140
|
+
- **It does not rewrite history.** `--push` removes the paths from the branch *tip*. Every earlier
|
|
141
|
+
commit still contains them, and the blobs stay reachable. A fresh clone is clean; `git log` is not.
|
|
142
|
+
- **It is not a secret scrubber.** If a `.env` or credential was committed, treat it as compromised,
|
|
143
|
+
rotate it, and use `git-filter-repo` separately. Say this out loud if the audit shows anything
|
|
144
|
+
credential-shaped.
|
|
145
|
+
- **It never deletes from disk.** Every removal is `git rm --cached`, and the script re-verifies each
|
|
146
|
+
path is still present before reporting success.
|
|
147
|
+
|
|
148
|
+
## Edge cases
|
|
149
|
+
|
|
150
|
+
| Situation | Behavior |
|
|
151
|
+
|---|---|
|
|
152
|
+
| No `.gitignore` at the root | `ignored` creates one; `visible` exits 0 as a no-op. |
|
|
153
|
+
| No git repo | All three exit 1 with a clear message — this skill needs git. |
|
|
154
|
+
| No upstream branch | Audit says nothing was ever pushed. `--push` refuses rather than guessing a remote; the commit is already safe locally. |
|
|
155
|
+
| Unrelated staged changes | `untrack-jenga-files.sh` refuses (exit 4) so it cannot sweep your work into its commit. A staged `.gitignore` alone is allowed through and folded in, since it is the companion change. |
|
|
156
|
+
| Unterminated managed block | `repair-gitignore.sh` refuses to write (exit 3) rather than risk eating the rest of the file. |
|
|
157
|
+
| Path tracked but absent from disk | Reported as a warning; nothing was lost, git simply tracked something the working tree did not have. |
|