forgedock 1.11.0 → 1.12.1
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/bin/engine/phases-args.test.mjs +113 -0
- package/bin/engine/phases.mjs +433 -109
- package/bin/engine/runlog.mjs +21 -1
- package/bin/engine-cli.mjs +14 -2
- package/bin/engine.mjs +150 -19
- package/bin/forgedock.mjs +2 -2
- package/bin/hooks/interactive-engine.mjs +3 -0
- package/bin/labels.json +5 -0
- package/bin/orchestrate-preflight.mjs +1 -1
- package/bin/runner.mjs +111 -12
- package/bin/watch-utils.mjs +1 -0
- package/commands/cleanup.md +1 -1
- package/commands/orchestrate/phase-4-execution.md +58 -24
- package/commands/orchestrate/phase-6-report.md +29 -0
- package/commands/quality-gate.md +96 -12
- package/commands/recover-orphans.md +5 -5
- package/commands/review-pr-agents/protocols.md +1 -1
- package/commands/review-pr-agents.md +11 -11
- package/commands/review-pr.md +227 -33
- package/commands/work-on/build/implement.md +1 -1
- package/commands/work-on/build/validate.md +58 -7
- package/commands/work-on/build.md +11 -4
- package/commands/work-on/decompose.md +2 -2
- package/commands/work-on/investigate.md +14 -4
- package/commands/work-on/remediate.md +109 -19
- package/commands/work-on/review.md +79 -25
- package/commands/work-on.md +23 -15
- package/docs/WORK-ON-RUNTIME.md +2 -2
- package/docs/spec/label-state-machine.md +6 -1
- package/docs/spec/review-protocol.md +1 -1
- package/package.json +1 -1
- package/packages/protocol/src/phases.js +27 -1
- package/scripts/check-branch-ancestry.sh +82 -0
- package/scripts/check-branch-ancestry.test.sh +78 -0
- package/scripts/check-spec-markers.sh +2 -0
- package/scripts/forge-root.test.sh +36 -15
- package/scripts/review-pr-domain-map.test.sh +87 -0
- package/scripts/spec-no-worktree-script.test.sh +46 -0
- package/scripts/transition-label.sh +3 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# Fixtures for scripts/check-branch-ancestry.sh. Run: bash scripts/check-branch-ancestry.test.sh (bash 3.2 compatible)
|
|
6
|
+
|
|
7
|
+
set -u
|
|
8
|
+
DIR=$(cd "$(dirname "$0")" && pwd)
|
|
9
|
+
S="$DIR/check-branch-ancestry.sh"
|
|
10
|
+
T=$(mktemp -d "${TMPDIR:-/tmp}/check-branch-ancestry-test.XXXXXX")
|
|
11
|
+
trap 'rm -rf "$T"' EXIT
|
|
12
|
+
pass=0; fail=0
|
|
13
|
+
OUT=""
|
|
14
|
+
|
|
15
|
+
g() { git -C "$T/r" -c user.name=t -c user.email=t@example.com -c commit.gpgsign=false "$@" >/dev/null 2>&1; }
|
|
16
|
+
commit() { echo "$1" > "$T/r/$1.txt"; g add -A; g commit -m "$1"; }
|
|
17
|
+
# run <branch> <base>: sets OUT and RC
|
|
18
|
+
run() { OUT=$(cd "$T/r" && bash "$S" "$@" 2>&1); RC=$?; }
|
|
19
|
+
expect() { # expect <want_rc> <desc> [want_substring]
|
|
20
|
+
if [ "$RC" = "$1" ] && { [ -z "${3:-}" ] || case "$OUT" in *"$3"*) true ;; *) false ;; esac; }; then
|
|
21
|
+
pass=$((pass+1))
|
|
22
|
+
else
|
|
23
|
+
fail=$((fail+1)); echo "FAIL: $2 (rc=$RC, want $1)"; printf '%s\n' "$OUT" | sed 's/^/ /'
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
mkdir "$T/r"; git -C "$T/r" init -q -b staging >/dev/null 2>&1 || { git -C "$T/r" init -q; g checkout -b staging; }
|
|
28
|
+
commit base
|
|
29
|
+
# milestone diverges from staging after staging2
|
|
30
|
+
commit staging2
|
|
31
|
+
g checkout -b milestone/m1; commit ms1
|
|
32
|
+
g checkout staging
|
|
33
|
+
# feature branch off the original base commit
|
|
34
|
+
g checkout -b feat HEAD~1; commit feat1
|
|
35
|
+
|
|
36
|
+
# 1. no merges
|
|
37
|
+
run feat staging; expect 0 "no merges passes"
|
|
38
|
+
|
|
39
|
+
# 2. base-sync merge passes (staging advances, then is merged into feat)
|
|
40
|
+
g merge --no-ff -m "Merge staging into feat" staging
|
|
41
|
+
run feat staging; expect 0 "base-sync merge passes"
|
|
42
|
+
run HEAD staging; expect 0 "HEAD form passes"
|
|
43
|
+
|
|
44
|
+
# 3. staging advances further and is synced again: still passes
|
|
45
|
+
g checkout -q staging; commit staging3; g checkout -q feat
|
|
46
|
+
g merge --no-ff -m "Merge staging again" staging
|
|
47
|
+
run feat staging; expect 0 "repeated base-sync merges pass"
|
|
48
|
+
|
|
49
|
+
# 4. foreign merge fails, foreign parent printed
|
|
50
|
+
MS=$(git -C "$T/r" rev-parse milestone/m1)
|
|
51
|
+
g merge --no-ff -m "Merge milestone into feat" milestone/m1
|
|
52
|
+
run feat staging; expect 1 "foreign merge fails" "$MS"
|
|
53
|
+
case "$OUT" in *"Merge milestone into feat"*) pass=$((pass+1)) ;; *) fail=$((fail+1)); echo "FAIL: subject not printed"; echo "$OUT" ;; esac
|
|
54
|
+
|
|
55
|
+
# 5. octopus with one foreign parent fails
|
|
56
|
+
g checkout -q -b feat2 staging; commit f2
|
|
57
|
+
g checkout -q staging; g checkout -q -b side; commit side1
|
|
58
|
+
g checkout -q feat2
|
|
59
|
+
g merge --no-ff -m "octopus" side milestone/m1
|
|
60
|
+
run feat2 staging; expect 1 "octopus with foreign parent fails" "$MS"
|
|
61
|
+
|
|
62
|
+
# 5b. branch cut from a milestone line, then syncing the base: foreign first-parent history fails
|
|
63
|
+
MS1=$(git -C "$T/r" rev-parse milestone/m1)
|
|
64
|
+
g checkout -q -b feat3 milestone/m1; commit f3
|
|
65
|
+
g merge --no-ff -m "Merge staging into feat3" staging
|
|
66
|
+
run feat3 staging; expect 1 "milestone-cut branch with base sync fails" "$MS1"
|
|
67
|
+
|
|
68
|
+
# 5c. milestone-cut branch with no merge at all also fails
|
|
69
|
+
g checkout -q -b feat4 milestone/m1; commit f4
|
|
70
|
+
run feat4 staging; expect 1 "milestone-cut branch without merges fails" "$MS1"
|
|
71
|
+
|
|
72
|
+
# 6. bad refs fail closed (exit 2)
|
|
73
|
+
run nosuchbranch staging; expect 2 "bad branch ref exits 2"
|
|
74
|
+
run feat nosuchbase; expect 2 "bad base ref exits 2"
|
|
75
|
+
run feat; expect 2 "missing arg exits 2"
|
|
76
|
+
|
|
77
|
+
echo "check-branch-ancestry tests: $pass passed, $fail failed"
|
|
78
|
+
[ "$fail" -eq 0 ]
|
|
@@ -13,7 +13,7 @@ ok() { PASS=$((PASS+1)); }
|
|
|
13
13
|
bad() { FAILN=$((FAILN+1)); echo "FAIL: $1"; }
|
|
14
14
|
expect() { [ "$2" = "$3" ] && ok || bad "$1 (got '$3' want '$2')"; }
|
|
15
15
|
|
|
16
|
-
SITES="commands/work-on.md commands/work-on/review.md commands/work-on/investigate.md commands/work-on/decompose.md commands/work-on/build.md commands/work-on/close.md commands/review-pr.md commands/orchestrate/phase-1-resolve.md commands/orchestrate/phase-4-execution.md"
|
|
16
|
+
SITES="commands/work-on.md commands/work-on/build/validate.md commands/quality-gate.md commands/work-on/review.md commands/work-on/investigate.md commands/work-on/decompose.md commands/work-on/build.md commands/work-on/close.md commands/review-pr.md commands/orchestrate/phase-1-resolve.md commands/orchestrate/phase-4-execution.md"
|
|
17
17
|
|
|
18
18
|
# extract the bootstrap block (comment line .. closing top-level fi), indentation stripped
|
|
19
19
|
# Block ends at the first line (after the marker) that starts with `fi` at the marker's own indentation.
|
|
@@ -229,20 +229,41 @@ if command -v node >/dev/null 2>&1; then
|
|
|
229
229
|
done
|
|
230
230
|
fi
|
|
231
231
|
|
|
232
|
-
# Guard (#3400): gate-helper (trusted-comments.sh)
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
done
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
232
|
+
# Guard (#3400, #3454): every gate-helper (trusted-comments.sh) resolver block under commands/ must be
|
|
233
|
+
# byte-identical, must scan the plugin cache (the plugin-root placeholder is not always substituted in
|
|
234
|
+
# forked runs, so without the scan a cache-only install never resolves the script), and must never list the
|
|
235
|
+
# working directory (the repo under review is author-controlled). Blocks are discovered, not hard-coded, so a
|
|
236
|
+
# new copy cannot drift unnoticed. A block runs from its '# TRUSTED_SCRIPT resolver' header to 'done <<< "$_tc"'.
|
|
237
|
+
: > "$T/tc_all"; TC_BLOCKS=0; TC_STRAY=0
|
|
238
|
+
while IFS= read -r f; do
|
|
239
|
+
awk -v out="$T/tc_all" '
|
|
240
|
+
/^[ ]*# TRUSTED_SCRIPT resolver/ {inb=1; line=""}
|
|
241
|
+
inb { sub(/^[ ]+/, ""); line = line $0 "\\n" }
|
|
242
|
+
inb && /^done <<< "\$_tc"$/ { print line >> out; inb=0; n++ }
|
|
243
|
+
END { print n+0 }' "$f" > "$T/tc_n"
|
|
244
|
+
TC_BLOCKS=$((TC_BLOCKS + $(cat "$T/tc_n")))
|
|
245
|
+
# every trusted-comments.sh resolution case-arm must belong to a block (no stray short copies)
|
|
246
|
+
arms=$(grep -c 'trusted-comments.sh" \]' "$f" || true)
|
|
247
|
+
[ "$arms" = "$(cat "$T/tc_n")" ] || { TC_STRAY=$((TC_STRAY + 1)); echo " stray resolver in $f: $arms arms vs $(cat "$T/tc_n") blocks"; }
|
|
248
|
+
done < <(grep -rl 'trusted-comments.sh" \]' "$ROOT/commands")
|
|
249
|
+
expect "trusted-comments.sh resolver blocks discovered (review-pr 7 + review + remediate + phase-4)" 10 "$TC_BLOCKS"
|
|
250
|
+
expect "trusted-comments.sh resolvers without a canonical block" 0 "$TC_STRAY"
|
|
251
|
+
expect "trusted-comments.sh resolvers byte-identical" 1 "$(sort -u "$T/tc_all" | wc -l | tr -d ' ')"
|
|
252
|
+
grep -qF 'plugins/cache/forgedock/forgedock' "$T/tc_all" && ok || bad 'trusted-comments.sh resolver lacks the plugin-cache scan'
|
|
253
|
+
grep -qF 'CLAUDE_CONFIG_DIR' "$T/tc_all" && ok || bad 'trusted-comments.sh resolver lacks CLAUDE_CONFIG_DIR'
|
|
254
|
+
grep -qF '"$PWD"' "$T/tc_all" && bad 'trusted-comments.sh resolver lists "$PWD"' || ok
|
|
255
|
+
|
|
256
|
+
# Behavior (#3454): a cache-only install with an unsubstituted plugin-root placeholder must resolve the script.
|
|
257
|
+
# Run the real block from review.md against a fake HOME that has only plugins/cache/forgedock/forgedock/<semver>.
|
|
258
|
+
awk '/^[ ]*# TRUSTED_SCRIPT resolver/ {inb=1} inb {sub(/^[ ]+/, ""); print} inb && /^done <<< "\$_tc"$/ {exit}' "$ROOT/commands/work-on/review.md" > "$T/tc_block.sh"
|
|
259
|
+
mkdir -p "$T/tchome/.claude/plugins/cache/forgedock/forgedock/1.9.0/scripts" "$T/tchome/.claude/plugins/cache/forgedock/forgedock/1.12.0/scripts" "$T/tcwork"
|
|
260
|
+
: > "$T/tchome/.claude/plugins/cache/forgedock/forgedock/1.9.0/scripts/trusted-comments.sh"
|
|
261
|
+
: > "$T/tchome/.claude/plugins/cache/forgedock/forgedock/1.12.0/scripts/trusted-comments.sh"
|
|
262
|
+
mkdir -p "$T/tcwork/scripts"; : > "$T/tcwork/scripts/trusted-comments.sh" # author-controlled cwd copy must NOT win
|
|
263
|
+
out=$(cd "$T/tcwork" && env -i PATH="$PATH" HOME="$T/tchome" bash -c 'source "$1"; printf %s "$TRUSTED_SCRIPT"' _ "$T/tc_block.sh" 2>&1)
|
|
264
|
+
expect "cache-only install resolves newest cached trusted-comments.sh" "$T/tchome/.claude/plugins/cache/forgedock/forgedock/1.12.0/scripts/trusted-comments.sh" "$out"
|
|
265
|
+
out=$(cd "$T/tcwork" && env -i PATH="$PATH" HOME="$T/nohome" bash -c 'source "$1"; printf %s "$TRUSTED_SCRIPT"' _ "$T/tc_block.sh" 2>&1)
|
|
266
|
+
expect "no install resolves empty (cwd copy never used)" "" "$out"
|
|
246
267
|
|
|
247
268
|
echo "forge-root tests: pass=$PASS fail=$FAILN skipped=$SKIPPED"
|
|
248
269
|
[ "$FAILN" -eq 0 ]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# Guards the review-pr roster-label to marker-domain mapping: every roster spelling collapses to one marker
|
|
6
|
+
# domain, the helper is identical wherever it is inlined, the roster dedups across spellings, and every
|
|
7
|
+
# persona file is covered by the table and the catalog. Run: bash scripts/review-pr-domain-map.test.sh
|
|
8
|
+
# (bash 3.2 compatible)
|
|
9
|
+
|
|
10
|
+
set -u
|
|
11
|
+
DIR=$(cd "$(dirname "$0")" && pwd)
|
|
12
|
+
ROOT=$(cd "$DIR/.." && pwd)
|
|
13
|
+
SPEC="$ROOT/commands/review-pr.md"
|
|
14
|
+
CATALOG="$ROOT/commands/review-pr-agents.md"
|
|
15
|
+
PERSONAS="$ROOT/commands/review-pr-agents"
|
|
16
|
+
pass=0; fail=0
|
|
17
|
+
ok() { pass=$((pass+1)); }
|
|
18
|
+
bad() { fail=$((fail+1)); echo "FAIL: $1"; }
|
|
19
|
+
|
|
20
|
+
WORK=$(mktemp -d "${TMPDIR:-/tmp}/review-pr-domain-map.XXXXXX")
|
|
21
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
22
|
+
|
|
23
|
+
# Test 1: helper copies. Every inline definition must be byte-identical (each Bash call is a fresh shell).
|
|
24
|
+
awk -v dir="$WORK" '
|
|
25
|
+
/^# BEGIN review-pr-agent-domain/ {n++; out=dir "/helper." n; on=1}
|
|
26
|
+
on {print > out}
|
|
27
|
+
/^# END review-pr-agent-domain/ {on=0; close(out)}
|
|
28
|
+
' "$SPEC"
|
|
29
|
+
copies=$(ls "$WORK"/helper.* 2>/dev/null | wc -l | tr -d ' ')
|
|
30
|
+
if [ "$copies" -ge 4 ]; then ok; else bad "expected >=4 inline agent_domain copies (roster, canonicalize, dispatch, phase 4), found $copies"; fi
|
|
31
|
+
for f in "$WORK"/helper.*; do
|
|
32
|
+
[ -f "$f" ] || continue
|
|
33
|
+
if cmp -s "$f" "$WORK/helper.1"; then ok; else bad "$(basename "$f") differs from helper.1"; fi
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
# Test 2: no site derives the marker by lowercasing the roster label directly
|
|
37
|
+
if grep -nE "AGENT_DOMAIN=.*tr '\[:upper:\]' '\[:lower:\]'" "$SPEC" >/dev/null; then bad "direct lowercase marker derivation remains"; else ok; fi
|
|
38
|
+
sites=$(grep -c 'AGENT_DOMAIN=$(agent_domain "$AGENT")' "$SPEC")
|
|
39
|
+
if [ "$sites" -ge 2 ]; then ok; else bad "expected >=2 agent_domain derivation sites, found $sites"; fi
|
|
40
|
+
|
|
41
|
+
# Test 3: spelling collapse
|
|
42
|
+
# shellcheck disable=SC1090
|
|
43
|
+
. "$WORK/helper.1"
|
|
44
|
+
check() { got=$(agent_domain "$1"); if [ "$got" = "$2" ]; then ok; else bad "agent_domain '$1' = '$got', want '$2'"; fi; }
|
|
45
|
+
for l in INFRA Infrastructure infra infrastructure; do check "$l" infra; done
|
|
46
|
+
for l in SCRAPING Scraping scraper Scraper scraping; do check "$l" scraper; done
|
|
47
|
+
for l in FRONTEND Frontend frontend Web; do check "$l" frontend; done
|
|
48
|
+
for l in Security SECURITY; do check "$l" security; done
|
|
49
|
+
for l in AUTH Auth; do check "$l" auth; done
|
|
50
|
+
for l in BILLING Billing; do check "$l" billing; done
|
|
51
|
+
for l in CONCURRENCY Concurrency; do check "$l" concurrency; done
|
|
52
|
+
for l in DATABASE Database; do check "$l" database; done
|
|
53
|
+
for l in API Api; do check "$l" api; done
|
|
54
|
+
check "CustomDomain" customdomain
|
|
55
|
+
|
|
56
|
+
# Test 4: add_agent dedups across spellings; canonicalization block yields one entry per persona
|
|
57
|
+
ADD=$(awk '/^add_agent\(\) \{/{on=1} on{print} on && /^\}/{exit}' "$SPEC")
|
|
58
|
+
if [ -n "$ADD" ]; then ok; else bad "add_agent not found in spec"; fi
|
|
59
|
+
eval "$ADD"
|
|
60
|
+
SELECTED_AGENTS="Security INFRA SCRAPING"
|
|
61
|
+
add_agent "Infrastructure"; add_agent "Scraping"; add_agent "Auth"; add_agent "AUTH"; add_agent "security"
|
|
62
|
+
want=$(printf '%s' "Security INFRA SCRAPING auth")
|
|
63
|
+
if [ "$SELECTED_AGENTS" = "$want" ]; then ok; else bad "add_agent roster '$SELECTED_AGENTS', want '$want'"; fi
|
|
64
|
+
|
|
65
|
+
CANON=$(awk '/^# END review-pr-agent-domain/{seen++} seen && /^CANON_ROSTER=/{on=1} on{print} on && /^SELECTED_AGENTS=/{exit}' "$SPEC" | head -20)
|
|
66
|
+
SELECTED_AGENTS="Security AUTH INFRA Infrastructure SCRAPING Scraping Frontend API api"
|
|
67
|
+
eval "$CANON"
|
|
68
|
+
want="security auth infra scraper frontend api"
|
|
69
|
+
if [ "$SELECTED_AGENTS" = "$want" ]; then ok; else bad "canonical roster '$SELECTED_AGENTS', want '$want'"; fi
|
|
70
|
+
cnt=$(printf '%s' "$SELECTED_AGENTS" | tr ' ' '\n' | grep -c '.')
|
|
71
|
+
if [ "$cnt" -eq 6 ]; then ok; else bad "roster count $cnt, want 6"; fi
|
|
72
|
+
|
|
73
|
+
# Test 5: every persona file (minus protocols and spec-cli) has a table row in the spec and a catalog row
|
|
74
|
+
for pf in "$PERSONAS"/*.md; do
|
|
75
|
+
name=$(basename "$pf" .md)
|
|
76
|
+
case "$name" in protocols|spec-cli) continue ;; esac
|
|
77
|
+
if grep -qE "^\| .*\| \`$name\` \| \`$name\.md\` \|" "$SPEC"; then ok; else bad "spec table has no row for persona $name"; fi
|
|
78
|
+
if grep -qE "^\| .*\| \`$name\` \| \`review-pr-agents/$name\.md\` \|" "$CATALOG"; then ok; else bad "catalog has no marker-domain row for persona $name"; fi
|
|
79
|
+
got=$(agent_domain "$name")
|
|
80
|
+
if [ "$got" = "$name" ]; then ok; else bad "persona $name maps to $got"; fi
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
# Test 6: the protocol defines the marker domain through the mapping
|
|
84
|
+
if grep -qF 'marker domain' "$PERSONAS/protocols.md"; then ok; else bad "protocols.md does not define the marker domain"; fi
|
|
85
|
+
|
|
86
|
+
echo "review-pr-domain-map: $pass passed, $fail failed"
|
|
87
|
+
[ "$fail" -eq 0 ]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# spec-no-worktree-script.test.sh — guard/gate scripts named in specs must resolve from ForgeDock's
|
|
3
|
+
# install root only. A candidate under {WORKTREE_PATH}/scripts, {REPO_PATH}/scripts or a cwd-relative
|
|
4
|
+
# scripts/ is author-controlled (the PR branch under audit) and would let a branch supply the script
|
|
5
|
+
# that gates it. Read-only data uses (grep of a file's contents, JSON manifest reads) are not matched.
|
|
6
|
+
# Usage: bash scripts/spec-no-worktree-script.test.sh
|
|
7
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
8
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; ROOT="$HERE/.."
|
|
11
|
+
PASS=0; FAILN=0
|
|
12
|
+
ok() { PASS=$((PASS+1)); }
|
|
13
|
+
bad() { FAILN=$((FAILN+1)); echo "FAIL: $1"; }
|
|
14
|
+
|
|
15
|
+
# Execution-resolution patterns: a fallback assignment or candidate list pointing at a repo-controlled scripts dir.
|
|
16
|
+
PATTERNS=(
|
|
17
|
+
'\|\| [A-Za-z_]+="\{(WORKTREE_PATH|REPO_PATH)\}/scripts/[A-Za-z0-9_.-]+\.sh"'
|
|
18
|
+
'for _d in [^;]*\{(WORKTREE_PATH|REPO_PATH)\}/scripts'
|
|
19
|
+
'\$\{FORGEDOCK_SCRIPTS:-scripts\}'
|
|
20
|
+
'\|\| [A-Za-z_]+="scripts/[A-Za-z0-9_.-]+\.sh"'
|
|
21
|
+
)
|
|
22
|
+
FILES=$(cd "$ROOT" && find commands -name '*.md' | sort)
|
|
23
|
+
for p in "${PATTERNS[@]}"; do
|
|
24
|
+
hits=$(cd "$ROOT" && grep -nE "$p" $FILES 2>/dev/null || true)
|
|
25
|
+
[ -z "$hits" ] && ok || bad "repo-controlled script resolution matches /$p/: $hits"
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# Representative swept instances must each carry the install-root-only resolution.
|
|
29
|
+
chk() { grep -qF "$2" "$ROOT/$1" && ok || bad "$1 lacks: $2"; }
|
|
30
|
+
chk commands/work-on/build/validate.md 'ANCESTRY_SCRIPT="${FORGE_ROOT:+$FORGE_ROOT/scripts/check-branch-ancestry.sh}"'
|
|
31
|
+
chk commands/quality-gate.md 'VALIDATOR="${FORGE_ROOT:+$FORGE_ROOT/scripts/validate-spec-graph.sh}"'
|
|
32
|
+
chk commands/quality-gate.md 'CONFLICT_CHECKER="${FORGE_ROOT:+$FORGE_ROOT/scripts/check-native-conflicts.sh}"'
|
|
33
|
+
chk commands/quality-gate.md 'SPEC_BASH_CHECKER="${FORGE_ROOT:+$FORGE_ROOT/scripts/check-spec-bash.sh}"'
|
|
34
|
+
chk commands/quality-gate.md 'CLASSIFIER="${FORGE_ROOT:+$FORGE_ROOT/scripts/flaky-quarantine.sh}"'
|
|
35
|
+
chk commands/review-pr.md 'CLASSIFIER="${FORGE_ROOT:+$FORGE_ROOT/scripts/flaky-quarantine.sh}"'
|
|
36
|
+
# validate.md must not name the worktree scripts dir at all (ac-1)
|
|
37
|
+
grep -qF '{WORKTREE_PATH}/scripts' "$ROOT/commands/work-on/build/validate.md" && bad "validate.md references {WORKTREE_PATH}/scripts" || ok
|
|
38
|
+
|
|
39
|
+
# Behavioral: the validate.md ancestry resolver with FORGE_ROOT unset/empty must resolve to nothing (never /scripts or ./scripts).
|
|
40
|
+
for fr in "" "/nonexistent-root"; do
|
|
41
|
+
out=$(FORGE_ROOT="$fr" bash -c 'ANCESTRY_SCRIPT="${FORGE_ROOT:+$FORGE_ROOT/scripts/check-branch-ancestry.sh}"; [ -f "$ANCESTRY_SCRIPT" ] || ANCESTRY_SCRIPT=""; printf %s "$ANCESTRY_SCRIPT"')
|
|
42
|
+
[ -z "$out" ] && ok || bad "resolver yielded '$out' for FORGE_ROOT='$fr'"
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
echo "spec-no-worktree-script tests: pass=$PASS fail=$FAILN"
|
|
46
|
+
[ "$FAILN" -eq 0 ]
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
# GH_FLAG Repository flag passed to gh (e.g. -R RapierCraftStudios/forgedock)
|
|
9
9
|
# May be multiple tokens — pass before TARGET_STATE
|
|
10
10
|
# TARGET_STATE One of: investigating, ready-to-build, building, in-review,
|
|
11
|
-
# merged, invalid, decomposed, awaiting-merge
|
|
11
|
+
# merged, invalid, decomposed, awaiting-merge,
|
|
12
|
+
# remediating (label workflow:remediating: autonomous remediation pending, not a human gate)
|
|
12
13
|
#
|
|
13
14
|
# --validate Sub-command mode: translate an investigation verdict into a
|
|
14
15
|
# finding-lifecycle label (needs-validation → validated/false-positive).
|
|
@@ -184,6 +185,7 @@ VALID_STATES=(
|
|
|
184
185
|
"invalid"
|
|
185
186
|
"decomposed"
|
|
186
187
|
"awaiting-merge"
|
|
188
|
+
"remediating"
|
|
187
189
|
)
|
|
188
190
|
|
|
189
191
|
# ---------------------------------------------------------------------------
|