@jenga-ai/agent 3.2.0 → 3.5.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 +52 -12
- package/agents/developer.md +16 -1
- package/agents/scrum-master.md +1 -0
- package/bin/jenga.js +10 -0
- package/lib/commands/dashboard.js +92 -0
- package/lib/skill-allow-list.json +6 -2
- package/package.json +21 -2
- package/project/app/api/lib/resolve-project-root.js +120 -0
- package/project/app/api/package.json +16 -0
- package/project/app/api/parsers/architecture.js +72 -0
- package/project/app/api/parsers/board.js +141 -0
- package/project/app/api/parsers/documentation.js +125 -0
- package/project/app/api/parsers/git-log.js +52 -0
- package/project/app/api/parsers/ideas.js +62 -0
- package/project/app/api/parsers/knowledge-graph.js +73 -0
- package/project/app/api/parsers/lib/markdown-dir-reader.js +163 -0
- package/project/app/api/parsers/rapports.js +148 -0
- package/project/app/api/parsers/todo.js +179 -0
- package/project/app/api/response.js +47 -0
- package/project/app/api/routes/architecture.js +23 -0
- package/project/app/api/routes/board.js +46 -0
- package/project/app/api/routes/documentation.js +24 -0
- package/project/app/api/routes/health.js +25 -0
- package/project/app/api/routes/history.js +55 -0
- package/project/app/api/routes/rapports.js +24 -0
- package/project/app/api/scripts/capture-snapshot.js +294 -0
- package/project/app/api/server.js +112 -0
- package/project/app/api/types.js +40 -0
- package/project/app/package.json +21 -0
- package/project/app/ui/dist/assets/index-7fj-vllY.js +104 -0
- package/project/app/ui/dist/assets/index-CdK3Qrep.css +1 -0
- package/project/app/ui/dist/index.html +13 -0
- package/project/app/ui/package.json +23 -0
- package/project/app/ui/scripts/build-snapshot-html.cjs +214 -0
- package/project/app/ui/scripts/dashboard-open.cjs +88 -0
- package/project/app/ui/scripts/dashboard-start.cjs +87 -0
- package/scripts/acquire-concurrency-slot.sh +220 -0
- package/scripts/compute-deploy-reconcile.sh +439 -0
- package/scripts/jenga-permission-level-switch.sh +19 -3
- package/scripts/mark-deployed.sh +532 -0
- package/scripts/populate-knowledge-graph.js +429 -0
- package/scripts/release-concurrency-slot.sh +129 -0
- package/scripts/validate-board.sh +60 -2
- package/scripts/verify-consumer-install.sh +470 -0
- package/skills/j-cloud-connect/SKILL.md +95 -0
- package/skills/j-cloud-connect/scripts/configure-backend.sh +267 -0
- package/skills/j-cloud-connect/scripts/install-rclone.sh +153 -0
- package/skills/j-dashboard/SKILL.md +144 -0
- package/skills/j-dashboard/scripts/launch.sh +121 -0
- package/skills/j-dashboard/scripts/resolve-app-dir.sh +164 -0
- package/skills/j-dashboard/scripts/snapshot.sh +267 -0
- package/skills/j-dashboard-share/SKILL.md +96 -0
- package/skills/j-dashboard-share/scripts/upload-snapshot.sh +173 -0
- package/skills/j-init/SKILL.md +52 -13
- package/skills/j-init/assets/.gitignore_template +1 -2
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
- package/skills/j-init/scripts/init.sh +19 -5
- package/skills/j-playbook/SKILL.md +12 -0
- package/skills/j-playbook-new/SKILL.md +155 -0
- package/skills/j-playbook-new/scripts/playbook-new.sh +332 -0
- package/skills/j-publish/scripts/npm_ci_pipeline.sh +6 -0
- package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
- package/skills/j-uncharted/SKILL.md +54 -7
- package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
- package/skills/j-uncharted/scripts/elicitation-state.sh +45 -7
- package/skills/jenga/scripts/load-nl-catalog.js +22 -6
- package/skills/jenga/scripts/load-playbooks.sh +146 -35
- package/templates/SCRUM_BOARD_SCHEMA.md +18 -6
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: j.playbook-new
|
|
3
|
+
description: Guided wizard that walks you through authoring a new project-local playbook — id, name, description, keywords, examples, and an ordered list of skills — validates every input against the real catalogs, and self-validates the written file before reporting success.
|
|
4
|
+
keywords:
|
|
5
|
+
- new playbook
|
|
6
|
+
- create playbook
|
|
7
|
+
- author playbook
|
|
8
|
+
- project playbook
|
|
9
|
+
- custom playbook
|
|
10
|
+
examples:
|
|
11
|
+
- "I want to create my own playbook"
|
|
12
|
+
- "help me author a new project-local playbook"
|
|
13
|
+
- "set up a custom workflow chain for my project"
|
|
14
|
+
- "j.playbook-new"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Playbook New — Guided Playbook-Authoring Wizard
|
|
18
|
+
|
|
19
|
+
## Purpose
|
|
20
|
+
|
|
21
|
+
`skills/jenga/scripts/load-playbooks.sh` merges two playbook sources into one catalog: the
|
|
22
|
+
framework-owned `skills/jenga/playbooks/` tree, and a project-owned `project/.playbooks/`
|
|
23
|
+
directory (`E53_S09_T01`). This skill is the guided authoring path for the second source — it
|
|
24
|
+
walks the user step by step through the required fields, validates each one against the real,
|
|
25
|
+
live catalogs (never a hand-maintained list of skill names or existing playbook ids), writes
|
|
26
|
+
`project/.playbooks/<id>.json`, and re-validates the just-written file via
|
|
27
|
+
`skills/jenga/scripts/load-playbooks.sh lookup` before ever declaring success. Hand-editing the
|
|
28
|
+
written JSON file remains the escape hatch for anything this wizard doesn't author (see the "v1
|
|
29
|
+
scope cut" note below).
|
|
30
|
+
|
|
31
|
+
**All deterministic work — slug/uniqueness validation, catalog lookups, and the JSON write
|
|
32
|
+
itself — lives in `skills/j-playbook-new/scripts/playbook-new.sh`** (per `CLAUDE.md`'s "Scripts
|
|
33
|
+
Over Inline Logic" principle). This skill's own job is only to run the conversational loop around
|
|
34
|
+
that script and interpret its JSON results — it never re-implements any validation or catalog
|
|
35
|
+
logic inline.
|
|
36
|
+
|
|
37
|
+
**v1 scope cut (deliberate, matches `E53_S09`'s story-level scope cut):** this wizard authors
|
|
38
|
+
plain, ordered, bare-string skill-name chains only — no `forward_from`, `resolve`, `conditional`,
|
|
39
|
+
or `playbook`-type (composition) `StepObject` fields. Hand-edit the written file directly if you
|
|
40
|
+
need any of those.
|
|
41
|
+
|
|
42
|
+
**Scope note — keywords and examples.** The playbook schema
|
|
43
|
+
(`skills/jenga/playbooks/schema.json`) requires every playbook to carry non-empty `keywords` and
|
|
44
|
+
`examples` arrays, exactly like every existing built-in playbook file already does — these are
|
|
45
|
+
what let `/jenga`'s own natural-language matching (`match-playbook.sh`) ever propose this playbook
|
|
46
|
+
from a free-text request. This wizard therefore asks for both, in addition to id/name/description/
|
|
47
|
+
skill-list, so the result is a genuinely useful, matchable playbook rather than one that merely
|
|
48
|
+
parses.
|
|
49
|
+
|
|
50
|
+
## Instructions
|
|
51
|
+
|
|
52
|
+
1. **Ask for an id** — a short, stable, kebab-case identifier (e.g. `my-release-flow`). Validate
|
|
53
|
+
it by running:
|
|
54
|
+
```
|
|
55
|
+
skills/j-playbook-new/scripts/playbook-new.sh validate-id "<id>"
|
|
56
|
+
```
|
|
57
|
+
Parse the single JSON object printed to stdout:
|
|
58
|
+
- `{"valid": true}` — continue to step 2.
|
|
59
|
+
- `{"valid": false, "reason": "..."}` — show the `reason` to the user verbatim and re-prompt
|
|
60
|
+
for a different id. Do not proceed until a validation call returns `valid: true`.
|
|
61
|
+
|
|
62
|
+
2. **Ask for a `name`** — a short, human-readable display name (e.g. "My Release Flow"), shown to
|
|
63
|
+
users in confirmation prompts and routing output, mirroring every existing playbook's `name`
|
|
64
|
+
field. No script validation needed beyond "non-empty" — re-prompt if the user gives an empty
|
|
65
|
+
answer.
|
|
66
|
+
|
|
67
|
+
3. **Ask for a `description`** — one sentence explaining what this playbook accomplishes
|
|
68
|
+
end-to-end. Re-prompt if empty.
|
|
69
|
+
|
|
70
|
+
4. **Ask for `keywords`** — one or more short phrases (1-3 words each) a user might type that
|
|
71
|
+
should match this playbook, mirroring every existing playbook's `keywords` field (see
|
|
72
|
+
`skills/jenga/playbooks/brainstorm-to-mirror.json` for a concrete example of the expected
|
|
73
|
+
shape and specificity). Accept them one at a time or as a single comma-separated batch —
|
|
74
|
+
your judgment, whichever the user's response shape suggests. Require at least one.
|
|
75
|
+
|
|
76
|
+
5. **Ask for `examples`** — one or more natural-language example prompts a user might type that
|
|
77
|
+
should resolve to this playbook (again, mirror the existing built-in playbooks' style and
|
|
78
|
+
level of specificity). Require at least one; at least one example should plausibly span the
|
|
79
|
+
full breadth of the playbook's steps, not just its first one, so it's distinguishable from a
|
|
80
|
+
single-skill match — use your judgment coaching the user toward this if their first example is
|
|
81
|
+
too narrow.
|
|
82
|
+
|
|
83
|
+
6. **Ask for an ordered list of skill names** — one at a time, or as a single ordered batch —
|
|
84
|
+
your judgment based on how the user responds. For **each** name entered, validate it by
|
|
85
|
+
running:
|
|
86
|
+
```
|
|
87
|
+
skills/j-playbook-new/scripts/playbook-new.sh validate-skill "<name>"
|
|
88
|
+
```
|
|
89
|
+
Parse the JSON result:
|
|
90
|
+
- `{"valid": true}` — accept it into the ordered list and continue.
|
|
91
|
+
- `{"valid": false, "reason": "..."}` — show the `reason` verbatim and re-prompt for that
|
|
92
|
+
position in the list (do not silently drop it or guess a correction).
|
|
93
|
+
|
|
94
|
+
Once the user signals they're done adding skills, require **at least 2** total (a playbook is a
|
|
95
|
+
chain — a single-skill "playbook" isn't a meaningful use of this mechanism). If fewer than 2
|
|
96
|
+
were entered, tell the user this and continue prompting for more.
|
|
97
|
+
|
|
98
|
+
7. **Write the playbook.** Assemble the JSON payload from steps 1-6:
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"id": "<id>",
|
|
102
|
+
"name": "<name>",
|
|
103
|
+
"description": "<description>",
|
|
104
|
+
"keywords": ["<keyword 1>", "..."],
|
|
105
|
+
"examples": ["<example 1>", "..."],
|
|
106
|
+
"steps": ["<skill 1>", "<skill 2>", "..."]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
Pipe it to stdin of:
|
|
110
|
+
```
|
|
111
|
+
skills/j-playbook-new/scripts/playbook-new.sh write
|
|
112
|
+
```
|
|
113
|
+
Parse the JSON result:
|
|
114
|
+
- `{"written": true, "path": "..."}` — continue to step 8.
|
|
115
|
+
- `{"written": false, "reason": "..."}` — show the `reason` to the user verbatim. This should
|
|
116
|
+
only happen if something changed between validation and write (e.g. a race, or a shape
|
|
117
|
+
issue this wizard's own prompts didn't already catch) — do not silently retry; tell the user
|
|
118
|
+
what failed and, if it's fixable (e.g. the id collided after all), loop back to the relevant
|
|
119
|
+
earlier step.
|
|
120
|
+
|
|
121
|
+
8. **Self-validate before declaring success — never skip this step.** Run:
|
|
122
|
+
```
|
|
123
|
+
skills/jenga/scripts/load-playbooks.sh lookup "<id>"
|
|
124
|
+
```
|
|
125
|
+
Branch on the returned `status`:
|
|
126
|
+
- **`"valid"`** — report success to the user: the playbook was written to
|
|
127
|
+
`project/.playbooks/<id>.json` and is confirmed loadable. Mention it can now be invoked via
|
|
128
|
+
`j.playbook <id>` or matched naturally through `j.jenga`.
|
|
129
|
+
- **`"invalid"`** — report the `reason` field to the user **verbatim** — never a generic
|
|
130
|
+
failure message. This is a real defect (the write succeeded but load-time validation still
|
|
131
|
+
rejects it) — do not claim success.
|
|
132
|
+
- **`"not_found"`** — report to the user that the write appears to have silently failed (the
|
|
133
|
+
file the wizard just wrote could not be found by the loader) — this would indicate an
|
|
134
|
+
environment problem (e.g. a different project root being resolved by the two scripts), not a
|
|
135
|
+
normal outcome. Never claim success.
|
|
136
|
+
|
|
137
|
+
Under no circumstances report success to the user without having seen `"status": "valid"` from
|
|
138
|
+
this exact call.
|
|
139
|
+
|
|
140
|
+
## Edge Cases
|
|
141
|
+
|
|
142
|
+
- **The user wants to add `forward_from`/`resolve`/`conditional`/composition to a step.** Tell
|
|
143
|
+
them this wizard doesn't author those fields (v1 scope cut) and that they can hand-edit
|
|
144
|
+
`project/.playbooks/<id>.json` afterward — the schema supports these fields identically
|
|
145
|
+
regardless of which directory a playbook file lives in.
|
|
146
|
+
- **The id collides with a project playbook that already exists on disk but is currently
|
|
147
|
+
invalid** (e.g. a hand-edited file with a JSON syntax error) — `validate-id` still rejects it (it
|
|
148
|
+
checks raw file existence, not just catalog membership) rather than silently overwriting a file
|
|
149
|
+
the user may not realize is broken.
|
|
150
|
+
- **A skill name the user enters exists under multiple forms in the catalog** (e.g. both a bare
|
|
151
|
+
and a `j-`-prefixed directory, during this repo's ongoing `E50` naming-contract transition) —
|
|
152
|
+
accept whichever exact form the user typed if `validate-skill` reports it valid; this wizard
|
|
153
|
+
does not impose a preference between forms the catalog itself doesn't distinguish.
|
|
154
|
+
- **The user cancels mid-wizard** — do not write anything; only step 7 ever touches disk, and only
|
|
155
|
+
once a complete, locally-validated payload exists.
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# skills/j-playbook-new/scripts/playbook-new.sh
|
|
4
|
+
#
|
|
5
|
+
# Deterministic helper behind the `j.playbook-new` guided wizard (E53_S09_T02). Per CLAUDE.md's
|
|
6
|
+
# "Scripts Over Inline Logic" principle, every mechanical step of the wizard -- id slug/uniqueness
|
|
7
|
+
# validation, skill-name validation against the real catalog, and the JSON write itself -- lives
|
|
8
|
+
# here rather than as inline agent prose in skills/j-playbook-new/SKILL.md. The agent driving the
|
|
9
|
+
# wizard calls this script once per step and interprets its JSON result; it never re-implements
|
|
10
|
+
# any of this logic itself.
|
|
11
|
+
#
|
|
12
|
+
# Reuses, never re-implements, the two existing single-source-of-truth catalogs:
|
|
13
|
+
# - skills/jenga/scripts/load-playbooks.sh (merged builtin+project playbook catalog, E53_S09_T01)
|
|
14
|
+
# - skills/jenga/scripts/load-nl-catalog.sh (the real, generated skill catalog /jenga's own
|
|
15
|
+
# natural-language matching already uses)
|
|
16
|
+
#
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# USAGE
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# playbook-new.sh validate-id <id>
|
|
21
|
+
#
|
|
22
|
+
# Checks <id> is slug-safe (the same pattern skills/jenga/playbooks/schema.json requires:
|
|
23
|
+
# ^[a-z0-9]+(-[a-z0-9]+)*$) and does not collide with any id already present in the merged catalog
|
|
24
|
+
# (skills/jenga/scripts/load-playbooks.sh, no arguments -- built-in and project sources both) OR an
|
|
25
|
+
# existing (possibly currently invalid, and therefore catalog-invisible) file at
|
|
26
|
+
# project/.playbooks/<id>.json. Prints exactly one JSON object to stdout:
|
|
27
|
+
# {"valid": true}
|
|
28
|
+
# {"valid": false, "reason": "<human-readable reason>"}
|
|
29
|
+
# Exit 0 for BOTH outcomes -- mirrors load-playbooks.sh's own `lookup` mode convention of
|
|
30
|
+
# reserving a non-zero exit for usage/setup errors only, never for a normal negative validation
|
|
31
|
+
# result the caller is expected to branch on.
|
|
32
|
+
#
|
|
33
|
+
# playbook-new.sh validate-skill <name>
|
|
34
|
+
#
|
|
35
|
+
# Checks <name> is a real, currently-loadable skill per skills/jenga/scripts/load-nl-catalog.sh's
|
|
36
|
+
# generated catalog -- the exact source /jenga's own natural-language matching already uses, never
|
|
37
|
+
# a hand-maintained list. Prints:
|
|
38
|
+
# {"valid": true}
|
|
39
|
+
# {"valid": false, "reason": "..."}
|
|
40
|
+
# Exit 0 for both outcomes, same convention as validate-id.
|
|
41
|
+
#
|
|
42
|
+
# playbook-new.sh write
|
|
43
|
+
#
|
|
44
|
+
# Reads a single JSON object from stdin:
|
|
45
|
+
# {"id": "...", "name": "...", "description": "...", "keywords": ["..."],
|
|
46
|
+
# "examples": ["..."], "steps": ["...", "..."]}
|
|
47
|
+
# Pre-validates it against the same required-field / non-empty-list / steps-shape rules
|
|
48
|
+
# skills/jenga/playbooks/schema.json declares (id slug pattern, all six required fields present,
|
|
49
|
+
# keywords/examples non-empty lists of non-empty strings, steps a list of >= 2 non-empty strings --
|
|
50
|
+
# this wizard authors bare-string steps only, per this task's v1 scope cut) BEFORE writing anything
|
|
51
|
+
# to disk. This is a defensive, redundant pre-check only -- the actual source of truth for validity
|
|
52
|
+
# remains load-playbooks.sh's own load-time validation, which the wizard's own self-validation step
|
|
53
|
+
# (SKILL.md step 8) always runs afterward regardless of this result. Creates project/.playbooks/ if
|
|
54
|
+
# it does not yet exist, and refuses to overwrite an already-existing file for the same id. On
|
|
55
|
+
# success, writes project/.playbooks/<id>.json (pretty-printed) and prints
|
|
56
|
+
# {"written": true, "path": "<absolute path>"}. On any shape/overwrite failure, prints
|
|
57
|
+
# {"written": false, "reason": "..."} and writes nothing. Exit 0 for both outcomes.
|
|
58
|
+
#
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# PROJECT ROOT RESOLUTION
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# Honors JENGA_PLAYBOOKS_TEST_ROOT -- the SAME override variable load-playbooks.sh itself defines
|
|
63
|
+
# (see that script's header "TESTING OVERRIDE"), not a second, script-specific one -- so a fixture
|
|
64
|
+
# pointing this script at a throwaway project root also makes load-playbooks.sh (invoked internally
|
|
65
|
+
# by validate-id, and by the wizard's own later self-validation step) resolve project/.playbooks/
|
|
66
|
+
# under that same root. Never set this variable in a real invocation.
|
|
67
|
+
#
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# EXIT CODES
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# 0 a result JSON object was printed to stdout (whether valid:true/false or written:true/false)
|
|
72
|
+
# 2 usage error, or a real setup failure (missing python3, the sibling load-playbooks.sh /
|
|
73
|
+
# load-nl-catalog.sh scripts could not be located, one of them exited non-zero, or the
|
|
74
|
+
# project/.playbooks/ directory could not be created)
|
|
75
|
+
#
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
set -euo pipefail
|
|
79
|
+
|
|
80
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
81
|
+
LOAD_PLAYBOOKS="$SCRIPT_DIR/../../jenga/scripts/load-playbooks.sh"
|
|
82
|
+
LOAD_NL_CATALOG="$SCRIPT_DIR/../../jenga/scripts/load-nl-catalog.sh"
|
|
83
|
+
|
|
84
|
+
if [ ! -f "$LOAD_PLAYBOOKS" ]; then
|
|
85
|
+
echo "Error: could not locate load-playbooks.sh at $LOAD_PLAYBOOKS" >&2
|
|
86
|
+
exit 2
|
|
87
|
+
fi
|
|
88
|
+
if [ ! -f "$LOAD_NL_CATALOG" ]; then
|
|
89
|
+
echo "Error: could not locate load-nl-catalog.sh at $LOAD_NL_CATALOG" >&2
|
|
90
|
+
exit 2
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
94
|
+
echo "Error: python3 is required by playbook-new.sh" >&2
|
|
95
|
+
exit 2
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if [ -n "${JENGA_PLAYBOOKS_TEST_ROOT:-}" ]; then
|
|
99
|
+
# Test-only override -- see header "PROJECT ROOT RESOLUTION" above. Never set in a real
|
|
100
|
+
# invocation.
|
|
101
|
+
PROJECT_DIR="$JENGA_PLAYBOOKS_TEST_ROOT"
|
|
102
|
+
else
|
|
103
|
+
PROJECT_DIR="${JENGA_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || pwd)}}"
|
|
104
|
+
fi
|
|
105
|
+
PLAYBOOKS_TARGET_DIR="$PROJECT_DIR/project/.playbooks"
|
|
106
|
+
|
|
107
|
+
if [ $# -lt 1 ]; then
|
|
108
|
+
echo "Usage: $(basename "$0") <validate-id|validate-skill|write> [args...]" >&2
|
|
109
|
+
exit 2
|
|
110
|
+
fi
|
|
111
|
+
MODE="$1"
|
|
112
|
+
shift
|
|
113
|
+
|
|
114
|
+
PY_SCRIPT="$(mktemp -t playbook-new-XXXXXX.py)"
|
|
115
|
+
trap 'rm -f "$PY_SCRIPT"' EXIT
|
|
116
|
+
|
|
117
|
+
cat > "$PY_SCRIPT" <<'PY'
|
|
118
|
+
import json
|
|
119
|
+
import os
|
|
120
|
+
import re
|
|
121
|
+
import sys
|
|
122
|
+
|
|
123
|
+
mode = sys.argv[1]
|
|
124
|
+
SLUG_RE = re.compile(r'^[a-z0-9]+(-[a-z0-9]+)*$')
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def emit(obj):
|
|
128
|
+
print(json.dumps(obj))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if mode == "validate-id":
|
|
132
|
+
target_id = sys.argv[2]
|
|
133
|
+
playbooks_target_dir = sys.argv[3]
|
|
134
|
+
|
|
135
|
+
if not SLUG_RE.match(target_id):
|
|
136
|
+
emit({
|
|
137
|
+
"valid": False,
|
|
138
|
+
"reason": (
|
|
139
|
+
f"'{target_id}' is not slug-safe -- must match ^[a-z0-9]+(-[a-z0-9]+)*$ "
|
|
140
|
+
f"(lowercase letters, digits, single hyphens between segments)"
|
|
141
|
+
),
|
|
142
|
+
})
|
|
143
|
+
sys.exit(0)
|
|
144
|
+
|
|
145
|
+
existing_file = os.path.join(playbooks_target_dir, f"{target_id}.json")
|
|
146
|
+
if os.path.isfile(existing_file):
|
|
147
|
+
emit({
|
|
148
|
+
"valid": False,
|
|
149
|
+
"reason": (
|
|
150
|
+
f"a file already exists at {existing_file} -- choose a different id or edit "
|
|
151
|
+
f"that file directly"
|
|
152
|
+
),
|
|
153
|
+
})
|
|
154
|
+
sys.exit(0)
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
catalog = json.load(sys.stdin)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
emit({
|
|
160
|
+
"valid": False,
|
|
161
|
+
"reason": f"could not read the playbook catalog to check for collisions ({e})",
|
|
162
|
+
})
|
|
163
|
+
sys.exit(0)
|
|
164
|
+
|
|
165
|
+
for entry in catalog:
|
|
166
|
+
if entry.get("id") == target_id:
|
|
167
|
+
src = entry.get("source", "unknown")
|
|
168
|
+
emit({
|
|
169
|
+
"valid": False,
|
|
170
|
+
"reason": (
|
|
171
|
+
f"id '{target_id}' collides with an already-loaded {src} playbook (see "
|
|
172
|
+
f"skills/jenga/scripts/load-playbooks.sh's merged catalog) -- choose a "
|
|
173
|
+
f"different id"
|
|
174
|
+
),
|
|
175
|
+
})
|
|
176
|
+
sys.exit(0)
|
|
177
|
+
|
|
178
|
+
emit({"valid": True})
|
|
179
|
+
sys.exit(0)
|
|
180
|
+
|
|
181
|
+
elif mode == "validate-skill":
|
|
182
|
+
target_name = sys.argv[2]
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
nl_catalog = json.load(sys.stdin)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
emit({"valid": False, "reason": f"could not read the generated skill catalog ({e})"})
|
|
188
|
+
sys.exit(0)
|
|
189
|
+
|
|
190
|
+
for entry in nl_catalog:
|
|
191
|
+
if entry.get("name") == target_name:
|
|
192
|
+
emit({"valid": True})
|
|
193
|
+
sys.exit(0)
|
|
194
|
+
|
|
195
|
+
emit({
|
|
196
|
+
"valid": False,
|
|
197
|
+
"reason": (
|
|
198
|
+
f"'{target_name}' is not a recognized skill directory name in the generated skill "
|
|
199
|
+
f"catalog (skills/jenga/scripts/load-nl-catalog.sh) -- check spelling and the exact "
|
|
200
|
+
f"directory-name form the catalog currently lists"
|
|
201
|
+
),
|
|
202
|
+
})
|
|
203
|
+
sys.exit(0)
|
|
204
|
+
|
|
205
|
+
elif mode == "write":
|
|
206
|
+
playbooks_target_dir = sys.argv[2]
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
payload = json.load(sys.stdin)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
emit({"written": False, "reason": f"stdin was not valid JSON ({e})"})
|
|
212
|
+
sys.exit(0)
|
|
213
|
+
|
|
214
|
+
if not isinstance(payload, dict):
|
|
215
|
+
emit({"written": False, "reason": "stdin JSON must be an object"})
|
|
216
|
+
sys.exit(0)
|
|
217
|
+
|
|
218
|
+
required_fields = ["id", "name", "description", "keywords", "examples", "steps"]
|
|
219
|
+
missing = [f for f in required_fields if f not in payload]
|
|
220
|
+
if missing:
|
|
221
|
+
emit({"written": False, "reason": f"missing required field(s) {missing}"})
|
|
222
|
+
sys.exit(0)
|
|
223
|
+
|
|
224
|
+
target_id = payload["id"]
|
|
225
|
+
if not isinstance(target_id, str) or not SLUG_RE.match(target_id):
|
|
226
|
+
emit({
|
|
227
|
+
"written": False,
|
|
228
|
+
"reason": f"id '{target_id}' is not slug-safe -- must match ^[a-z0-9]+(-[a-z0-9]+)*$",
|
|
229
|
+
})
|
|
230
|
+
sys.exit(0)
|
|
231
|
+
|
|
232
|
+
for field in ("name", "description"):
|
|
233
|
+
if not isinstance(payload[field], str) or not payload[field]:
|
|
234
|
+
emit({"written": False, "reason": f"'{field}' must be a non-empty string"})
|
|
235
|
+
sys.exit(0)
|
|
236
|
+
|
|
237
|
+
for field in ("keywords", "examples"):
|
|
238
|
+
value = payload[field]
|
|
239
|
+
if (
|
|
240
|
+
not isinstance(value, list)
|
|
241
|
+
or len(value) == 0
|
|
242
|
+
or not all(isinstance(v, str) and v for v in value)
|
|
243
|
+
):
|
|
244
|
+
emit({
|
|
245
|
+
"written": False,
|
|
246
|
+
"reason": f"'{field}' must be a non-empty list of non-empty strings",
|
|
247
|
+
})
|
|
248
|
+
sys.exit(0)
|
|
249
|
+
|
|
250
|
+
steps = payload["steps"]
|
|
251
|
+
if not isinstance(steps, list) or len(steps) < 2 or not all(isinstance(s, str) and s for s in steps):
|
|
252
|
+
emit({
|
|
253
|
+
"written": False,
|
|
254
|
+
"reason": (
|
|
255
|
+
"'steps' must be a list of at least 2 non-empty strings (bare skill-name steps "
|
|
256
|
+
"only -- this wizard authors no StepObject fields, per this task's v1 scope cut)"
|
|
257
|
+
),
|
|
258
|
+
})
|
|
259
|
+
sys.exit(0)
|
|
260
|
+
|
|
261
|
+
target_file = os.path.join(playbooks_target_dir, f"{target_id}.json")
|
|
262
|
+
if os.path.isfile(target_file):
|
|
263
|
+
emit({
|
|
264
|
+
"written": False,
|
|
265
|
+
"reason": f"a file already exists at {target_file} -- refusing to overwrite",
|
|
266
|
+
})
|
|
267
|
+
sys.exit(0)
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
os.makedirs(playbooks_target_dir, exist_ok=True)
|
|
271
|
+
except OSError as e:
|
|
272
|
+
print(f"Error: could not create {playbooks_target_dir}: {e}", file=sys.stderr)
|
|
273
|
+
sys.exit(2)
|
|
274
|
+
|
|
275
|
+
ordered = {
|
|
276
|
+
"id": target_id,
|
|
277
|
+
"name": payload["name"],
|
|
278
|
+
"description": payload["description"],
|
|
279
|
+
"keywords": payload["keywords"],
|
|
280
|
+
"examples": payload["examples"],
|
|
281
|
+
"steps": steps,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
with open(target_file, "w", encoding="utf-8") as fh:
|
|
286
|
+
json.dump(ordered, fh, indent=2)
|
|
287
|
+
fh.write("\n")
|
|
288
|
+
except OSError as e:
|
|
289
|
+
print(f"Error: could not write {target_file}: {e}", file=sys.stderr)
|
|
290
|
+
sys.exit(2)
|
|
291
|
+
|
|
292
|
+
emit({"written": True, "path": os.path.abspath(target_file)})
|
|
293
|
+
sys.exit(0)
|
|
294
|
+
|
|
295
|
+
else:
|
|
296
|
+
print(f"Error: unrecognized mode '{mode}' (usage: validate-id|validate-skill|write)", file=sys.stderr)
|
|
297
|
+
sys.exit(2)
|
|
298
|
+
PY
|
|
299
|
+
|
|
300
|
+
case "$MODE" in
|
|
301
|
+
validate-id)
|
|
302
|
+
if [ $# -lt 1 ] || [ -z "${1:-}" ]; then
|
|
303
|
+
echo "Usage: $(basename "$0") validate-id <id>" >&2
|
|
304
|
+
exit 2
|
|
305
|
+
fi
|
|
306
|
+
TARGET_ID="$1"
|
|
307
|
+
if ! CATALOG_JSON="$("$LOAD_PLAYBOOKS")"; then
|
|
308
|
+
echo "Error: load-playbooks.sh failed while checking id '$TARGET_ID' for collisions" >&2
|
|
309
|
+
exit 2
|
|
310
|
+
fi
|
|
311
|
+
printf '%s' "$CATALOG_JSON" | python3 "$PY_SCRIPT" validate-id "$TARGET_ID" "$PLAYBOOKS_TARGET_DIR"
|
|
312
|
+
;;
|
|
313
|
+
validate-skill)
|
|
314
|
+
if [ $# -lt 1 ] || [ -z "${1:-}" ]; then
|
|
315
|
+
echo "Usage: $(basename "$0") validate-skill <name>" >&2
|
|
316
|
+
exit 2
|
|
317
|
+
fi
|
|
318
|
+
TARGET_NAME="$1"
|
|
319
|
+
if ! NL_CATALOG_JSON="$("$LOAD_NL_CATALOG")"; then
|
|
320
|
+
echo "Error: load-nl-catalog.sh failed while validating skill name '$TARGET_NAME'" >&2
|
|
321
|
+
exit 2
|
|
322
|
+
fi
|
|
323
|
+
printf '%s' "$NL_CATALOG_JSON" | python3 "$PY_SCRIPT" validate-skill "$TARGET_NAME"
|
|
324
|
+
;;
|
|
325
|
+
write)
|
|
326
|
+
python3 "$PY_SCRIPT" write "$PLAYBOOKS_TARGET_DIR"
|
|
327
|
+
;;
|
|
328
|
+
*)
|
|
329
|
+
echo "Error: unrecognized mode '$MODE' (usage: $(basename "$0") <validate-id|validate-skill|write> [args...])" >&2
|
|
330
|
+
exit 2
|
|
331
|
+
;;
|
|
332
|
+
esac
|
|
@@ -191,6 +191,9 @@ jobs:
|
|
|
191
191
|
- name: Install dependencies
|
|
192
192
|
run: npm ci
|
|
193
193
|
|
|
194
|
+
- name: Install project/app workspace dependencies (E29_S05_T01)
|
|
195
|
+
run: npm ci --prefix project/app
|
|
196
|
+
|
|
194
197
|
- name: Regenerate lib/legacy-shipped-paths.json (E26_S08_T03)
|
|
195
198
|
run: node scripts/generate-legacy-shipped-paths.js || echo "::warning::legacy-shipped-paths generation failed; publishing without an updated list"
|
|
196
199
|
|
|
@@ -224,6 +227,9 @@ jobs:
|
|
|
224
227
|
- name: Install dependencies
|
|
225
228
|
run: npm ci
|
|
226
229
|
|
|
230
|
+
- name: Install project/app workspace dependencies (E29_S05_T01)
|
|
231
|
+
run: npm ci --prefix project/app
|
|
232
|
+
|
|
227
233
|
- name: Stage to npm
|
|
228
234
|
run: npm stage publish --provenance --access ${NPM_ACCESS} --tag ${DIST_TAG}
|
|
229
235
|
|
|
@@ -168,6 +168,7 @@ For genuinely undocumented code, there is often no reliable human oracle to conf
|
|
|
168
168
|
|
|
169
169
|
- **The deterministic pipeline remains the tool of record for zero-oracle codebases.** `onboard --legacy` and `segment --mode delivery` never depend on anyone confirming intent — they ground everything in mechanical evidence (file structure, dependencies, test coverage) and say so explicitly under `Open Questions` when the evidence doesn't support a conclusion. When there is no one left who understands the code, reach for one of those, not the conversational flow.
|
|
170
170
|
- **When running the conversational flow, do not manufacture confidence.** If the user's answer is uncertain, hedged, or contradicts what discovery/Investigative Mode found, write the node honestly — do not round an uncertain answer up to a confirmed one. There is no schema field yet to tag confidence (the stub schema is intentionally minimal); until one exists, say so in the node's `description` text itself (e.g. "per the user, this module retries failed charges — unconfirmed against the code, which shows only a single retry attempt") rather than silently dropping the caveat.
|
|
171
|
+
- **`verification_depth: strict` is this guidance's concrete implementation, not a separate idea (E40_S06_T02).** When a candidate's Familiarity Check answer is `No` (Convergence Loop Step 1), Step 4's risk-weighted gating never lets an escalating finding round up to a false confirmation by asking the user to bless it — it auto-flags the node with exactly the hedged-`description` convention this bullet describes and converges it without a prompt. This bullet states the principle; Step 4's `strict` branch is what enforces it mechanically, so the two sections should be read as one mechanism, not two independently-arrived-at claims.
|
|
171
172
|
- **A confidently wrong answer is not detectable by this flow.** Corroboration against a second signal (commit history, existing docs, a second person) is the only mitigation, and it is not built here — this is the accepted residual risk, not a gap to engineer around mid-conversation.
|
|
172
173
|
|
|
173
174
|
### Directory Triage
|
|
@@ -213,10 +214,56 @@ Silence, a counter-question, or an ambiguous reply is not consent — re-ask, th
|
|
|
213
214
|
|
|
214
215
|
Runs once per surviving candidate (a subsystem, a named flow, a directory) after Directory Triage. This is the "propose understanding, ask the user to confirm or correct" cycle at the center of the redesign — and the one the scrutiny flagged as having no termination bound and no defense against confirmation fatigue. Both gaps are closed mechanically, not by agent discipline alone:
|
|
215
216
|
|
|
216
|
-
1. **
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
1. **Familiarity Check — once per candidate, before Investigative Mode dispatch.** Ask (per the Interaction Pattern in `CLAUDE.md` — numbered list, free-text last):
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
Are you familiar with this service/segment?
|
|
221
|
+
1. Yes
|
|
222
|
+
2. A little
|
|
223
|
+
3. No
|
|
224
|
+
4. Other (describe below)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Silence, a counter-question, or an ambiguous reply is not consent — re-ask, the same convention used at every other confirmation gate in this skill. Map the answer to a `verification_depth` scoped to this candidate only — `Yes` → `shallow`, `A little` → `moderate`, `No` → `strict` — and persist it immediately, keyed by this candidate's node id, via `elicitation-state.sh`'s checkpoint mechanism (see Multi-Session Persistence below for the exact call and merge semantics):
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
printf '{"verification_depth": {"%s": "%s"}}' "<candidate-id>" "<shallow|moderate|strict>" \
|
|
231
|
+
| bash skills/j-uncharted/scripts/elicitation-state.sh checkpoint --id <elicitation-id> --json -
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Check before asking.** A candidate whose `verification_depth` is already present in the state file's `checkpoint.verification_depth` (per Multi-Session Persistence below) has already answered this — do not re-ask it, on a fresh session or otherwise.
|
|
235
|
+
|
|
236
|
+
This question operationalizes the Human-Oracle-Availability Limitation above — it is the mechanism for finding out, per candidate, how much weight the user's own confirmations should carry, rather than assuming a uniform level of trust for every candidate in one run. **`verification_depth` is read back and consumed by Step 4's risk-weighted gating below (`E40_S06_T02`)**, which branches its auto-accept/confirm/auto-flag behavior per depth. The fixed internal/external question template used whenever shallow/moderate gating does decide to prompt (Step 5) is `skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md` (`E40_S06_T03`) — this step's job remains asking the question and making the answer durable for those steps to read.
|
|
237
|
+
2. **Dispatch Investigative Mode.** Per `agents/developer.md`'s and `agents/tester.md`'s Investigative Mode sections (E20_S08_T02), dispatch the developer to trace what the code actually does for the candidate, and the tester to trace what the test suite actually exercises and verifies for the same candidate — two distinct vantage points, not two names for the same read. Both are read-only, worktree-sandboxed, no commits, no board writes.
|
|
238
|
+
3. **Propose understanding.** From both traces, draft the candidate's coarse graph node(s)/edge(s) (per the stub schema) and a plain-language summary of what they represent.
|
|
239
|
+
4. **Risk-weighted gating — not every finding gets a prompt, and `verification_depth` (Step 1) decides how gating itself behaves, not just what counts as risky.** This is the fix for confirmation fatigue (solution assessment, Problem 6, Solution B — RECOMMENDED). The baseline escalation criteria — the three triggers that force an explicit confirmation — are unchanged from before `E40_S06`:
|
|
240
|
+
|
|
241
|
+
- **T1 — inference-dependent:** the node's description depends on an inference the traces don't fully support.
|
|
242
|
+
- **T2 — structurally central:** the node has many outgoing edges.
|
|
243
|
+
- **T3 — already-flagged uncertain:** the Human-Oracle-Availability Limitation above already flagged this finding as uncertain.
|
|
244
|
+
|
|
245
|
+
A finding tripping none of T1-T3 is **low-risk** and auto-accepts regardless of depth. A finding tripping any of T1-T3 is **escalating**, and what happens to it now branches on the current candidate's `verification_depth` (read from `checkpoint.verification_depth.<candidate-id>`, per Step 1):
|
|
246
|
+
|
|
247
|
+
- **`moderate` (the default, unchanged calibration)** — exactly today's behavior: every escalating finding (any of T1-T3) forces a confirm prompt (Step 5); every low-risk finding auto-accepts without a prompt. **Log every auto-accepted node** in the elicitation state's checkpoint data (see Multi-Session Persistence below) so the decision is auditable later, per that solution's own mitigation for "the scoring mechanism itself misjudges impact."
|
|
248
|
+
- **`shallow` (widened auto-accept)** — the concrete widening rule: **drop T2 (structurally central) as an escalation trigger.** A finding tripping T2 alone — structurally central, but not inference-dependent and not already flagged uncertain — is reclassified low-risk and auto-accepted (still logged, same as above) instead of escalating. T1 and T3 still force a confirm prompt exactly as under `moderate`; only the T2-alone case widens. This is the literal reading of "findings that would sit just below today's high-impact bar" from the task's own framing — a purely structural signal with no corroborating uncertainty is no longer, by itself, enough to interrupt the user.
|
|
249
|
+
- **`strict` (no confirm prompt for escalating findings, ever)** — a finding tripping any of T1-T3 is **never presented to the user**. Instead:
|
|
250
|
+
1. Write the node directly with a hedged, low-confidence `description`, reusing the exact hedging convention the Human-Oracle-Availability Limitation section already specifies (e.g. "unconfirmed — traces did not fully corroborate this," adapted to name the specific gap).
|
|
251
|
+
2. Call `elicitation-state.sh converge` directly — **do not call `elicitation-state.sh turn` for this node.** No confirmation round is spent; the node goes straight from "proposed" to "converged," never "pending":
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
bash skills/j-uncharted/scripts/elicitation-state.sh converge --id <elicitation-id> --node <node-id> --note "auto-flagged under strict depth: <one-line reason, e.g. 'structurally central, traces disagree on scope'>"
|
|
255
|
+
```
|
|
256
|
+
3. **Log the auto-flag** in the elicitation state's checkpoint data, the same way `moderate`/`shallow` auto-accepts are logged — this is an automatic decision, not a silent one, and stays auditable exactly like every other gating outcome.
|
|
257
|
+
|
|
258
|
+
Low-risk findings under `strict` are unaffected — they auto-accept exactly as under `moderate`/`shallow`. `strict` only changes what happens to the escalating case.
|
|
259
|
+
|
|
260
|
+
The fixed internal/external question template used whenever `shallow`/`moderate` gating does decide to prompt is `skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md` (Step 5, `E40_S06_T03`) — dormant under `strict`, since no prompt ever fires there.
|
|
261
|
+
5. **Confirm/correct, one round per call to `elicitation-state.sh turn`.** Never reached for a `strict`-depth candidate's escalating findings — those converge directly per Step 4 above. For a node requiring confirmation under `shallow`/`moderate`, do **not** present the draft with a fully open-ended "propose understanding, ask to confirm or correct" prompt. Instead use the fixed question set in `skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md` (`E40_S06_T03`), selecting the variant by node kind:
|
|
262
|
+
|
|
263
|
+
- **Internal variant** — the node represents the candidate/service itself (the thing this investigation is about).
|
|
264
|
+
- **External variant** — the node represents a dependency or consumer the traces surfaced outside the candidate (something it calls, or something that calls it).
|
|
265
|
+
|
|
266
|
+
Present the drafted node/edge summary from Step 3 first, then ask the selected variant's fixed questions, then offer the same confirm / correct-with-detail / defer-as-unconfirmed / other choice as before (per the Interaction Pattern in `CLAUDE.md`) — the template file spells out both the questions and this response block verbatim, so read it rather than reconstructing either from memory. Each round, call:
|
|
220
267
|
|
|
221
268
|
```bash
|
|
222
269
|
bash skills/j-uncharted/scripts/elicitation-state.sh turn --id <elicitation-id> --node <node-id>
|
|
@@ -233,7 +280,7 @@ Runs once per surviving candidate (a subsystem, a named flow, a directory) after
|
|
|
233
280
|
```
|
|
234
281
|
|
|
235
282
|
Option 3 is the only way past the cap, and it is a per-node, explicit, one-time override — it does not raise the cap for the rest of the run.
|
|
236
|
-
|
|
283
|
+
6. **On convergence** (confirmed, corrected-and-accepted, or resolved via the cap choice above), call:
|
|
237
284
|
|
|
238
285
|
```bash
|
|
239
286
|
bash skills/j-uncharted/scripts/elicitation-state.sh converge --id <elicitation-id> --node <node-id> --note "<one-line summary of what was confirmed>"
|
|
@@ -252,9 +299,9 @@ A whole-codebase `onboard` conversation, or an investigation of a large director
|
|
|
252
299
|
```
|
|
253
300
|
|
|
254
301
|
Idempotent — safe to call again on a resumed `<elicitation-id>` without resetting progress. Choose `<elicitation-id>` so it is stable and re-derivable across sessions (e.g. `onboard-<root-slug>-<date>`, or `segment-investigate-<target-slug>`), since a resuming session must be able to reconstruct it to call `init` again.
|
|
255
|
-
- **`checkpoint` after every converged node
|
|
302
|
+
- **`checkpoint` after every converged node, after the Directory Triage confirmation gate, and after every Familiarity Check answer** — never only at the end. This is what makes a mid-run pause lossless: `checkpoint --id <id> --json <file>` merges arbitrary progress data (triage results, draft nodes not yet converged, per-candidate `verification_depth`, anything else worth surviving a pause) into the state file. `verification_depth` is stored as one object keyed by candidate id — `checkpoint.verification_depth.<candidate-id>` — and, per the script's own merge semantics (see its header), checking one candidate in never clobbers another candidate already recorded there.
|
|
256
303
|
- **`pause` when a session must end before the elicitation has converged.** Immediately after calling `elicitation-state.sh pause --id <elicitation-id>`, write the scrum-master's own `SessionEnd` handoff (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 `handoffs/` convention) with `status: "elicitation_paused"` and both `elicitation_id` and `state_file` set — `hooks/on_session_end.sh` routes that into an `elicitation_resume` trigger on `scrum_triggers.jsonl`, which the next scrum-master session's Drain Scrum Triggers Queue procedure picks up (`agents/scrum-master.md`).
|
|
257
|
-
- **On resume**, read `state_file` directly — every converged node, every flagged node, and the checkpoint data (including the confirmed directory-triage lists) are already there. Do not re-run Directory Triage or re-ask about an already-converged node; resume the Convergence Loop only for nodes still `pending` or explicitly deferred.
|
|
304
|
+
- **On resume**, read `state_file` directly — every converged node, every flagged node, and the checkpoint data (including the confirmed directory-triage lists and any per-candidate `verification_depth` already recorded) are already there. Do not re-run Directory Triage, re-ask the Familiarity Check for a candidate already present under `checkpoint.verification_depth`, or re-ask about an already-converged node; resume the Convergence Loop only for nodes still `pending` or explicitly deferred, and only ask the Familiarity Check for a candidate that has neither.
|
|
258
305
|
- **`complete` when every candidate has converged, been deferred, or been explicitly accepted past the cap.** The state file is left on disk afterward as an audit trail — nothing currently prunes a completed elicitation's state file.
|
|
259
306
|
|
|
260
307
|
---
|