@danmoisan/drm-copilot-mcp 1.1.17 → 1.1.18
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/out/mcp-server.js +500 -106
- package/package.json +4 -3
- package/resources/claude-customizations/.claude/agents/feature-review.md +10 -1
- package/resources/claude-customizations/.claude/agents/orchestrator.md +16 -3
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +6 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +375 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +119 -3
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +1 -1
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +169 -5
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +4 -15
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +13 -6
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +30 -12
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +18 -3
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +5 -1
- package/resources/claude-customizations/pack-manifests/core.json +2 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +5 -3
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +3 -1
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +30 -7
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +18 -3
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +8 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +4 -0
|
@@ -4,16 +4,19 @@
|
|
|
4
4
|
# every file within the 500-line cap. Provides the git-binary test seam
|
|
5
5
|
# (cleanup_wt_git), branch/worktree enumeration (enumerate_branches,
|
|
6
6
|
# parse_worktree_list), path normalization (normalize_wt_path), the
|
|
7
|
-
# current-worktree/branch and base-branch protection set (compute_protected),
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# current-worktree/branch and base-branch protection set (compute_protected), the
|
|
8
|
+
# main-freshness warning (check_main_freshness), the shared absolute-path predicate
|
|
9
|
+
# (cleanup_wt_is_absolute_path), and the worktree-tracking scan roots
|
|
10
|
+
# (cleanup_wt_split_roots, cleanup_wt_derive_scan_roots, cleanup_wt_scan_roots). The
|
|
11
|
+
# classification ladder, consolidation, deletion, and the CLI live in sibling files
|
|
12
|
+
# (cleanup_worktrees_lib.sh, cleanup_worktrees_actions_lib.sh, cleanup-worktrees.sh).
|
|
11
13
|
#
|
|
12
14
|
# Sourcing contract: defines functions and the single constant CLEANUP_WT_BASE_BRANCH
|
|
13
15
|
# and runs no other work at source time, so wrapper and bats suites source it safely.
|
|
14
16
|
# It MUST be sourced before cleanup_worktrees_lib.sh, whose classification functions
|
|
15
17
|
# call cleanup_wt_git, parse_worktree_list, compute_protected, and normalize_wt_path
|
|
16
|
-
# defined here.
|
|
18
|
+
# defined here. The filesystem-scan helper cleanup_worktrees_scan_helper.sh runs as a
|
|
19
|
+
# separate process and sources this library directly for cleanup_wt_is_absolute_path.
|
|
17
20
|
#
|
|
18
21
|
# All git commands go through cleanup_wt_git so tests can stub the git binary via
|
|
19
22
|
# CLEANUP_WT_GIT_BIN. Git exit-code capture rule: the `git worktree list --porcelain`
|
|
@@ -250,3 +253,164 @@ check_main_freshness() {
|
|
|
250
253
|
fi
|
|
251
254
|
return 0
|
|
252
255
|
}
|
|
256
|
+
|
|
257
|
+
cleanup_wt_is_absolute_path() {
|
|
258
|
+
# Return 0 when <path> is absolute, else 1. Pure: no filesystem access.
|
|
259
|
+
#
|
|
260
|
+
# Absolute forms are a leading `/` (POSIX paths, MSYS `/c/...` paths, and `//server`
|
|
261
|
+
# UNC paths) and a drive letter followed by `/` or `\` (`C:/...`, `c:/...`,
|
|
262
|
+
# `C:\...`), which is the form Git for Windows writes into a worktree's `.git`
|
|
263
|
+
# pointer file (issue #706). A drive letter with no separator (`C:rel`) is
|
|
264
|
+
# drive-relative and is not absolute; an empty path is not absolute. This is the one
|
|
265
|
+
# absolute-path predicate of the skill: the preserve library and the filesystem-scan
|
|
266
|
+
# helper both call it.
|
|
267
|
+
#
|
|
268
|
+
# Args: $1 = path. Returns 0 (absolute) or 1 (not absolute).
|
|
269
|
+
local path=${1:-}
|
|
270
|
+
[[ $path == /* || $path == [A-Za-z]:[/\\]* ]]
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
cleanup_wt_split_roots() {
|
|
274
|
+
# Split a CLEANUP_WT_ORPHAN_ROOTS value into absolute roots, one per line.
|
|
275
|
+
#
|
|
276
|
+
# Entries are separated by `;`, by a newline, or by `:`. A `:` is part of the path
|
|
277
|
+
# instead when the text before it in the current entry is exactly one ASCII letter
|
|
278
|
+
# and the character after it is `/` or `\` (a drive letter, as in `C:/x`). Empty
|
|
279
|
+
# entries are dropped. An entry that is not absolute (cleanup_wt_is_absolute_path) is
|
|
280
|
+
# dropped with a one-line stderr diagnostic naming it, because a relative root would
|
|
281
|
+
# be resolved against the process's current working directory.
|
|
282
|
+
#
|
|
283
|
+
# The value is walked one character at a time with parameter expansion and never
|
|
284
|
+
# word-split, so no pathname expansion occurs and a `*` is emitted literally.
|
|
285
|
+
#
|
|
286
|
+
# Args: $1 = the override value. Always returns 0.
|
|
287
|
+
local value=${1:-}
|
|
288
|
+
local -a entries=()
|
|
289
|
+
local entry="" ch next i
|
|
290
|
+
for ((i = 0; i < ${#value}; i++)); do
|
|
291
|
+
ch=${value:i:1}
|
|
292
|
+
if [[ $ch == ';' || $ch == $'\n' ]]; then
|
|
293
|
+
entries+=("$entry")
|
|
294
|
+
entry=""
|
|
295
|
+
elif [[ $ch == ':' ]]; then
|
|
296
|
+
next=${value:i+1:1}
|
|
297
|
+
if [[ ${#entry} -eq 1 && $entry == [A-Za-z] && $next == [/\\] ]]; then
|
|
298
|
+
entry+=$ch
|
|
299
|
+
else
|
|
300
|
+
entries+=("$entry")
|
|
301
|
+
entry=""
|
|
302
|
+
fi
|
|
303
|
+
else
|
|
304
|
+
entry+=$ch
|
|
305
|
+
fi
|
|
306
|
+
done
|
|
307
|
+
entries+=("$entry")
|
|
308
|
+
for entry in "${entries[@]}"; do
|
|
309
|
+
[[ -z $entry ]] && continue
|
|
310
|
+
if ! cleanup_wt_is_absolute_path "$entry"; then
|
|
311
|
+
printf 'cleanup-worktrees: CLEANUP_WT_ORPHAN_ROOTS entry is not absolute, dropped: %s\n' "$entry" >&2
|
|
312
|
+
continue
|
|
313
|
+
fi
|
|
314
|
+
printf '%s\n' "$entry"
|
|
315
|
+
done
|
|
316
|
+
return 0
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
cleanup_wt_derive_scan_roots() {
|
|
320
|
+
# Echo the registration-derived scan roots, one per line.
|
|
321
|
+
#
|
|
322
|
+
# Input is parse_worktree_list output; its first record is the main worktree. The
|
|
323
|
+
# candidate for every later record is the parent directory of its path (backslashes
|
|
324
|
+
# converted to `/`). A candidate is kept only when its normalize_wt_path value N:
|
|
325
|
+
# - is not the main worktree's normalized path and is not one of its ancestors
|
|
326
|
+
# (scanning an ancestor would list the main worktree's siblings), and
|
|
327
|
+
# - is not equal to, and not inside, any registered worktree, main included
|
|
328
|
+
# (its subdirectories are that worktree's own content, not worktrees).
|
|
329
|
+
# Kept candidates are emitted in LC_ALL=C order of N, once per N, using the first
|
|
330
|
+
# spelling seen in record order.
|
|
331
|
+
#
|
|
332
|
+
# Args: $1 = parse_worktree_list output. Always returns 0.
|
|
333
|
+
local records=${1:-}
|
|
334
|
+
local -a paths=() norms=()
|
|
335
|
+
local record path
|
|
336
|
+
while IFS= read -r record; do
|
|
337
|
+
[[ -z $record ]] && continue
|
|
338
|
+
path=${record%%|*}
|
|
339
|
+
paths+=("$path")
|
|
340
|
+
norms+=("$(normalize_wt_path "$path")")
|
|
341
|
+
done <<<"$records"
|
|
342
|
+
((${#paths[@]} < 2)) && return 0
|
|
343
|
+
local main_norm=${norms[0]}
|
|
344
|
+
local -A seen=()
|
|
345
|
+
local -a kept=()
|
|
346
|
+
local i p parent n w inside
|
|
347
|
+
for ((i = 1; i < ${#paths[@]}; i++)); do
|
|
348
|
+
p=${paths[i]//\\//}
|
|
349
|
+
parent=${p%/*}
|
|
350
|
+
[[ -z $parent || $parent == "$p" ]] && continue
|
|
351
|
+
n=$(normalize_wt_path "$parent")
|
|
352
|
+
[[ -z $n || -n ${seen[$n]:-} ]] && continue
|
|
353
|
+
[[ $n == "$main_norm" || $main_norm == "$n"/* ]] && continue
|
|
354
|
+
inside=0
|
|
355
|
+
for w in "${norms[@]}"; do
|
|
356
|
+
[[ -z $w ]] && continue
|
|
357
|
+
if [[ $n == "$w" || $n == "$w"/* ]]; then
|
|
358
|
+
inside=1
|
|
359
|
+
break
|
|
360
|
+
fi
|
|
361
|
+
done
|
|
362
|
+
((inside == 1)) && continue
|
|
363
|
+
seen[$n]=1
|
|
364
|
+
kept+=("$n|$parent")
|
|
365
|
+
done
|
|
366
|
+
((${#kept[@]} == 0)) && return 0
|
|
367
|
+
local line
|
|
368
|
+
while IFS= read -r line; do
|
|
369
|
+
printf '%s\n' "${line#*|}"
|
|
370
|
+
done < <(printf '%s\n' "${kept[@]}" | LC_ALL=C sort -t '|' -k1,1)
|
|
371
|
+
return 0
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
cleanup_wt_scan_roots() {
|
|
375
|
+
# Echo the worktree-tracking roots to scan, one per line.
|
|
376
|
+
#
|
|
377
|
+
# Configured roots come first: the CLEANUP_WT_ORPHAN_ROOTS entries
|
|
378
|
+
# (cleanup_wt_split_roots) when that variable is set, otherwise the default pair
|
|
379
|
+
# derived from the main worktree path (the first `git worktree list --porcelain`
|
|
380
|
+
# stanza, the same derivation consolidation_worktree_path uses in
|
|
381
|
+
# cleanup_worktrees_actions_lib.sh): `<main>/.claude/worktrees` then `<main>-wt`.
|
|
382
|
+
# Registration-derived roots (cleanup_wt_derive_scan_roots) are always appended.
|
|
383
|
+
# The combined list is deduplicated by normalize_wt_path, keeping the first spelling.
|
|
384
|
+
#
|
|
385
|
+
# The listing is read once (`out=$(...) || rc=$?`). On a parse_worktree_list hard
|
|
386
|
+
# failure nothing is derived from it: with no override no root is emitted at all,
|
|
387
|
+
# rather than a bare relative path resolved against the current working directory,
|
|
388
|
+
# and with an override exactly the override roots are emitted.
|
|
389
|
+
# cleanup_wt_scan_records returns 0 with no record for an empty root list, so the
|
|
390
|
+
# advisory records degrade to silence rather than to a misleading scan.
|
|
391
|
+
# Always returns 0.
|
|
392
|
+
local out rc=0 first_record main_wt="" configured="" derived=""
|
|
393
|
+
out=$(parse_worktree_list) || rc=$?
|
|
394
|
+
if ((rc == 0)); then
|
|
395
|
+
first_record=${out%%$'\n'*}
|
|
396
|
+
main_wt=${first_record%%|*}
|
|
397
|
+
derived=$(cleanup_wt_derive_scan_roots "$out")
|
|
398
|
+
fi
|
|
399
|
+
if [[ -n ${CLEANUP_WT_ORPHAN_ROOTS:-} ]]; then
|
|
400
|
+
configured=$(cleanup_wt_split_roots "$CLEANUP_WT_ORPHAN_ROOTS")
|
|
401
|
+
elif [[ -n $main_wt ]]; then
|
|
402
|
+
configured="${main_wt}/.claude/worktrees"$'\n'"${main_wt}-wt"
|
|
403
|
+
fi
|
|
404
|
+
local -A seen=()
|
|
405
|
+
local root n
|
|
406
|
+
while IFS= read -r root; do
|
|
407
|
+
[[ -z $root ]] && continue
|
|
408
|
+
n=$(normalize_wt_path "$root")
|
|
409
|
+
# A root of `/` normalizes to empty, which is not a valid array key.
|
|
410
|
+
[[ -z $n ]] && n=/
|
|
411
|
+
[[ -n ${seen[$n]:-} ]] && continue
|
|
412
|
+
seen[$n]=1
|
|
413
|
+
printf '%s\n' "$root"
|
|
414
|
+
done <<<"${configured}"$'\n'"${derived}"
|
|
415
|
+
return 0
|
|
416
|
+
}
|
|
@@ -158,7 +158,7 @@ preserve_relative_path_reason() {
|
|
|
158
158
|
local name=${1-} val=${2-}
|
|
159
159
|
if [[ -z $val ]]; then
|
|
160
160
|
printf '%s is empty' "$name"
|
|
161
|
-
elif
|
|
161
|
+
elif cleanup_wt_is_absolute_path "$val"; then
|
|
162
162
|
printf '%s is absolute: %s' "$name" "$val"
|
|
163
163
|
elif [[ $val == ".." || $val == "../"* || $val == *"/../"* || $val == *"/.." ]]; then
|
|
164
164
|
printf '%s contains a .. segment: %s' "$name" "$val"
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
# whose gitdir pointer no longer resolves, and the ancestor relationship between two
|
|
7
7
|
# branches that both resolve NOT_MERGED. The enumeration/protection seam
|
|
8
8
|
# (cleanup_wt_git, enumerate_branches, parse_worktree_list, normalize_wt_path,
|
|
9
|
-
# compute_protected, check_main_freshness) lives in
|
|
10
|
-
# and the classification ladder (classify_branch)
|
|
11
|
-
# both MUST be sourced before this file. Keeping
|
|
12
|
-
# every file within the 500-line cap.
|
|
9
|
+
# compute_protected, check_main_freshness, cleanup_wt_scan_roots) lives in
|
|
10
|
+
# cleanup_worktrees_enumerate_lib.sh and the classification ladder (classify_branch)
|
|
11
|
+
# lives in cleanup_worktrees_lib.sh; both MUST be sourced before this file. Keeping
|
|
12
|
+
# these records in a sibling file keeps every file within the 500-line cap.
|
|
13
13
|
#
|
|
14
14
|
# Sourcing contract: this library defines functions only; it never runs work at source
|
|
15
15
|
# time, so the wrapper and the bats suites can source it without side effects.
|
|
@@ -111,47 +111,10 @@ scan_stale_refs() {
|
|
|
111
111
|
return 0
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
cleanup_wt_scan_roots() {
|
|
115
|
-
# Echo the worktree-tracking roots to scan, one per line.
|
|
116
|
-
#
|
|
117
|
-
# CLEANUP_WT_ORPHAN_ROOTS overrides the derivation with a colon-separated list.
|
|
118
|
-
# Otherwise BOTH roots derive from the main worktree path — the first
|
|
119
|
-
# `git worktree list --porcelain` stanza, the same derivation
|
|
120
|
-
# consolidation_worktree_path uses in cleanup_worktrees_actions_lib.sh. They are
|
|
121
|
-
# `<main-worktree-path>` joined with `.claude/worktrees` (the agent worktree folder)
|
|
122
|
-
# and `<main-worktree-path>-wt` (the sibling worktree folder), in that order.
|
|
123
|
-
#
|
|
124
|
-
# Deriving both from one read means a parse_worktree_list hard failure emits no root
|
|
125
|
-
# at all, rather than a bare relative path that would be resolved against whatever
|
|
126
|
-
# the process's current working directory happened to be. cleanup_wt_scan_records
|
|
127
|
-
# returns 0 with no record for an empty root list, so the advisory records degrade to
|
|
128
|
-
# silence rather than to a misleading scan. Always returns 0.
|
|
129
|
-
local override=${CLEANUP_WT_ORPHAN_ROOTS:-}
|
|
130
|
-
if [[ -n $override ]]; then
|
|
131
|
-
local IFS=:
|
|
132
|
-
local part
|
|
133
|
-
for part in $override; do
|
|
134
|
-
[[ -n $part ]] && printf '%s\n' "$part"
|
|
135
|
-
done
|
|
136
|
-
return 0
|
|
137
|
-
fi
|
|
138
|
-
local out rc=0 first_record main_wt=""
|
|
139
|
-
out=$(parse_worktree_list) || rc=$?
|
|
140
|
-
if ((rc != 0)); then
|
|
141
|
-
return 0
|
|
142
|
-
fi
|
|
143
|
-
first_record=${out%%$'\n'*}
|
|
144
|
-
main_wt=${first_record%%|*}
|
|
145
|
-
if [[ -n $main_wt ]]; then
|
|
146
|
-
printf '%s\n' "${main_wt}/.claude/worktrees"
|
|
147
|
-
printf '%s\n' "${main_wt}-wt"
|
|
148
|
-
fi
|
|
149
|
-
return 0
|
|
150
|
-
}
|
|
151
|
-
|
|
152
114
|
cleanup_wt_scan_records() {
|
|
153
115
|
# Echo the raw `<path>|<has_gitfile>|<gitdir_target_exists>|<size>` records for every
|
|
154
|
-
# candidate directory under the resolved scan roots, one per line.
|
|
116
|
+
# candidate directory under the resolved scan roots, one per line. The roots come
|
|
117
|
+
# from cleanup_wt_scan_roots in cleanup_worktrees_enumerate_lib.sh.
|
|
155
118
|
#
|
|
156
119
|
# Every filesystem read goes through the CLEANUP_WT_SCAN_BIN seam, so the bats suites
|
|
157
120
|
# replay canned records instead of touching the real filesystem. The scan output is
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
# the SHELL_QC_<TOOL>_BIN seams substitute a checked-in stand-in for a real dependency.
|
|
41
41
|
# An empty value is treated as missing and falls back to `.git`.
|
|
42
42
|
set -euo pipefail
|
|
43
|
+
# shellcheck source=.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh
|
|
44
|
+
# shellcheck disable=SC1091
|
|
45
|
+
source "$(dirname -- "${BASH_SOURCE[0]}")/cleanup_worktrees_enumerate_lib.sh"
|
|
43
46
|
|
|
44
47
|
scan_helper_dir_size() {
|
|
45
48
|
# Echo the best-effort human-readable size of a directory, or `unknown`.
|
|
@@ -70,20 +73,6 @@ scan_helper_gitfile_name() {
|
|
|
70
73
|
printf '%s\n' ".git"
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
scan_helper_is_absolute_path() {
|
|
74
|
-
# Return 0 when <path> is absolute, else 1. Pure: no filesystem access.
|
|
75
|
-
#
|
|
76
|
-
# Absolute forms are a leading `/` (POSIX paths, MSYS `/c/...` paths, and `//server`
|
|
77
|
-
# UNC paths) and a drive letter followed by `/` or `\` (`C:/...`, `c:/...`,
|
|
78
|
-
# `C:\...`), which is the form Git for Windows writes into a worktree's `.git`
|
|
79
|
-
# pointer file (issue #706). A drive letter with no separator (`C:rel`) is
|
|
80
|
-
# drive-relative and is not absolute; an empty path is not absolute.
|
|
81
|
-
#
|
|
82
|
-
# Args: $1 = path. Returns 0 (absolute) or 1 (not absolute).
|
|
83
|
-
local path=${1:-}
|
|
84
|
-
[[ $path == /* || $path == [A-Za-z]:[/\\]* ]]
|
|
85
|
-
}
|
|
86
|
-
|
|
87
76
|
scan_helper_target_present() {
|
|
88
77
|
# Return 0 when <path> exists, else non-zero. This is the helper's single
|
|
89
78
|
# filesystem existence check, kept in its own function as a test seam: bats tests
|
|
@@ -113,7 +102,7 @@ scan_helper_gitdir_target_exists() {
|
|
|
113
102
|
printf '0\n'
|
|
114
103
|
return 0
|
|
115
104
|
fi
|
|
116
|
-
if !
|
|
105
|
+
if ! cleanup_wt_is_absolute_path "$target"; then
|
|
117
106
|
target="$dir/$target"
|
|
118
107
|
fi
|
|
119
108
|
if scan_helper_target_present "$target"; then
|
|
@@ -221,9 +221,10 @@ Procedure, triggered by S9 step 6's merge failure:
|
|
|
221
221
|
3. The existing R1–R5 loop processes this finding exactly as a local blocking finding:
|
|
222
222
|
`atomic-planner` (R1) plans the resolution, `atomic-executor` performs preflight (R2) then
|
|
223
223
|
resolves the conflict markers, stages, and commits (R3), `feature-review` re-audits (R4).
|
|
224
|
-
4.
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
4. Conflict cycles share the child's own `remediation_loop.completed_attempts` count with
|
|
225
|
+
local-finding and CI-failure cycles. The active cycle number is `completed_attempts + 1`, and a
|
|
226
|
+
cycle without an applied candidate consumes no number.
|
|
227
|
+
5. After three completed attempts without resolution, the child's `orchestrator` records
|
|
227
228
|
`step9_status: "blocked_conflict_loop_limit"` (parallel to `blocked_ci_loop_limit`), does not
|
|
228
229
|
write DONE, and halts. It reports this status to `epic-orchestrator`, which mirrors it into
|
|
229
230
|
the epic checkpoint's per-feature `merge_status: "blocked_conflict_loop_limit"` field.
|
|
@@ -242,9 +243,15 @@ cross-call/conversation-state visibility.
|
|
|
242
243
|
dependency's `merge_status` is `merged` or `worktree_removed`.
|
|
243
244
|
- **Layer 2 — retrospective backstop:** the wave-barrier ordering invariant inside
|
|
244
245
|
`validate_epic_orchestrator_state_text`, enforced at `epic-orchestrator` `SubagentStop` time via
|
|
245
|
-
the parameterized `validate-orchestrator-output.ps1` hook. It
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
the parameterized `validate-orchestrator-output.ps1` hook. It checks only a dependent feature
|
|
247
|
+
that is treated as started: one with a string `worktree_created_at`, or with a `merge_status`
|
|
248
|
+
other than `not_started` (a missing or non-string `merge_status` counts as started). It appends
|
|
249
|
+
exactly one error per violated dependency edge. When the dependency's `merge_status` is not
|
|
250
|
+
`merged` or `worktree_removed`, the error is
|
|
251
|
+
`EPIC_WAVE_BARRIER_VIOLATION: <f> is treated as started while dependency <d> is not merged`.
|
|
252
|
+
Otherwise, when the dependency's `merge_confirmed_at` is later than the dependent's
|
|
253
|
+
`worktree_created_at`, the error is
|
|
254
|
+
`EPIC_WAVE_BARRIER_VIOLATION: <f> worktree_created_at precedes dependency <d> merge_confirmed_at`.
|
|
248
255
|
|
|
249
256
|
Both layers are required; neither alone closes the gap. `epic-orchestrator` does not launch wave
|
|
250
257
|
N+1 until every wave-N feature's dependency edges are durably confirmed merged, verified against
|
package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md
CHANGED
|
@@ -46,6 +46,8 @@ Use this skill when:
|
|
|
46
46
|
Use `yyyy-MM-ddTHH-mm` for all audit, remediation, and evidence artifacts.
|
|
47
47
|
Example: `2026-02-06T14-30`.
|
|
48
48
|
|
|
49
|
+
The `Timestamp` value is local time read from the host system clock when the recorded command runs (for example `Get-Date -Format yyyy-MM-ddTHH-mm`); the agent never composes or estimates it.
|
|
50
|
+
|
|
49
51
|
## Canonical Evidence Locations
|
|
50
52
|
|
|
51
53
|
- Baseline evidence: `evidence/baseline/`
|
|
@@ -110,6 +112,8 @@ When evidence artifacts are used for automated checking or plan reconciliation,
|
|
|
110
112
|
- `Command: <exact command>`
|
|
111
113
|
- `EXIT_CODE: <int>`
|
|
112
114
|
|
|
115
|
+
The first occurrence of `Timestamp`, `Command`, `EXIT_CODE`, and `ExpectedExitCode` forms the record in both the Python and the TypeScript parser; later occurrences, including empty later values, are ignored. An artifact that records several gates therefore reports its first gate.
|
|
116
|
+
|
|
113
117
|
One optional field may also be declared:
|
|
114
118
|
- `ExpectedExitCode: <int>` — the exit code the gate is expected to produce.
|
|
115
119
|
|
|
@@ -40,6 +40,16 @@ Each `delegation_receipts.promotion.*` field stores the raw MCP receipt payload
|
|
|
40
40
|
|
|
41
41
|
Note: VS Code command-palette commands may exist for interactive extension use, but this note is non-authoritative for agent sessions.
|
|
42
42
|
|
|
43
|
+
## Pre-Existing Issue Path
|
|
44
|
+
|
|
45
|
+
When the GitHub issue already exists (it was transferred, filed before orchestration, or created by epic decomposition), do not call `potential_to_issue`: that tool creates a new issue and would duplicate the existing one. Instead:
|
|
46
|
+
|
|
47
|
+
1. Verify the issue read-only with `gh issue view`, a `gh api` GET request, or a GitHub MCP issue read, and use its number as `${issue-num}`.
|
|
48
|
+
2. Record a top-level `issue_adoption` object in the orchestrator checkpoint (`issue_num`, `issue_url`, `origin`, `verified_via`, `verified_at`, `evidence`, `waived_tools`, and `potential_record` when a promotion-entry tool is waived). A valid record waives the `potential_to_issue` receipt requirement at completion.
|
|
49
|
+
3. `delegation_receipts.promotion.issue` may be omitted, because no promotion receipt exists for an adopted issue.
|
|
50
|
+
|
|
51
|
+
The field rules, the closed set of waivable tools, and the fail-closed behavior are defined in `.claude/rules/orchestrator-state.md` under `## Invariants (issue_adoption object)`.
|
|
52
|
+
|
|
43
53
|
## Canonical Variables
|
|
44
54
|
|
|
45
55
|
- `${promotion-type}`: `feature` or `bug`
|
|
@@ -72,7 +72,7 @@ If the branch diff modifies any path matching `.github/workflows/**`, `scripts/b
|
|
|
72
72
|
- The rule provides a second, independent line of defense for CI-gate-modifying features, separate from and prior to the orchestrator's S9 CI green gate.
|
|
73
73
|
- "Green workflow run against the branch head" means a workflow run whose head SHA matches the current branch head and whose conclusion is success for the affected workflow.
|
|
74
74
|
- A green `workflow_dispatch` run against the branch head also satisfies the rule, not only a PR-context run. This mitigates the chicken-and-egg case where a feature must land its CI gate before the gate can run in PR context (see spec.md Risks & Mitigations).
|
|
75
|
-
- When the rule fires and no qualifying green-run evidence is present, record a Blocking finding and
|
|
75
|
+
- When the rule fires and no qualifying green-run evidence is present, record a Blocking finding classified `awaiting_ci` (the remediation inputs carry `Remediability: awaiting_ci` and a `Remediability-Evidence:` line naming the awaited workflow) and route it to the wait path (`AWAITING_CI` when no other class is present) instead of the remediation handoff.
|
|
76
76
|
|
|
77
77
|
## Ordered Procedure
|
|
78
78
|
|
|
@@ -147,6 +147,8 @@ If the branch diff modifies any path matching `.github/workflows/**`, `scripts/b
|
|
|
147
147
|
- required acceptance criteria are FAIL or PARTIAL
|
|
148
148
|
- coverage regression below policy threshold (< 80% repo-wide per language, < 80% or regression for modified files, or < 90% for new files)
|
|
149
149
|
- coverage artifact absent for any language that has changed files
|
|
150
|
+
- Classify each blocking finding before triggering remediation: each receives one remediability class (`autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, or `human_decision_required`; `autonomous` when no class is stated). A published MCP runtime that lags the repository contract is classified `external_dependency`.
|
|
151
|
+
- Trigger the remediation handoff only when at least one blocking finding is `autonomous`. When every blocking finding is non-remediable, write the remediation inputs with the `HALT_NON_REMEDIABLE` or `AWAITING_CI` verdict and create no remediation plan target.
|
|
150
152
|
- Create `remediation-inputs.<timestamp>.md` first.
|
|
151
153
|
- Create the target remediation plan file from the canonical plan template.
|
|
152
154
|
- Hand off plan creation through `remediation-handoff-atomic-planner`.
|
|
@@ -81,7 +81,7 @@ blocked result before launch.
|
|
|
81
81
|
|
|
82
82
|
Because model selection is required once delegation occurs (see `## Model Selection`), a resuming orchestrator must repair a missing model choice deterministically before delegating at a delegating `next_step`. When the resumed `next_step` is a delegating step:
|
|
83
83
|
|
|
84
|
-
a. **Preflight the checkpoint.** Run the orchestrator-state validator with `--require-model-routing` (via `mcp__drm-copilot__validate_orchestration_artifacts` or the local CLI) against `artifacts/orchestration/orchestrator-state.json` before the first delegation. Record the result in a `model_routing_preflight` block `{ status ("pass"|"fail"), checked_at (ISO-8601 UTC), validator_command, output_summary }`.
|
|
84
|
+
a. **Preflight the checkpoint.** Run the orchestrator-state validator with `--require-model-routing` (via `mcp__drm-copilot__validate_orchestration_artifacts` or the repository-local validator CLI documented in `.claude/rules/orchestrator-state.md`) against `artifacts/orchestration/orchestrator-state.json` before the first delegation. Record the result in a `model_routing_preflight` block `{ status ("pass"|"fail"), checked_at (ISO-8601 UTC), validator_command, output_summary }`.
|
|
85
85
|
b. **Recompute the floor.** For the upcoming phase, recompute the complexity floor with `Get-ComplexityFloor -SignalsPresent <names>` (`.claude/lib/model-routing/ModelRouting.psm1`); do not reimplement the formula.
|
|
86
86
|
c. **Record the assessment.** Write a `complexity_assessments[]` entry `{ phase, band, floor, signals_present[], rationale, assessed_at }` with `floor` equal to the recomputed value and `band >= floor`.
|
|
87
87
|
d. **Resolve and record the receipt.** Resolve the model with `Resolve-DelegationModel -Agent <agent> -Band <complexity_band> -FablePolicy <fable_policy>` (`.claude/lib/model-routing/ModelRouting.psm1`) and write a `model_routing_receipts[]` entry `{ agent, phase, complexity_band, fable_policy, table_model, clamped_from | null, model }`.
|
|
@@ -137,6 +137,7 @@ A delegation prompt carrying the literal marker `Preparation mode: true` (issued
|
|
|
137
137
|
- **Route.** Select `route_id: preparation`. The route's routing-matrix entry requires receipts for `task-researcher`, `prd-feature`, `atomic-planner`, and `atomic-executor`; the skills `orchestrate`, `feature-promotion-lifecycle`, and `atomic-plan-contract`; and the promotion plus validator MCP tools.
|
|
138
138
|
- **Scope.** Run promotion (MCP surface), research, feature documents (`spec.md`, `user-story.md`), atomic planning, and the atomic-executor preflight (precondition validation only, R2 semantics: iterate plan revisions until `PREFLIGHT: ALL CLEAR`). Atomic execution, PR authoring, CI monitoring, and feature review are out of scope and are executed later by `epic-orchestrator`.
|
|
139
139
|
- **Terminal checkpoint.** Stop with `completed_steps` containing `S3_promotion` and `S4_atomic_planning`, `next_step: "S5_atomic_execution"`, out-of-scope step statuses set to `not-applicable`, and `blocked_reason: "none"`. Do NOT assert completion (`next_step: "complete"`, `S12_complete`, or a `completed` step8/9/10 status): the run has no PR or CI evidence, and the route's `requires_ci_gate: false` exempts it from `ci_gate` at the completion validator instead.
|
|
140
|
+
- **Premise-falsified halt.** Use `blocked_reason: "premise_falsified"` when every delegation and validator succeeded but evidence gathered during execution falsified the plan's premise. Still record the evidence path in free-form checkpoint keys; `blocked_reason` carries only the classification. The preparation terminal checkpoint above still sets `blocked_reason: "none"`. The full `blocked_reason` vocabulary and its partition are in `.claude/rules/orchestrator-state.md`.
|
|
140
141
|
- **Commit.** Commit the prepared feature folder and plan to the current branch (the worktree branch created off the epic integration branch) before stopping, and report the `plan-path` and preflight status in the final output.
|
|
141
142
|
|
|
142
143
|
## Epic Mode Bounded Return
|
|
@@ -184,7 +185,7 @@ PR creation and PR body edits are delegated work, not orchestrator work. The orc
|
|
|
184
185
|
The mandatory sequence is:
|
|
185
186
|
|
|
186
187
|
1. The orchestrator first refreshes the PR-context artifact via `mcp__drm-copilot__collect_pr_context` (or the equivalent context-collection mechanism), which writes `artifacts/pr_context.summary.txt`.
|
|
187
|
-
2. The orchestrator runs the orchestrator-state validator (`mcp__drm-copilot__validate_orchestration_artifacts` or the equivalent local CLI
|
|
188
|
+
2. The orchestrator runs the orchestrator-state validator (`mcp__drm-copilot__validate_orchestration_artifacts` or the equivalent repository-local validator CLI documented in `.claude/rules/orchestrator-state.md`) against `artifacts/orchestration/orchestrator-state.json --require-pr-creation-ready` and records the pass/fail result under a new `pr_author_preflight` field in the checkpoint, alongside `pr_author_receipt`: `{status ("pass"|"fail"), checked_at (ISO-8601 UTC), checkpoint_path, validator_command, output_summary}`. The orchestrator must not delegate to `Agent(pr-author)` when this preflight fails. The validator's full-lifecycle completion flag (`ci_gate`/`pr_gate`/routing-contract receipts) remains reserved for the post-PR/CI completion context (Step S9 / PR Creation Gate condition 6), not this pre-PR-creation preflight, because those values cannot exist before the first `gh pr create` of a branch.
|
|
188
189
|
3. The orchestrator then delegates PR creation and any PR body edits to `Agent(pr-author)`. The `pr-author` agent runs the `pr-author` skill to author the body, writes the body file `artifacts/pr_body_<N>.md` and the sibling receipt `artifacts/pr_body_<N>.receipt.json` with the shape `{skill, pr_body_path, number, sha256 (lowercase hex of the body bytes), context_summary_path, created_at (ISO-8601 UTC, strictly newer than `artifacts/pr_context.summary.txt` last-write)}`, issues `gh pr create --body-file artifacts/pr_body_<N>.md` (or `gh pr edit --body-file ...`), and reports the resulting PR URL or PR number.
|
|
189
190
|
4. The orchestrator records `pr_author_receipt` in the checkpoint, citing the body-file path and the receipt path that were verified.
|
|
190
191
|
|
|
@@ -237,20 +238,32 @@ After each `feature-review` delegation returns:
|
|
|
237
238
|
|
|
238
239
|
1. Locate `remediation-inputs.<timestamp>.md` in the active feature folder (match the highest ISO-8601 timestamp).
|
|
239
240
|
2. If no such file exists, treat as zero blocking findings and advance to the PR creation gate.
|
|
240
|
-
3. If the file exists, count lines matching `BLOCKING` or `Severity: Blocking` (case-sensitive). If count >= 1, enter the remediation loop. If count = 0, advance to the PR creation gate.
|
|
241
|
+
3. If the file exists, count lines matching `BLOCKING` or `Severity: Blocking` (case-sensitive). If count >= 1, enter the remediation loop. If count = 0, advance to the PR creation gate. This count rule applies unchanged when the file carries no `Review-Verdict:` line: a count of at least 1 is `REMEDIATION_REQUIRED` and every finding is treated as `autonomous`.
|
|
242
|
+
4. If the file carries a `Review-Verdict:` line, recompute the verdict from the `Remediability:` lines of the blocking findings (a blocking finding block without a `Remediability:` line counts as `autonomous`): no blocking finding is `PASS`; any `autonomous` finding is `REMEDIATION_REQUIRED`; otherwise any `external_dependency`, `policy_hold`, or `human_decision_required` finding is `HALT_NON_REMEDIABLE`; otherwise `AWAITING_CI`. The `remediation-inputs` file is authoritative; the optional `review-status:` token in the review result is a cross-check.
|
|
243
|
+
5. A mismatch between the declared and recomputed verdicts is a reviewer contract error: record it under `artifact_errors`, re-request the review once, and halt with `blocked_reason: "delegate_contract_incomplete"` if it persists.
|
|
244
|
+
6. Append a `remediation_loop.review_outcomes[]` entry (`verdict` plus one `findings[]` object with `remediability` per blocking finding) for every review, including `PASS`.
|
|
245
|
+
7. Act on the verdict: `PASS` advances to the PR creation gate; `REMEDIATION_REQUIRED` enters the remediation loop with the plan scoped to the `autonomous` findings, carrying the non-remediable findings forward unchanged; `HALT_NON_REMEDIABLE` follows the halt branch; `AWAITING_CI` follows the wait branch.
|
|
246
|
+
|
|
247
|
+
Halt branch (`HALT_NON_REMEDIABLE`): set `blocked_reason` to the highest-precedence halt class present, append one `human_interaction.requirements[]` entry with `response: "halt"` for each halt class present (`external_dependency`, `policy_hold`, `human_decision_required`) describing the human action needed, leave `remediation_loop.completed_attempts` unchanged, write no cycle, create no remediation plan, and stop. The precedence is:
|
|
248
|
+
|
|
249
|
+
Halt precedence: human_decision_required > policy_hold > external_dependency
|
|
250
|
+
|
|
251
|
+
Wait branch (`AWAITING_CI`): set `blocked_reason: "awaiting_ci"` and set `next_step` to the step that re-checks (`S7_feature_review` for a review-time wait, `S9_ci_green` for an S9 poll timeout). Record no `human_interaction.requirements[]` entry for a wait, write no cycle, and create no remediation plan. Poll within the existing bounded S9 interval and timeout; when the bound is exhausted, persist and stop. On resume, set `blocked_reason: "none"` and re-run the named step. For S9, the awaited run is identified by `ci_gate.head_sha` and `ci_gate.pr_pipeline_run_id` with `conclusion: "pending"`; for a review-time wait, the awaited workflow is named in the finding's `Remediability-Evidence:` line.
|
|
241
252
|
|
|
242
253
|
## Remediation Loop (R1–R5)
|
|
243
254
|
|
|
244
|
-
A bounded loop consisting of five steps. The
|
|
255
|
+
A bounded loop consisting of five steps. The attempt count is `remediation_loop.completed_attempts`, and `remediation_pass` equals `remediation_loop.completed_attempts`. The active cycle number is `completed_attempts + 1`, and `remediation.cycle_N` in `next_step` uses that number; `remediation_loop.current_cycle` is a record position in `cycles[]`, not a count. A halt or wait verdict never opens a cycle, and every cycle opened from a review records `opened_by_review`, the zero-based index of that `review_outcomes[]` entry.
|
|
256
|
+
|
|
257
|
+
Cycle accounting: a remediation attempt is complete when R3 execution finished (`execution_status: "complete"`) and the pre-R4 commit recorded a non-empty change set; only then set `candidate_applied: true` on that cycle and increment `completed_attempts`. A cycle that ends without a candidate (execution not started, execution failed, or an empty staged change set) records `candidate_applied: false` and consumes no number. It is followed by at most one re-plan under the same number when the executor output identifies an autonomous plan defect; otherwise reclassify the blocker (typically `external_dependency`) and halt. Two consecutive no-candidate cycles halt with `step6_status: "blocked_remediation_loop_limit"`. Local, CI-failure, merge-conflict, and parallel-drift cycles share the one count.
|
|
245
258
|
|
|
246
259
|
- **R1 — Remediation planning:** Delegate to `atomic-planner` with `remediation-inputs.<timestamp>.md` path as primary context. Receive `remediation-plan.<timestamp>.md` in the active feature folder.
|
|
247
260
|
- **R2 — Preflight clearance:** Delegate to `atomic-executor` for precondition validation only (no implementation). If the executor does not return `PREFLIGHT: ALL CLEAR`, return to R1 and re-delegate to `atomic-planner` with the required-changes output from the executor. Only after `PREFLIGHT: ALL CLEAR` may the orchestrator advance to R3.
|
|
248
261
|
- **R3 — Remediation execution:** Delegate to `atomic-executor` with full execution authorization. Each task's toolchain loop (format → lint → type-check → test) is mandatory; no skipping.
|
|
249
262
|
- **Pre-R4 commit:** Stage all changes (`git add -A`), delegate to `Agent(commit-message)` to generate a commit message from the staged diff (the agent returns message text only and does not commit), then commit with the generated message. The `git commit` action remains on the orchestrator. Advance to R4 only after a successful commit.
|
|
250
263
|
- **R4 — Re-audit:** Delegate to `feature-review` with the same inputs as the original review (resolved base branch, feature folder, refreshed PR context artifacts, acceptance-criteria source). No scope narrowing. The canonical issue number line must be included.
|
|
251
|
-
- **R5 — Loop-exit decision:**
|
|
264
|
+
- **R5 — Loop-exit decision:** Evaluate the re-audit per Post-Review Outcome Evaluation. If it produces zero blocking findings, exit the loop and advance to the PR creation gate. If its verdict is `HALT_NON_REMEDIABLE` or `AWAITING_CI`, follow the halt or wait branch instead of opening the next cycle. Otherwise, record the cycle's `candidate_applied` value and the resulting `completed_attempts` in the checkpoint and return to R1.
|
|
252
265
|
|
|
253
|
-
**Termination guard:**
|
|
266
|
+
**Termination guard:** Halt after three completed attempts: when `remediation_loop.completed_attempts` equals 3 and the latest verdict is not `PASS`, the orchestrator records `step6_status: "blocked_remediation_loop_limit"` in the checkpoint and halts. No further automation is attempted.
|
|
254
267
|
|
|
255
268
|
## Issue Number Consistency
|
|
256
269
|
|
|
@@ -275,10 +288,12 @@ S9 procedure:
|
|
|
275
288
|
1. Resolve the live PR head SHA for the feature branch (`gh pr view --json headRefOid` or equivalent).
|
|
276
289
|
2. Invoke `gh pr checks --required --json bucket,name,state,link,workflow` (or an equivalent JSON-emitting command) against that head SHA. `gh` is the only sanctioned channel for querying GitHub Actions state.
|
|
277
290
|
3. Parse the JSON by running `pwsh -NoProfile -File .claude/lib/ci-gate/Invoke-CiGateParser.ps1 -ChecksJson <checks-json> -HeadSha <head-sha>`, which emits the `ci_gate` object defined below and derives `ci_gate.conclusion` as `success` when all required checks pass, `failure` when any required check failed, and `pending` when any required check is still in progress.
|
|
278
|
-
4. Poll with a bounded interval and a documented total timeout while `conclusion == "pending"`. When the timeout is exhausted,
|
|
291
|
+
4. Poll with a bounded interval and a documented total timeout while `conclusion == "pending"`. When the timeout is exhausted, record the timeout as a finding with `Remediability: awaiting_ci` in `remediation-inputs.<timestamp>.md` and follow the wait branch in Post-Review Outcome Evaluation (`blocked_reason: "awaiting_ci"`, `next_step: "S9_ci_green"`); `step9_status` stays `pending`. A poll timeout does not enter the remediation loop.
|
|
279
292
|
5. Write the `ci_gate` object and `last_verified_ci_sha` to the checkpoint, and set `step9_status` to `passed` only when `ci_gate.conclusion == "success"` AND `ci_gate.head_sha` equals the current PR head SHA.
|
|
280
293
|
6. If the checkpoint's `epic_mode` is `true`, execute `gh pr merge --merge <PR>` merging the feature branch into `epic_context.integration_branch` (already the PR's base branch per the epic-mode `--base` override applied at S8). On success, record `epic_merge: { merge_commit_sha, target_branch, merged_at }` in the checkpoint. On failure due to merge conflict (non-mergeable PR), do not retry blindly: convert the conflict into a synthetic Blocking finding per "Merge-Conflict Remediation" below and re-enter the standard R1–R5 remediation loop; do not proceed to DONE.
|
|
281
294
|
|
|
295
|
+
CI-dependent acceptance criteria: when the item has acceptance criteria whose verification requires the result of CI on the PR head, the item's own orchestrator run owns their check-off, and a parent or coordinating session never commits them from its own root. After step 5 records `ci_gate.conclusion == "success"`, check off each CI-dependent criterion in the item's own worktree, commit, push to the PR branch, and re-run S9 from step 1 against the new head SHA, so that `ci_gate.head_sha` equals the final PR head before DONE. In epic mode this completes before step 6 merges. A failed re-run enters `## Remediation Loop — CI-Failure Handling` below; the shared `remediation_loop.completed_attempts` count and its halt after three completed attempts are unchanged.
|
|
296
|
+
|
|
282
297
|
DONE is not written while `step9_status` is anything other than `passed`.
|
|
283
298
|
|
|
284
299
|
## Checkpoint Schema — CI Gate Fields
|
|
@@ -321,13 +336,13 @@ A checkpoint that predates this schema and has no `ci_gate` object (or no `step9
|
|
|
321
336
|
|
|
322
337
|
## Remediation Loop — CI-Failure Handling
|
|
323
338
|
|
|
324
|
-
When S9 records `step9_status: "failed_remediation_required"` (a failed required check
|
|
339
|
+
When S9 records `step9_status: "failed_remediation_required"` (a failed required check; a poll timeout follows the wait branch per S9 step 4):
|
|
325
340
|
|
|
326
|
-
1. The failed-check log from `gh run view <run-id> --log-failed`
|
|
341
|
+
1. The failed-check log from `gh run view <run-id> --log-failed` is written as `remediation-inputs.<timestamp>.md` in the active feature folder.
|
|
327
342
|
2. The failure is converted to a synthetic finding with severity `Blocking` that identifies the failing check by name and the failing job by URL.
|
|
328
343
|
3. The existing R1-R5 remediation loop processes that finding exactly as it processes a local blocking finding. No new loop is introduced.
|
|
329
|
-
4.
|
|
330
|
-
5.
|
|
344
|
+
4. CI-failure cycles share `remediation_loop.completed_attempts` with local-finding cycles; a CI-failure finding stays `autonomous`, and the active cycle number is `completed_attempts + 1`.
|
|
345
|
+
5. The shared guard applies: after three completed attempts without a passing result, the orchestrator records `step9_status: "blocked_ci_loop_limit"`, does not write DONE, and halts. No further automation is attempted.
|
|
331
346
|
|
|
332
347
|
## PR Creation Gate
|
|
333
348
|
|
|
@@ -335,6 +350,7 @@ The orchestrator must not create a PR, push a branch for PR purposes, or report
|
|
|
335
350
|
|
|
336
351
|
1. `blocking_findings_resolved: true` — the most recent `feature-review` produced zero blocking findings.
|
|
337
352
|
2. The AC verification artifact (`p14-acceptance-criteria-checkoff.md` or equivalent) confirms all acceptance criteria pass.
|
|
353
|
+
A CI-dependent acceptance criterion, one whose verification requires the result of CI on the PR head, cannot pass before CI exists: condition 2 is satisfied when every other criterion passes and each CI-dependent criterion is listed as pending-CI in the AC verification artifact. Pending-CI criteria are checked off under S9 before DONE. This clarifies condition 2 and adds no condition.
|
|
338
354
|
3. The mandatory toolchain passed in its most recent run on the branch (no linting/type-check/test failures).
|
|
339
355
|
4. The checkpoint `next_step` is `S8_create_pr` (precondition to entering S9).
|
|
340
356
|
5. PR body produced via the pr-author handoff: `artifacts/pr_body_<N>.md` exists with a matching `artifacts/pr_body_<N>.receipt.json`, created with `--body-file`.
|
|
@@ -420,6 +436,8 @@ Example:
|
|
|
420
436
|
]
|
|
421
437
|
```
|
|
422
438
|
|
|
423
|
-
The validator counts an MCP receipt as successful only when `tool` is a non-empty string, `ok` is exactly `true`, and `evidence` is a non-empty string. Every `required_mcp_tools` entry must have such a receipt.
|
|
439
|
+
The validator counts an MCP receipt as successful only when `tool` is a non-empty string, `ok` is exactly `true`, and `evidence` is a non-empty string. Every `required_mcp_tools` entry must have such a receipt unless a valid `issue_adoption` record waives it.
|
|
440
|
+
|
|
441
|
+
When the GitHub issue already exists before orchestration starts (transferred, filed by hand, or created by epic decomposition) and has been verified read-only (`gh issue view`, a `gh api` GET, or a GitHub MCP issue read), do not call `potential_to_issue`. Record a top-level `issue_adoption` object in the checkpoint instead of a `potential_to_issue` receipt. It may waive only `potential_to_issue` and, with a valid `potential_record` under `docs/features/potential/`, the checkpoint's promotion-entry tool (`new_potential_entry`, or `new_potential_bug_entry` for a bug-type checkpoint); `potential_to_issue` must always be listed. Every other required tool still needs a successful receipt. Any adoption error waives nothing. The field rules, the closed waivable set, and the error placement are defined in `.claude/rules/orchestrator-state.md` under `## Invariants (issue_adoption object)`.
|
|
424
442
|
|
|
425
443
|
These three receipt arrays, populated with the retained required names of the selected route, are what allow the routing-contract validation under `require_complete: true` to pass.
|
|
@@ -313,6 +313,12 @@ unchanged: `.claude/skills/orchestrate/SKILL.md` is **not modified by this featu
|
|
|
313
313
|
`parallel_mode` clause in its step 9, no `parallel_merge` object in the child checkpoint, and no
|
|
314
314
|
additional condition on the child's PR Creation Gate.
|
|
315
315
|
|
|
316
|
+
Issue #744 later amended two parts of that child contract for CI-dependent acceptance criteria,
|
|
317
|
+
whose verification requires the result of CI on the item's pull-request head: PR Creation Gate
|
|
318
|
+
condition 2 and step S9 of `.claude/skills/orchestrate/SKILL.md`. The child checks those criteria
|
|
319
|
+
off in its own worktree, pushes the check-off commit to its pull-request branch, and re-runs S9
|
|
320
|
+
before DONE. The parent never commits acceptance-criteria check-offs from the coordinator root.
|
|
321
|
+
|
|
316
322
|
Procedure, per item:
|
|
317
323
|
|
|
318
324
|
1. The item's child orchestration runs unmodified, with `epic_mode` `false` or absent, and finishes
|
|
@@ -323,6 +329,11 @@ Procedure, per item:
|
|
|
323
329
|
`gh pr view --json state,mergedAt,headRefOid`, and with `gh pr checks` when the check conclusion
|
|
324
330
|
must be re-read — never from an in-memory completion notification — then record
|
|
325
331
|
`merge_status: ci_green`.
|
|
332
|
+
Before recording `merge_status: ci_green`, confirm that the `headRefOid` value equals the
|
|
333
|
+
`ci_gate.head_sha` the child reported at DONE and that the child reported no pending
|
|
334
|
+
CI-dependent acceptance criteria. When either check fails, do not record
|
|
335
|
+
`merge_status: ci_green` and do not run `gh pr merge`; the item waits until its child pushes
|
|
336
|
+
the check-off and re-runs S9, and the parent does not commit the check-off itself.
|
|
326
337
|
3. Execute `gh pr merge --merge <PR>` for that item's pull request, whose base is `main`.
|
|
327
338
|
4. On success, record `merge_commit_sha`, `merged_at`, and `merge_status: merged`, then regenerate
|
|
328
339
|
`docs/features/parallel/<slug>/parallel-status.md`.
|
|
@@ -417,8 +428,10 @@ the escalation path.
|
|
|
417
428
|
2. The parent re-delegates that item's child orchestration. The child processes the finding through
|
|
418
429
|
its unmodified R1 through R5 remediation loop exactly as it processes any local Blocking
|
|
419
430
|
finding. No new remediation loop is introduced by this procedure.
|
|
420
|
-
3.
|
|
421
|
-
|
|
431
|
+
3. Drift cycles share the child's `remediation_loop.completed_attempts` count with its
|
|
432
|
+
local-finding and CI-failure cycles, and the child halts after three completed attempts. The
|
|
433
|
+
active cycle number is `completed_attempts + 1`, and a cycle without an applied candidate
|
|
434
|
+
consumes no number.
|
|
422
435
|
4. Each remediated pass ends again at child DONE with the pull request open and CI green, after
|
|
423
436
|
which the parent retries the merge per `## Per-Item Merge to Main (Merge-on-Green)`. During
|
|
424
437
|
remediation the item's `merge_status` legitimately remains `pr_open` or `ci_green`: the
|
|
@@ -567,6 +580,8 @@ hold:
|
|
|
567
580
|
`artifact_type: "parallel-orchestrator-state"`.
|
|
568
581
|
4. Each item's acceptance criteria have been checked off in that item's own acceptance-criteria
|
|
569
582
|
source files by that item's own run, per the `acceptance-criteria-tracking` skill.
|
|
583
|
+
A CI-dependent criterion is checked off and pushed by that item's own run before its DONE, so
|
|
584
|
+
the head the parent merges already contains the check-off; the parent never commits it.
|
|
570
585
|
|
|
571
586
|
In `open` mode there is no automatic completion. The run is a standing queue and terminates only via
|
|
572
587
|
`/parallel-close`, which is owned by F6 and is neither specified nor shipped by this feature. Do not
|
|
@@ -1113,7 +1128,7 @@ R5 loop that drives the remediation preceding either write is **reused
|
|
|
1113
1128
|
unmodified**: `atomic-planner` plans the resolution, `atomic-executor` performs preflight then
|
|
1114
1129
|
resolves, `feature-review` re-audits, and the loop exits on zero blocking findings. No new
|
|
1115
1130
|
remediation loop is authored, no line of the existing loop is modified, and the shared
|
|
1116
|
-
`
|
|
1131
|
+
`remediation_loop.completed_attempts` count applies, with the halt after three completed attempts. `.claude/skills/orchestrate/SKILL.md` is not modified by this
|
|
1117
1132
|
feature.
|
|
1118
1133
|
|
|
1119
1134
|
#### Layer-1 Narrowing — a Documented Limitation
|
package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md
CHANGED
|
@@ -54,6 +54,8 @@ Trigger remediation when any of these are true:
|
|
|
54
54
|
- Acceptance criteria are not met.
|
|
55
55
|
- A required CI check fails after the PR is open (workflow-file changes specifically must enter the loop and trigger the `modified-workflow-needs-green-run` rule defined in `.claude/skills/feature-review-workflow/SKILL.md`).
|
|
56
56
|
|
|
57
|
+
Remediation triggers only on blocking findings classified `autonomous`. Each blocking finding carries one remediability class (`autonomous`, `external_dependency`, `policy_hold`, `awaiting_ci`, or `human_decision_required`; `autonomous` when no class is stated). When no blocking finding is `autonomous`, the review verdict is `HALT_NON_REMEDIABLE` or `AWAITING_CI`: the orchestrator halts or waits, and no cycle, remediation plan, or `atomic-planner` handoff is created.
|
|
58
|
+
|
|
57
59
|
## Required Remediation Inputs
|
|
58
60
|
|
|
59
61
|
The orchestrator authors `remediation/<entry-ts>/remediation-inputs.md` with:
|
|
@@ -118,11 +120,13 @@ Iteration ceiling. When a cycle's `iterations` would exceed 2, the orchestrator
|
|
|
118
120
|
|
|
119
121
|
When preflight is clear, `atomic-executor` executes the plan task-by-task. The executor invokes workers (`python-typed-engineer`, `typescript-engineer`, `csharp-typed-engineer`, `powershell-typed-engineer`) internally as needed. The orchestrator does not call workers.
|
|
120
122
|
|
|
123
|
+
Before the reaudit, the orchestrator records `candidate_applied` on the current cycle: `true` only when execution finished with `execution_status: "complete"` and the pre-reaudit commit recorded a non-empty change set, and `false` otherwise (execution not started, execution failed, or an empty staged change set). `remediation_loop.completed_attempts` equals the number of cycles whose `candidate_applied` is `true`, and a cycle with `candidate_applied: false` consumes no attempt number.
|
|
124
|
+
|
|
121
125
|
When execution is complete, the orchestrator delegates to `feature-review`. `feature-review` produces the three reaudit artifacts under `docs/features/active/<slug>/audit/<exit-ts>/` using the exit timestamp.
|
|
122
126
|
|
|
123
127
|
## Exit Gate
|
|
124
128
|
|
|
125
|
-
The orchestrator reads the latest cycle's three reaudit artifacts and computes `blocking_count` as the total number of FAIL findings plus material PARTIAL findings flagged as blocking. Only when `blocking_count == 0` does the orchestrator set `exit_condition_met = true` on the current cycle and mark the remediation loop complete. Otherwise, the orchestrator opens cycle N+1 with a new `remediation/<new-ts>/remediation-inputs.md` and runs the full chain again.
|
|
129
|
+
The orchestrator reads the latest cycle's three reaudit artifacts and computes `blocking_count` as the total number of FAIL findings plus material PARTIAL findings flagged as blocking. Only when `blocking_count == 0` does the orchestrator set `exit_condition_met = true` on the current cycle and mark the remediation loop complete. Otherwise, when at least one remaining blocking finding is `autonomous`, the orchestrator opens cycle N+1 with a new `remediation/<new-ts>/remediation-inputs.md` and runs the full chain again. When the remaining blocking findings are all non-remediable, the orchestrator does not open the next cycle: it halts on `HALT_NON_REMEDIABLE` or waits on `AWAITING_CI`.
|
|
126
130
|
|
|
127
131
|
## Context Package (When Required)
|
|
128
132
|
|