forgedock 1.16.2 → 1.17.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/bin/engine/outcome-codes.mjs +92 -0
- package/bin/engine/phases.mjs +86 -26
- package/bin/engine/projector.mjs +48 -1
- package/bin/engine/runlog.mjs +13 -5
- package/bin/engine/telemetry-seam.mjs +62 -0
- package/bin/engine/telemetry-seam.test.mjs +60 -0
- package/bin/engine/telemetry.mjs +148 -0
- package/bin/engine/telemetry.test.mjs +120 -0
- package/bin/engine-cli.mjs +12 -1
- package/bin/engine.mjs +145 -40
- package/bin/forgedock.mjs +7 -1
- package/bin/hooks/interactive-engine.mjs +10 -0
- package/bin/hooks/pre-tool-use.mjs +187 -0
- package/bin/runner.mjs +7 -0
- package/bin/stats/aggregate.mjs +231 -0
- package/bin/stats/cache.mjs +40 -0
- package/bin/stats/github-source.mjs +140 -0
- package/bin/stats/index.mjs +154 -0
- package/bin/stats/local-source.mjs +79 -0
- package/bin/stats/package.json +6 -0
- package/bin/stats/render.mjs +82 -0
- package/bin/stats/stats.test.mjs +224 -0
- package/commands/cleanup.md +83 -2
- package/commands/deploy-info.md +8 -4
- package/commands/orchestrate/phase-1-resolve.md +4 -1
- package/commands/orchestrate/phase-4-execution.md +17 -2
- package/commands/orchestrate/phase-5-cleanup.md +18 -2
- package/commands/pipeline-health/phase-1-context.md +2 -0
- package/commands/pipeline-health/phase-2-metrics.md +12 -5
- package/commands/pipeline-status.md +21 -1
- package/commands/review-pr-agents/protocols.md +1 -0
- package/commands/review-pr-agents.md +10 -2
- package/commands/review-pr-staging.md +102 -25
- package/commands/review-pr.md +119 -103
- package/commands/work-on/build/implement.md +26 -5
- package/commands/work-on/build/validate.md +3 -2
- package/commands/work-on/build.md +95 -64
- package/commands/work-on/close.md +17 -6
- package/commands/work-on/decompose.md +45 -4
- package/commands/work-on/investigate.md +6 -2
- package/commands/work-on/remediate.md +22 -6
- package/commands/work-on/review.md +2 -2
- package/docs/CONFIG.md +33 -0
- package/docs/OUTCOME-CODES.md +48 -0
- package/docs/RELEASE-NOTES.md +12 -0
- package/docs/STATS.md +62 -0
- package/docs/TELEMETRY.md +65 -0
- package/docs/WORK-ON-RUNTIME.md +13 -0
- package/docs/site/command-reference.md +1 -1
- package/docs/spec/label-state-machine.md +1 -1
- package/package.json +3 -2
- package/scripts/builder-resume-match.test.sh +58 -0
- package/scripts/cap-findings.sh +110 -0
- package/scripts/cap-findings.test.sh +130 -0
- package/scripts/check-spec-markers.sh +2 -0
- package/scripts/classify-finding.sh +65 -16
- package/scripts/classify-finding.test.sh +43 -1
- package/scripts/classify-grep-exit.sh +20 -0
- package/scripts/classify-grep-exit.test.sh +46 -0
- package/scripts/close-parent-c3.test.sh +54 -0
- package/scripts/cost-report.sh +146 -0
- package/scripts/cost-report.test.sh +80 -0
- package/scripts/diff-size.sh +5 -0
- package/scripts/doctor-pipeline-state.sh +43 -0
- package/scripts/file-deferred-followups.sh +159 -0
- package/scripts/file-deferred-followups.test.sh +106 -0
- package/scripts/fixtures/cost-report/a-merged1.jsonl +6 -0
- package/scripts/fixtures/cost-report/a-merged2-remediated.jsonl +7 -0
- package/scripts/fixtures/cost-report/a-needs-human.jsonl +4 -0
- package/scripts/fixtures/cost-report/b-merged.jsonl +3 -0
- package/scripts/fixtures/cost-report/c-merged-attempt-usage.jsonl +8 -0
- package/scripts/fixtures/cost-report/hydrated-no-usage.jsonl +3 -0
- package/scripts/fixtures/cost-report/old-log-no-version.jsonl +3 -0
- package/scripts/fixtures/cost-report/unfinished-no-terminal.jsonl +2 -0
- package/scripts/fixtures/cost-report/unfinished-reopened.jsonl +5 -0
- package/scripts/forge-root.test.sh +8 -3
- package/scripts/lib/fence-bootstrap.sh +4 -3
- package/scripts/lib/pr-issue-number.sh +18 -0
- package/scripts/lib/review-template-resolve.sh +41 -0
- package/scripts/lib/telemetry.sh +168 -0
- package/scripts/remediate-liveness.sh +42 -6
- package/scripts/review-acceptance-gate.sh +16 -7
- package/scripts/review-acceptance-gate.test.sh +13 -0
- package/scripts/review-pr-domain-map.test.sh +8 -3
- package/scripts/review-pr-gate2.test.sh +15 -0
- package/scripts/review-pr-staging-finding-filter.test.sh +44 -0
- package/scripts/review-pr-staging-scope.test.sh +65 -5
- package/scripts/review-prepare.sh +517 -0
- package/scripts/review-prepare.test.sh +270 -0
- package/scripts/review-roster.sh +172 -0
- package/scripts/review-roster.test.sh +163 -0
- package/scripts/run-shell-tests.sh +3 -0
- package/scripts/telemetry.test.sh +106 -0
- package/scripts/wait-ci-green.sh +8 -1
- package/templates/forge.yaml.minimal +9 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
# Tests for scripts/lib/telemetry.sh (forge_emit): record shape per event, upsert, rollover, buffer/flush,
|
|
5
|
+
# kill switch, never-fails guarantee. No network: gh is replaced via FORGE_TELEMETRY_GH.
|
|
6
|
+
set -u
|
|
7
|
+
HERE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
LIB="$HERE/lib/telemetry.sh"
|
|
9
|
+
PASS=0; FAIL=0
|
|
10
|
+
ok() { PASS=$((PASS+1)); }
|
|
11
|
+
bad() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
|
|
12
|
+
check() { if [ "$2" = "$3" ]; then ok; else bad "$1 (got '$2', want '$3')"; fi; }
|
|
13
|
+
|
|
14
|
+
TD=$(mktemp -d); trap 'rm -rf "$TD"' EXIT
|
|
15
|
+
export TD FORGE_REPO=o/r FORGE_ISSUE=5 FORGE_TELEMETRY_BUFFER_DIR="$TD/buf"
|
|
16
|
+
unset FORGE_TELEMETRY FORGE_PR FORGE_RUN FORGE_PHASE FORGE_TELEMETRY_MAX_CHARS
|
|
17
|
+
cat > "$TD/gh" <<'STUB'
|
|
18
|
+
#!/usr/bin/env bash
|
|
19
|
+
echo "$*" >> "$TD/calls"
|
|
20
|
+
case "$*" in
|
|
21
|
+
"api --paginate "*) if [ -f "$TD/comments.json" ]; then cat "$TD/comments.json"; else echo '[]'; fi ;;
|
|
22
|
+
"api -X PATCH"*) b="${@: -1}"; printf '%s' "${b#body=}" > "$TD/patched" ;;
|
|
23
|
+
"api -X POST"*) b="${@: -1}"; printf '%s' "${b#body=}" > "$TD/posted" ;;
|
|
24
|
+
esac
|
|
25
|
+
STUB
|
|
26
|
+
cat > "$TD/gh-fail" <<'STUB'
|
|
27
|
+
#!/usr/bin/env bash
|
|
28
|
+
exit 1
|
|
29
|
+
STUB
|
|
30
|
+
chmod +x "$TD/gh" "$TD/gh-fail"
|
|
31
|
+
export FORGE_TELEMETRY_GH="$TD/gh"
|
|
32
|
+
set_comments() { jq -n --arg b "$1" --arg t "${2:-Bot}" '[{id:77,body:$b,user:{type:$t},author_association:"NONE"}]' > "$TD/comments.json"; }
|
|
33
|
+
nrec() { printf '%s\n' "$1" | grep -c '^{'; }
|
|
34
|
+
reset() { rm -rf "$TD/calls" "$TD/posted" "$TD/patched" "$TD/comments.json" "$TD/buf"; }
|
|
35
|
+
|
|
36
|
+
. "$LIB"
|
|
37
|
+
|
|
38
|
+
# 1. One fixture per event shape: record carries the shared keys plus the payload, typed.
|
|
39
|
+
reset
|
|
40
|
+
forge_emit diff_size lines=10 excluded=2 threshold=0 result=OK
|
|
41
|
+
REC=$(grep '^{' "$TD/posted")
|
|
42
|
+
check "post has marker" "$(head -n 1 "$TD/posted")" "<!-- FORGE:TELEMETRY v1 -->"
|
|
43
|
+
check "shared keys" "$(printf '%s' "$REC" | jq -c 'keys_unsorted[:9]')" '["v","ts","forge_version","repo","issue","pr","run","phase","event"]'
|
|
44
|
+
check "issue number" "$(printf '%s' "$REC" | jq -r '.issue')" "5"
|
|
45
|
+
check "pr null" "$(printf '%s' "$REC" | jq -r '.pr')" "null"
|
|
46
|
+
check "numeric payload" "$(printf '%s' "$REC" | jq -r '.lines + .excluded')" "12"
|
|
47
|
+
check "string payload" "$(printf '%s' "$REC" | jq -r '.result')" "OK"
|
|
48
|
+
for fx in "acceptance_gate phase=review verified=3 failed=0 malformed=1 deferred=0:phase" \
|
|
49
|
+
"finding_disposition severity=HIGH confidence=CONFIRMED agent=auth disposition=ISSUE reason=severity-HIGH:disposition" \
|
|
50
|
+
"review_panel agents_selected=4 agents_completed=4 mode=FULL:mode" \
|
|
51
|
+
"ci_gate result=PASS rc=0 checks=7:checks" \
|
|
52
|
+
"liveness decision=DEFER why=lease:decision"; do
|
|
53
|
+
reset; FORGE_PR=9 forge_emit ${fx%%:*}
|
|
54
|
+
key=${fx##*:}; ev=${fx%% *}
|
|
55
|
+
check "fixture $ev event" "$(grep '^{' "$TD/posted" | jq -r '.event')" "$ev"
|
|
56
|
+
check "fixture $ev key $key" "$(grep '^{' "$TD/posted" | jq -r --arg k "$key" 'has($k)')" "true"
|
|
57
|
+
done
|
|
58
|
+
# review-side events go to the PR, others to the issue
|
|
59
|
+
reset; FORGE_PR=9 forge_emit review_panel mode=FULL; grep -q 'issues/9/comments' "$TD/calls" && ok || bad "review_panel targets PR"
|
|
60
|
+
reset; FORGE_PR=9 forge_emit diff_size lines=1; grep -q 'issues/5/comments' "$TD/calls" && ok || bad "diff_size targets issue"
|
|
61
|
+
|
|
62
|
+
# 2. Sanitising: reserved keys, bad keys, backticks and newlines never reach the record.
|
|
63
|
+
reset
|
|
64
|
+
forge_emit x repo=evil 'a b=1' ok='a`b
|
|
65
|
+
c'
|
|
66
|
+
REC=$(grep '^{' "$TD/posted")
|
|
67
|
+
check "reserved key ignored" "$(printf '%s' "$REC" | jq -r '.repo')" "o/r"
|
|
68
|
+
check "value sanitised" "$(printf '%s' "$REC" | jq -r '.ok')" "a_b c"
|
|
69
|
+
check "bad key dropped" "$(printf '%s' "$REC" | jq 'has("a b")')" "false"
|
|
70
|
+
|
|
71
|
+
# 3. Upsert edits the single trusted comment in place; an untrusted one is ignored.
|
|
72
|
+
reset; forge_emit ci_gate result=PASS; set_comments "$(cat "$TD/posted")"
|
|
73
|
+
forge_emit liveness decision=PROCEED
|
|
74
|
+
check "upsert patches" "$(nrec "$(cat "$TD/patched")")" "2"
|
|
75
|
+
check "no second post" "$(grep -c 'POST' "$TD/calls")" "1"
|
|
76
|
+
reset; forge_emit ci_gate result=PASS; set_comments "$(cat "$TD/posted")" User; rm -f "$TD/posted"
|
|
77
|
+
forge_emit liveness decision=PROCEED
|
|
78
|
+
[ -f "$TD/posted" ] && [ ! -f "$TD/patched" ] && ok || bad "untrusted comment must not be patched"
|
|
79
|
+
|
|
80
|
+
# 4. Rollover to part=N near the size limit.
|
|
81
|
+
reset; forge_emit ci_gate result=PASS; set_comments "$(cat "$TD/posted")"; rm -f "$TD/posted"
|
|
82
|
+
FORGE_TELEMETRY_MAX_CHARS=200 forge_emit liveness decision=PROCEED
|
|
83
|
+
check "rollover header" "$(head -n 1 "$TD/posted")" "<!-- FORGE:TELEMETRY v1 part=2 -->"
|
|
84
|
+
|
|
85
|
+
# 5. Buffer on failure, flush on next emit, nothing lost.
|
|
86
|
+
reset
|
|
87
|
+
FORGE_TELEMETRY_GH="$TD/gh-fail" forge_emit diff_size lines=1
|
|
88
|
+
check "buffered" "$(ls "$TD/buf")" "o_r__5.jsonl"
|
|
89
|
+
forge_emit diff_size lines=2
|
|
90
|
+
check "flushed both records" "$(nrec "$(cat "$TD/posted")")" "2"
|
|
91
|
+
[ ! -e "$TD/buf/o_r__5.jsonl" ] && ok || bad "buffer removed after flush"
|
|
92
|
+
|
|
93
|
+
# 6. Never fails the caller: unwritable buffer dir, no gh, kill switch, bad event name, caller options intact.
|
|
94
|
+
reset; : > "$TD/afile"
|
|
95
|
+
FORGE_TELEMETRY_GH=/nonexistent FORGE_TELEMETRY_BUFFER_DIR="$TD/afile/buf" forge_emit diff_size lines=1; check "unwritable dir rc" "$?" "0"
|
|
96
|
+
OUT=$(set -e; forge_emit ci_gate result=PASS; echo still-running); check "caller set -e survives" "$OUT" "still-running"
|
|
97
|
+
check "stdout silent" "$(forge_emit ci_gate result=PASS 2>&1)" ""
|
|
98
|
+
reset; FORGE_TELEMETRY=off forge_emit ci_gate result=PASS; [ ! -e "$TD/calls" ] && ok || bad "FORGE_TELEMETRY=off must not call gh"
|
|
99
|
+
reset; forge_emit 'bad event!' a=1; [ ! -e "$TD/calls" ] && ok || bad "bad event name dropped"
|
|
100
|
+
reset; (unset FORGE_ISSUE; forge_emit ci_gate result=PASS); [ ! -e "$TD/calls" ] && ok || bad "no target dropped"
|
|
101
|
+
|
|
102
|
+
# 7. Exec'd CLI form used by spec call sites.
|
|
103
|
+
reset; bash "$LIB" ci_gate result=PASS; check "cli rc" "$?" "0"; [ -f "$TD/posted" ] && ok || bad "cli form emits"
|
|
104
|
+
|
|
105
|
+
echo "telemetry.test.sh: $PASS passed, $FAIL failed"
|
|
106
|
+
[ "$FAIL" -eq 0 ]
|
package/scripts/wait-ci-green.sh
CHANGED
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
# "No checks" passes only when GitHub Actions also reports no workflow runs for the head commit;
|
|
31
31
|
# if runs exist but their checks have not appeared yet, the gate keeps waiting.
|
|
32
32
|
set -uo pipefail
|
|
33
|
+
# Telemetry (forge#3834): best-effort decision record; never changes stdout, stderr or the exit code.
|
|
34
|
+
_TEL="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/telemetry.sh"; [ -f "$_TEL" ] && . "$_TEL" 2>/dev/null
|
|
35
|
+
type forge_emit >/dev/null 2>&1 || forge_emit() { :; }
|
|
33
36
|
|
|
34
37
|
PR=""; REPO=""
|
|
35
38
|
TIMEOUT="${FORGE_CI_TIMEOUT:-540}"; INTERVAL="${FORGE_CI_INTERVAL:-20}"; SETTLE="${FORGE_CI_SETTLE:-45}"
|
|
@@ -50,7 +53,11 @@ case "$REPO" in */*) ;; *) usage ;; esac
|
|
|
50
53
|
for v in "$TIMEOUT" "$INTERVAL" "$GRACE" "$SETTLE"; do case "$v" in ''|*[!0-9]*) usage ;; esac; done
|
|
51
54
|
[ "$INTERVAL" -ge 1 ] || INTERVAL=1
|
|
52
55
|
|
|
53
|
-
emit() {
|
|
56
|
+
emit() {
|
|
57
|
+
case "$1" in PASS) _rc=0 ;; FAIL) _rc=1 ;; TIMEOUT) _rc=3 ;; *) _rc=2 ;; esac
|
|
58
|
+
FORGE_REPO="${FORGE_REPO:-$REPO}" FORGE_PR="${FORGE_PR:-$PR}" forge_emit ci_gate result="$1" rc="$_rc" checks="${total:-0}"
|
|
59
|
+
echo "CI_GATE: $1"; echo "CI_GATE_HEAD: ${START_SHA:-unknown}"
|
|
60
|
+
}
|
|
54
61
|
head_sha() { gh pr view "$PR" -R "$REPO" --json headRefOid --jq '.headRefOid' 2>/dev/null; }
|
|
55
62
|
START_SHA="$(head_sha)"
|
|
56
63
|
if [ -z "$START_SHA" ]; then START_SHA=""; emit ERROR; echo "cannot read PR #$PR head in $REPO"; exit 2; fi
|
|
@@ -42,3 +42,12 @@ branches:
|
|
|
42
42
|
default: "main"
|
|
43
43
|
staging: "staging"
|
|
44
44
|
feature_pattern: "milestone/{slug}"
|
|
45
|
+
|
|
46
|
+
# =============================================================================
|
|
47
|
+
# DECOMPOSITION (OPTIONAL)
|
|
48
|
+
# Set close_parent: true to close a decomposed parent issue at decomposition
|
|
49
|
+
# (default false: it stays open until its last child closes).
|
|
50
|
+
# =============================================================================
|
|
51
|
+
|
|
52
|
+
# decomposition:
|
|
53
|
+
# close_parent: false
|