@jenga-ai/agent 1.2.4 → 2.0.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 +97 -91
- package/agents/developer.md +26 -7
- package/agents/scrum-master.md +57 -22
- package/agents/tester.md +68 -4
- package/hooks/on_session_end.sh +40 -1
- package/lib/generate-agent-context.js +18 -1
- package/lib/generate-copilot-instructions.js +18 -1
- package/lib/generate-skill-allow-list.js +191 -0
- package/lib/skill-allow-list.json +43 -0
- package/package.json +35 -20
- package/scripts/apply-j-prefix.sh +230 -0
- package/scripts/consume-context-digest.sh +103 -0
- package/scripts/postinstall.js +25 -0
- package/scripts/sweep-stale-context-digests.sh +132 -0
- package/scripts/validate-board.sh +5 -0
- package/scripts/write-context-digest.sh +230 -0
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/btw/SKILL.md +1 -1
- package/skills/clearify/SKILL.md +1 -1
- package/skills/close-story/SKILL.md +78 -6
- package/skills/close-story/scripts/check-privatized.sh +345 -0
- package/skills/commit/SKILL.md +12 -2
- package/skills/continue/SKILL.md +1 -1
- package/skills/deep-dive/SKILL.md +1 -1
- package/skills/dev-done/SKILL.md +46 -0
- package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
- package/skills/distribute/SKILL.md +1 -1
- package/skills/do/SKILL.md +100 -10
- package/skills/doc/README.md +155 -0
- package/skills/doc/SKILL.md +43 -13
- package/skills/doc/authoring-notes.md +72 -0
- package/skills/doc/scripts/resolve_last_update.py +149 -0
- package/skills/doc-sync/SKILL.md +1 -1
- package/skills/dooo/SKILL.md +1 -1
- package/skills/error/SKILL.md +1 -1
- package/skills/evaluate/SKILL.md +1 -1
- package/skills/examplify/SKILL.md +1 -1
- package/skills/help/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/improve/SKILL.md +1 -1
- package/skills/init/SKILL.md +8 -7
- package/skills/init/assets/scope-thresholds_template.json +7 -0
- package/skills/init/scripts/init.sh +6 -0
- package/skills/j-init/SKILL.md +168 -0
- package/skills/j-init/assets/.gitignore_template +15 -0
- package/skills/j-init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/j-init/assets/directory_structure.txt +14 -0
- package/skills/j-init/assets/scope-thresholds_template.json +7 -0
- package/skills/j-init/assets/strategy_stub_template.md +38 -0
- package/skills/j-init/assets/test-config_template.json +4 -0
- package/skills/j-init/assets/workflow_template.json +30 -0
- package/skills/j-init/scripts/apply-project-visibility.sh +176 -0
- package/skills/j-init/scripts/detect-existing-codebase.sh +166 -0
- package/skills/j-init/scripts/init.sh +116 -0
- package/skills/jbp/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +1 -1
- package/skills/jenga/scripts/render-confirmation.sh +55 -18
- package/skills/jenga-permission-level/SKILL.md +1 -1
- package/skills/lgtm/SKILL.md +1 -1
- package/skills/pi-plan/SKILL.md +1 -1
- package/skills/proceed/SKILL.md +1 -1
- package/skills/publish/SKILL.md +67 -1
- package/skills/publish/adapters/npm-ci.md +60 -4
- package/skills/publish/adapters/npm.md +18 -0
- package/skills/publish/assets/ci-contract.md +27 -0
- package/skills/publish/assets/publish.example.json +27 -0
- package/skills/publish/schemas/publish.schema.json +20 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +50 -1
- package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
- package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
- package/skills/publish/scripts/publish_common.sh +16 -0
- package/skills/publish/scripts/show_history.sh +12 -5
- package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
- package/skills/publish/scripts/write_ledger_entry.sh +92 -2
- package/skills/reconcile/SKILL.md +122 -12
- package/skills/reconcile/assets/report_format.md +17 -0
- package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
- package/skills/reconcile-origin/SKILL.md +1 -1
- package/skills/redo/SKILL.md +1 -1
- package/skills/skillify/SKILL.md +1 -1
- package/skills/spinoff/SKILL.md +1 -1
- package/skills/status/SKILL.md +1 -1
- package/skills/todo/SKILL.md +40 -3
- package/skills/todo/scripts/add_trivial_task.sh +216 -0
- package/skills/todo/scripts/update_story_tasks.py +87 -0
- package/skills/uncharted/SKILL.md +201 -22
- package/skills/uncharted/scripts/directory-triage.sh +342 -0
- package/skills/uncharted/scripts/elicitation-state.sh +457 -0
- package/skills/wtf/SKILL.md +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +90 -2
- package/templates/agent-context.md.tpl +47 -12
- package/templates/copilot-instructions.md.tpl +36 -9
- package/mcp/router/README.md +0 -19
- package/mcp/router/embedder.js +0 -23
- package/mcp/router/index.js +0 -204
- package/mcp/router/matcher.js +0 -87
- package/mcp/router/package-lock.json +0 -1048
- package/mcp/router/package.json +0 -11
- package/mcp/router/skill-index.js +0 -104
- package/skills/route/SKILL.md +0 -180
|
@@ -6,10 +6,36 @@ source "$SCRIPT_DIR/publish_common.sh"
|
|
|
6
6
|
|
|
7
7
|
usage() {
|
|
8
8
|
cat <<'USAGE'
|
|
9
|
-
Usage: write_ledger_entry.sh <target> <adapter> <platform_state> <notes_path> [--yes] [--dry-run] [--version <vX.Y.Z>] [--config <path>]
|
|
9
|
+
Usage: write_ledger_entry.sh <target> <adapter> <platform_state> <notes_path> [--yes] [--dry-run] [--version <vX.Y.Z>] [--config <path>] [--stage-id <id>] [--dist-tag <tag>] [--result <pass|fail>] [--reason <text>]
|
|
10
|
+
|
|
11
|
+
platform_state must be one of: uploaded, partial, dry-run, failed, staged,
|
|
12
|
+
stage_tested, approved, rejected.
|
|
13
|
+
|
|
14
|
+
--result <pass|fail> Optional. Records a stage_tested smoke-test outcome on
|
|
15
|
+
the entry (null when omitted). The npm_stage_inspect.sh
|
|
16
|
+
approve interlock reads this field.
|
|
17
|
+
--reason <text> Optional. Records a free-text reason on the entry —
|
|
18
|
+
used by npm_stage_inspect.sh's `approve --force
|
|
19
|
+
<reason>` to record why the test interlock was
|
|
20
|
+
overridden (null when omitted).
|
|
10
21
|
USAGE
|
|
11
22
|
}
|
|
12
23
|
|
|
24
|
+
# Full accepted platform_state vocabulary: the three pre-existing states
|
|
25
|
+
# actually written by publish_deploy.sh/reconcile_tags.sh today (uploaded,
|
|
26
|
+
# partial, dry-run) plus `failed`, documented as part of the original
|
|
27
|
+
# ledger-entry format (E22_S05_T02) though not currently emitted by any
|
|
28
|
+
# caller, plus the four staged-publishing states added by E22_S09_T02.
|
|
29
|
+
ALLOWED_PLATFORM_STATES=(uploaded partial dry-run failed staged stage_tested approved rejected)
|
|
30
|
+
|
|
31
|
+
platform_state_is_valid() {
|
|
32
|
+
local state="$1" allowed
|
|
33
|
+
for allowed in "${ALLOWED_PLATFORM_STATES[@]}"; do
|
|
34
|
+
[[ "$state" == "$allowed" ]] && return 0
|
|
35
|
+
done
|
|
36
|
+
return 1
|
|
37
|
+
}
|
|
38
|
+
|
|
13
39
|
[[ $# -ge 4 ]] || { usage >&2; exit 1; }
|
|
14
40
|
TARGET_NAME="$1"
|
|
15
41
|
ADAPTER_NAME="$2"
|
|
@@ -21,6 +47,10 @@ AUTO_ACCEPT=0
|
|
|
21
47
|
DRY_RUN=0
|
|
22
48
|
SELECTED_VERSION=''
|
|
23
49
|
CONFIG_PATH="${PUBLISH_CONFIG:-}"
|
|
50
|
+
STAGE_ID=''
|
|
51
|
+
DIST_TAG=''
|
|
52
|
+
RESULT=''
|
|
53
|
+
REASON=''
|
|
24
54
|
|
|
25
55
|
while [[ $# -gt 0 ]]; do
|
|
26
56
|
case "$1" in
|
|
@@ -42,6 +72,26 @@ while [[ $# -gt 0 ]]; do
|
|
|
42
72
|
CONFIG_PATH="$2"
|
|
43
73
|
shift 2
|
|
44
74
|
;;
|
|
75
|
+
--stage-id)
|
|
76
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
77
|
+
STAGE_ID="$2"
|
|
78
|
+
shift 2
|
|
79
|
+
;;
|
|
80
|
+
--dist-tag)
|
|
81
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
82
|
+
DIST_TAG="$2"
|
|
83
|
+
shift 2
|
|
84
|
+
;;
|
|
85
|
+
--result)
|
|
86
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
87
|
+
RESULT="$2"
|
|
88
|
+
shift 2
|
|
89
|
+
;;
|
|
90
|
+
--reason)
|
|
91
|
+
[[ $# -ge 2 ]] || { usage >&2; exit 1; }
|
|
92
|
+
REASON="$2"
|
|
93
|
+
shift 2
|
|
94
|
+
;;
|
|
45
95
|
--help|-h)
|
|
46
96
|
usage
|
|
47
97
|
exit 0
|
|
@@ -54,6 +104,14 @@ while [[ $# -gt 0 ]]; do
|
|
|
54
104
|
done
|
|
55
105
|
|
|
56
106
|
[[ -n "$TARGET_NAME" && -n "$ADAPTER_NAME" && -n "$PLATFORM_STATE" ]] || { usage >&2; exit 1; }
|
|
107
|
+
if ! platform_state_is_valid "$PLATFORM_STATE"; then
|
|
108
|
+
printf "Unknown platform_state '%s'. Expected one of: %s.\n" "$PLATFORM_STATE" "${ALLOWED_PLATFORM_STATES[*]}" >&2
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
if [[ -n "$RESULT" && "$RESULT" != "pass" && "$RESULT" != "fail" ]]; then
|
|
112
|
+
printf "Unknown --result '%s'. Expected 'pass' or 'fail'.\n" "$RESULT" >&2
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
57
115
|
command -v jq >/dev/null 2>&1 || { echo 'jq is required.' >&2; exit 1; }
|
|
58
116
|
|
|
59
117
|
HISTORY_FILE="$(publish_resolve_history_file "$CONFIG_PATH")"
|
|
@@ -98,6 +156,30 @@ else
|
|
|
98
156
|
NOTES_JSON="$(jq -Rn --arg path "$NOTES_PATH_RAW" '$path')"
|
|
99
157
|
fi
|
|
100
158
|
|
|
159
|
+
if [[ -z "$STAGE_ID" ]]; then
|
|
160
|
+
STAGE_ID_JSON='null'
|
|
161
|
+
else
|
|
162
|
+
STAGE_ID_JSON="$(jq -Rn --arg id "$STAGE_ID" '$id')"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
if [[ -z "$DIST_TAG" ]]; then
|
|
166
|
+
DIST_TAG_JSON='null'
|
|
167
|
+
else
|
|
168
|
+
DIST_TAG_JSON="$(jq -Rn --arg tag "$DIST_TAG" '$tag')"
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
if [[ -z "$RESULT" ]]; then
|
|
172
|
+
RESULT_JSON='null'
|
|
173
|
+
else
|
|
174
|
+
RESULT_JSON="$(jq -Rn --arg result "$RESULT" '$result')"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
if [[ -z "$REASON" ]]; then
|
|
178
|
+
REASON_JSON='null'
|
|
179
|
+
else
|
|
180
|
+
REASON_JSON="$(jq -Rn --arg reason "$REASON" '$reason')"
|
|
181
|
+
fi
|
|
182
|
+
|
|
101
183
|
ENTRY_JSON="$(jq -cn \
|
|
102
184
|
--arg id "$ENTRY_ID" \
|
|
103
185
|
--arg version "$VERSION" \
|
|
@@ -109,6 +191,10 @@ ENTRY_JSON="$(jq -cn \
|
|
|
109
191
|
--arg git_tag "$GIT_TAG" \
|
|
110
192
|
--arg commit_sha "$COMMIT_SHA" \
|
|
111
193
|
--argjson release_notes_path "$NOTES_JSON" \
|
|
194
|
+
--argjson stage_id "$STAGE_ID_JSON" \
|
|
195
|
+
--argjson dist_tag "$DIST_TAG_JSON" \
|
|
196
|
+
--argjson result "$RESULT_JSON" \
|
|
197
|
+
--argjson reason "$REASON_JSON" \
|
|
112
198
|
'{
|
|
113
199
|
id: $id,
|
|
114
200
|
version: $version,
|
|
@@ -119,7 +205,11 @@ ENTRY_JSON="$(jq -cn \
|
|
|
119
205
|
release_notes_path: $release_notes_path,
|
|
120
206
|
platform_state: $platform_state,
|
|
121
207
|
git_tag: $git_tag,
|
|
122
|
-
commit_sha: $commit_sha
|
|
208
|
+
commit_sha: $commit_sha,
|
|
209
|
+
stage_id: $stage_id,
|
|
210
|
+
dist_tag: $dist_tag,
|
|
211
|
+
result: $result,
|
|
212
|
+
reason: $reason
|
|
123
213
|
}')"
|
|
124
214
|
|
|
125
215
|
publish_append_history_entry "$HISTORY_FILE" "$ENTRY_JSON"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: reconcile
|
|
2
|
+
name: j:reconcile
|
|
3
3
|
description: Reconcile the scrum board with actual implementation state. Cross-checks every task's board status against git history and worktrees, merges orphaned worktree branches, demotes unimplemented "Done" items, promotes secretly-implemented items, flags code with no board provenance and offers /uncharted segment for it, and cleans stale entries from todo.md. Use when the board feels out of sync, after a big merge session, when tasks were completed outside the normal workflow, or when todo.md has grown stale. Trigger on phrases like "sync the board", "clean up the board", "reconcile", "board is out of date", "todo is stale", or "check what's really done".
|
|
4
4
|
metadata:
|
|
5
5
|
prefered_agent: scrum-master
|
|
@@ -9,21 +9,98 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
Walks the full board (epics → stories → tasks), verifies each item's status against what actually exists in git, and fixes any drift. Also cleans `project/todo.md` of entries that are already done. Then runs the same check in reverse — code that exists with no board item and no EST-tagged commit behind it — and offers `/uncharted segment` for what it finds.
|
|
11
11
|
|
|
12
|
+
## Scope argument
|
|
13
|
+
|
|
14
|
+
`/reconcile` optionally accepts a single scope argument that bounds every phase below to a subset
|
|
15
|
+
of the board instead of always walking the whole thing:
|
|
16
|
+
|
|
17
|
+
| Argument | Resolves to |
|
|
18
|
+
|---|---|
|
|
19
|
+
| _(none)_ | Full board — unchanged from today's behavior. |
|
|
20
|
+
| `E12` (bare epic id) | That epic, in full. |
|
|
21
|
+
| `E12_S03` or `S03` (bare story id) | The story's **containing epic, in full** — not just the named story. See "Default-scope-to-epic rule" below. |
|
|
22
|
+
| `E12_S03_T01` (bare task id) | The task's **containing epic, in full** — same default-scope-to-epic rule. |
|
|
23
|
+
| `S03-05` or `E12_S03-05` (a story range) | **Exactly** the named stories (and their tasks), plus rollup limited to only the epic(s) those stories belong to. Does not expand to unrelated stories in the same epic(s). Only story-level ranges are supported — an epic-level range (`E01-03`) or a task-level range (`T01-03`) is rejected with a clear reason. |
|
|
24
|
+
| Anything unresolvable (unknown id, malformed range, an ambiguous partial) | A clear error before any board file is touched — no partial result, no silent full-board fallback. |
|
|
25
|
+
|
|
26
|
+
**Default-scope-to-epic rule (settled — do not relitigate):** a scope argument naming a story or
|
|
27
|
+
task (not an epic) always resolves to that item's *containing epic, in full* — never just the
|
|
28
|
+
named story/task in isolation. Story and epic rollup (Phase 4) cannot be evaluated correctly
|
|
29
|
+
without seeing all sibling stories/tasks, so scoping to a lone story or task would risk an
|
|
30
|
+
incorrect rollup decision. If you want to reconcile *exactly* a set of stories without pulling in
|
|
31
|
+
the rest of their epic(s), use the range syntax (`S03-05`) instead — a range does not apply the
|
|
32
|
+
default-scope-to-epic expansion.
|
|
33
|
+
|
|
34
|
+
Omitting the argument entirely keeps today's full-board behavior completely unchanged, in every
|
|
35
|
+
phase below.
|
|
36
|
+
|
|
37
|
+
Scope resolution itself is deterministic and handled entirely by
|
|
38
|
+
`skills/reconcile/scripts/resolve-reconcile-scope.sh` (see Phase 0 immediately below) — this skill
|
|
39
|
+
only interprets that script's output; it does not re-parse scope arguments or re-derive range
|
|
40
|
+
expansion.
|
|
41
|
+
|
|
12
42
|
## Instructions
|
|
13
43
|
|
|
14
44
|
### 0. Read configuration
|
|
15
45
|
Read `project/configs/workflow.json` for board paths. Fall back to `project/board/` if missing.
|
|
16
46
|
The statuses that count as "completed" are: **Done**, **Passed**, **Passed with remarks**.
|
|
17
47
|
|
|
48
|
+
### Phase 0 — Resolve scope
|
|
49
|
+
Before touching anything else, resolve the scope argument (if any — see "Scope argument" above)
|
|
50
|
+
into a concrete set of epic/story/task ids:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
skills/reconcile/scripts/resolve-reconcile-scope.sh <scope-argument>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Omit `<scope-argument>` entirely for an unscoped, full-board run (the script's own no-argument
|
|
57
|
+
path, matching today's default).
|
|
58
|
+
|
|
59
|
+
- **On `{"status":"error","reason":"..."}` (non-zero exit)** — stop immediately. Report the
|
|
60
|
+
`reason` to the user as the reconcile failure. Do **not** proceed to Phase 1, and do not touch
|
|
61
|
+
any board file, `project/todo.md`, or write a reconcile report. This is the enforcement point
|
|
62
|
+
for the story's AC: an invalid or unresolvable scope argument fails with a clear error before
|
|
63
|
+
any board file is touched — no partial result and no silent full-board fallback.
|
|
64
|
+
- **On success** — stdout is a single JSON object:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"scope_type": "full" | "epic" | "range",
|
|
68
|
+
"epic_ids": [...],
|
|
69
|
+
"story_ids": [...],
|
|
70
|
+
"task_ids": [...],
|
|
71
|
+
"owned_path_hints": [...]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
Carry `scope_type`, `epic_ids`, `story_ids`, `task_ids`, and `owned_path_hints` forward as **the
|
|
75
|
+
resolved scope** through every phase below. When `scope_type` is `"full"`, every phase runs
|
|
76
|
+
exactly as documented for the unscoped case — nothing below changes behavior or output. When
|
|
77
|
+
`scope_type` is `"epic"` or `"range"`, each phase's "when scoped" instructions apply instead.
|
|
78
|
+
|
|
79
|
+
`owned_path_hints` is best-effort and non-authoritative (see Phase 5) — treat it only as a
|
|
80
|
+
positive filter, never as proof that an unlisted path is unrelated to the scope.
|
|
81
|
+
|
|
18
82
|
### 1. Snapshot the board
|
|
19
|
-
|
|
83
|
+
**When unscoped (`scope_type: "full"`):** scan every file in `epics/`, `stories/`, and `tasks/`,
|
|
84
|
+
exactly as before.
|
|
85
|
+
|
|
86
|
+
**When scoped (`scope_type: "epic"` or `"range"`):** only scan the board files whose id is in the
|
|
87
|
+
resolved `epic_ids` / `story_ids` / `task_ids` — for `"epic"` scope this is the epic file itself
|
|
88
|
+
plus every story/task file under it; for `"range"` scope this is exactly the named story/task
|
|
89
|
+
files plus the epic file(s) in `epic_ids` (the epic(s) those stories belong to). Files for
|
|
90
|
+
out-of-scope items are not scanned and play no further part in this run.
|
|
91
|
+
|
|
92
|
+
For each scanned item, record:
|
|
20
93
|
- `id`, `title`, `status` (the **pre-reconcile** status — needed in phase 4)
|
|
21
94
|
- `date_completed` (if set)
|
|
22
95
|
|
|
23
|
-
Also read `project/todo.md
|
|
96
|
+
Also read `project/todo.md`. When unscoped, parse every non-comment, non-blank line into the list
|
|
97
|
+
of todo entries, as before. When scoped, only parse lines that reference an id in the resolved
|
|
98
|
+
scope (`epic_ids` / `story_ids` / `task_ids`) — out-of-scope lines are left out of the list
|
|
99
|
+
entirely, which is what keeps Phase 6 from touching them later.
|
|
24
100
|
|
|
25
101
|
### 2. Verify "completed" tasks — are they really implemented?
|
|
26
|
-
For every task
|
|
102
|
+
For every task recorded in Phase 1 (i.e. every task on the board when unscoped, or only the
|
|
103
|
+
in-scope tasks when scoped) whose status is a completed status:
|
|
27
104
|
|
|
28
105
|
1. **Search git history** — run `git log --all --oneline --grep="<task_id>"` (e.g. `E01_S01_T01`). A matching commit is strong evidence of implementation.
|
|
29
106
|
2. **Check documentation artefacts** — look for a plan or summary file under `project/documentation/plans/` or `project/documentation/summaries/` whose name contains the task ID.
|
|
@@ -44,7 +121,8 @@ If implementation **cannot be confirmed**:
|
|
|
44
121
|
- Report the demotion.
|
|
45
122
|
|
|
46
123
|
### 3. Verify "incomplete" tasks — are they secretly implemented?
|
|
47
|
-
For every task
|
|
124
|
+
For every task recorded in Phase 1 (i.e. every task on the board when unscoped, or only the
|
|
125
|
+
in-scope tasks when scoped) whose status is **not** a completed status (Pending, In Progress, Running, Blocked, etc.):
|
|
48
126
|
|
|
49
127
|
1. **Search git history** for commits referencing the task ID.
|
|
50
128
|
2. **Check documentation artefacts** as in phase 2.
|
|
@@ -62,14 +140,25 @@ If implementation **is confirmed**:
|
|
|
62
140
|
If implementation **is not confirmed** — no action needed; the status is already correct.
|
|
63
141
|
|
|
64
142
|
### 4. Roll up story and epic statuses
|
|
65
|
-
After all tasks have been reconciled
|
|
143
|
+
After all tasks recorded in Phase 1 have been reconciled (every task on the board when unscoped,
|
|
144
|
+
or only the in-scope tasks when scoped):
|
|
66
145
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
146
|
+
- **When unscoped:** roll up every story and every epic on the board, exactly as before.
|
|
147
|
+
- **When scoped (`scope_type: "epic"`):** roll up only the in-scope stories and the one in-scope
|
|
148
|
+
epic.
|
|
149
|
+
- **When scoped (`scope_type: "range"`):** roll up only the in-scope stories, and only the
|
|
150
|
+
epic(s) in `epic_ids` (the epic(s) those named stories belong to) — never an unrelated story in
|
|
151
|
+
the same epic(s) that wasn't itself named in the range, per the story's AC.
|
|
152
|
+
|
|
153
|
+
For each story being rolled up: if all of its tasks are now in a completed status, set the story
|
|
154
|
+
to **Done** (if not already). If any task was demoted, and the story was previously completed, set
|
|
155
|
+
the story back to **In Progress**. For each epic being rolled up: apply the same roll-up logic
|
|
156
|
+
over its stories.
|
|
69
157
|
|
|
70
158
|
#### DoD Gap Detection
|
|
71
159
|
|
|
72
|
-
After rolling up statuses, scan every story
|
|
160
|
+
After rolling up statuses, scan every story being rolled up (every completed-status story on the
|
|
161
|
+
board when unscoped, or only the in-scope stories when scoped) whose status is a completed status (`Passed`, `Passed with remarks`, `Done`) for unchecked Definition of Done items:
|
|
73
162
|
|
|
74
163
|
1. Read the story file and locate the `## Definition of Done` section. If the section is absent, skip this story gracefully (no error).
|
|
75
164
|
2. Scan the DoD section for any lines matching `^- \[ \]` (unchecked checkboxes).
|
|
@@ -108,6 +197,22 @@ re-derive linkage yourself, and do not substitute a `grep` over `project/board/`
|
|
|
108
197
|
fails — a second answer to "is this path on the board" is what that reuse exists to prevent.
|
|
109
198
|
If the script exits non-zero, report the failure in the reconcile report and continue to phase 6.
|
|
110
199
|
|
|
200
|
+
#### Scoping (best-effort, not authoritative)
|
|
201
|
+
`detect-unlinked-code.sh` itself takes no scope argument and is never modified by this phase — it
|
|
202
|
+
always scans the whole repository, exactly as it does today. When a scope was resolved in Phase 0
|
|
203
|
+
and `scope_type` is **not** `"full"`, filter the *report* (not the script's own output) down to
|
|
204
|
+
paths that fall under one of the resolved scope's `owned_path_hints` prefixes before presenting
|
|
205
|
+
`groups[]` / `covered_groups[]` / `not_checked[]` to the user; a path outside every
|
|
206
|
+
`owned_path_hints` prefix is left out of the scoped report entirely.
|
|
207
|
+
|
|
208
|
+
This filtering happens only in this judgement/presentation layer, and it is explicitly
|
|
209
|
+
**best-effort, not authoritative**: `owned_path_hints` is derived only from each in-scope item's
|
|
210
|
+
own `docs:` frontmatter list (see `resolve-reconcile-scope.sh`'s own contract), so it can be empty
|
|
211
|
+
or simply not mention every path the scope actually owns. State this caveat directly in the report
|
|
212
|
+
(see `assets/report_format.md`) — never claim a path is "outside the epic" solely because it
|
|
213
|
+
didn't happen to appear in `owned_path_hints`; say the filtering is best-effort instead. When
|
|
214
|
+
`scope_type: "full"`, this phase is completely unchanged — report every finding, unfiltered.
|
|
215
|
+
|
|
111
216
|
#### Reading the result
|
|
112
217
|
|
|
113
218
|
- **`groups[]`** — the actionable findings, sorted by `unlinked_count` descending. Each entry is
|
|
@@ -176,15 +281,20 @@ phase. If the user picks option 1 or 2, finish the reconcile pass first, then ha
|
|
|
176
281
|
`/uncharted segment` at the end so the board is consistent before new items are written.
|
|
177
282
|
|
|
178
283
|
### 6. Clean `project/todo.md`
|
|
179
|
-
Walk the todo entries parsed in phase 1
|
|
284
|
+
Walk the todo entries parsed in phase 1 — when scoped, that list already excludes any entry that
|
|
285
|
+
doesn't reference an in-scope id (Phase 1 only parsed in-scope lines), so this phase never removes
|
|
286
|
+
or comments out an out-of-scope entry even if it would otherwise qualify under the rules below.
|
|
287
|
+
When unscoped, every entry from phase 1 is eligible, exactly as before.
|
|
180
288
|
|
|
181
289
|
- **Already-done entries** — if an entry references a task/story/epic whose pre-reconcile status (from the snapshot in phase 1) was already a completed status **and** whose implementation has been confirmed (phase 2), **remove the line entirely** from `project/todo.md`.
|
|
182
290
|
- **Newly-reconciled entries** — entries that were commented out in phase 3 stay as `<!-- RECONCILED: ... -->`.
|
|
183
291
|
- If `project/todo.md` is left with only the header, the format comment, and blank lines, delete the file.
|
|
184
292
|
|
|
185
293
|
### 7. Print a summary
|
|
186
|
-
Output a reconciliation report using the format in `assets/report_format.md
|
|
187
|
-
|
|
294
|
+
Output a reconciliation report using the format in `assets/report_format.md` — the report's very
|
|
295
|
+
first substantive line is always `Scope: ...`, reflecting the scope resolved in Phase 0 (see that
|
|
296
|
+
file's Section rules for the exact forms), so a scoped run is never mistaken for a full pass —
|
|
297
|
+
with one additional section from phase 5 placed just before `TODO CLEANUP`:
|
|
188
298
|
|
|
189
299
|
```
|
|
190
300
|
🗺️ UNLINKED CODE (no board item for the files or their directory)
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
RECONCILIATION REPORT
|
|
6
6
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
7
7
|
|
|
8
|
+
Scope: <resolved scope — see forms below>
|
|
9
|
+
|
|
8
10
|
📊 Scanned: <N> epics, <N> stories, <N> tasks
|
|
9
11
|
|
|
10
12
|
⬇️ DEMOTED (were Done/Passed → now Pending)
|
|
@@ -37,8 +39,23 @@
|
|
|
37
39
|
|
|
38
40
|
### Section rules
|
|
39
41
|
|
|
42
|
+
- `Scope:` is always the very first substantive line of the report — above `📊 Scanned:` — so a
|
|
43
|
+
scoped run is never mistaken for a full pass. Its exact form depends on the scope resolved in
|
|
44
|
+
`/reconcile`'s Phase 0:
|
|
45
|
+
- Unscoped run (`scope_type: "full"`): `Scope: full board`
|
|
46
|
+
- Epic scope (`scope_type: "epic"`): `Scope: E12 (full epic)`
|
|
47
|
+
- Range scope (`scope_type: "range"`): `Scope: S03-05 (2 stories, epic E12)` — name the epic(s)
|
|
48
|
+
the range's resolved stories belong to (`epic_ids`); if the range spans more than one epic,
|
|
49
|
+
list all of them, e.g. `Scope: S03-05 (3 stories, epics E12, E14)`.
|
|
50
|
+
When `Scope:` is not `full board`, the `📊 Scanned:` line's counts reflect the resolved scope's
|
|
51
|
+
own epics/stories/tasks only — not the whole board's.
|
|
40
52
|
- Omit any section that has zero items (e.g. if nothing was demoted, skip the DEMOTED block entirely).
|
|
41
53
|
- The MERGED section should include the branch name that was merged.
|
|
42
54
|
- The TODO CLEANUP section is always shown if `project/todo.md` existed at the start, even if zero changes were made (in that case show all counts as 0).
|
|
43
55
|
- If `project/todo.md` did not exist, omit the TODO CLEANUP section.
|
|
44
56
|
- The DOD GAPS section is omitted if no completed stories have unchecked DoD checkboxes.
|
|
57
|
+
- When `Scope:` is not `full board`, the UNLINKED CODE section's `groups[]` / `covered_groups[]` /
|
|
58
|
+
`not_checked[]` have been filtered to the resolved scope's `owned_path_hints` on a best-effort,
|
|
59
|
+
non-authoritative basis (see `/reconcile`'s Phase 5) — append "(scope-filtered, best-effort)" to
|
|
60
|
+
the `🗺️ UNLINKED CODE` heading in that case, since `owned_path_hints` can be incomplete and a
|
|
61
|
+
path missing from it is not proof the path lies outside the scope.
|