@mmerterden/multi-agent-pipeline 12.4.0 → 12.6.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/CHANGELOG.md +169 -0
- package/README.md +1 -1
- package/docs/features.md +20 -0
- package/index.js +7 -1
- package/install/_dev-only-files.mjs +1 -0
- package/package.json +4 -3
- package/pipeline/agents/security-auditor.md +1 -1
- package/pipeline/commands/archive-guard.md +5 -5
- package/pipeline/commands/multi-agent/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +45 -5
- package/pipeline/commands/multi-agent/refactor/SKILL.md +92 -12
- package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +119 -12
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/commands/sim-test.md +5 -5
- package/pipeline/lib/credential-store.sh +32 -0
- package/pipeline/lib/extract-conventions.sh +1 -0
- package/pipeline/lib/repo-cache.sh +1 -0
- package/pipeline/lib/shadow-git.sh +8 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +3 -3
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +3 -2
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +14 -4
- package/pipeline/multi-agent-refs/phases.md +2 -0
- package/pipeline/multi-agent-refs/prompt-assembly.md +31 -0
- package/pipeline/preferences-template.json +18 -1
- package/pipeline/schemas/agent-state.schema.json +90 -0
- package/pipeline/schemas/design-check-config.schema.json +162 -0
- package/pipeline/schemas/learnings-ledger.schema.json +4 -0
- package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
- package/pipeline/schemas/prefs.schema.json +161 -5
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/schemas/triage-corpus.schema.json +5 -1
- package/pipeline/scripts/README.md +7 -5
- package/pipeline/scripts/classify-plan-safety.mjs +8 -3
- package/pipeline/scripts/cost-budget-check.mjs +9 -5
- package/pipeline/scripts/eval-mine-corpus.mjs +19 -5
- package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
- package/pipeline/scripts/gc-worktrees.sh +23 -1
- package/pipeline/scripts/learning-curve.mjs +167 -0
- package/pipeline/scripts/learnings-ledger.mjs +27 -2
- package/pipeline/scripts/lint-mcp-refs.mjs +207 -0
- package/pipeline/scripts/memory-load.sh +5 -1
- package/pipeline/scripts/render-work-summary.sh +4 -1
- package/pipeline/scripts/repo-map.mjs +1 -1
- package/pipeline/scripts/smoke-command-inventory.sh +81 -0
- package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
- package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
- package/pipeline/scripts/smoke-generate-issue.sh +6 -5
- package/pipeline/scripts/smoke-learning-curve.sh +61 -0
- package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
- package/pipeline/scripts/smoke-review-readiness.sh +3 -2
- package/pipeline/scripts/smoke-schema-validation.sh +19 -5
- package/pipeline/scripts/smoke-shadow-git.sh +4 -2
- package/pipeline/scripts/triage-memory.mjs +66 -10
- package/pipeline/scripts/uninstall.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +24 -8
- package/pipeline/skills/.skills-index.json +39 -3
- package/pipeline/skills/shared/README.md +10 -6
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +22 -0
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +67 -11
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
- package/pipeline/skills/skills-index.md +8 -4
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-command-inventory.sh - the command inventory is DERIVED, never asserted.
|
|
3
|
+
#
|
|
4
|
+
# Every count that describes "how many multi-agent commands exist" used to be a
|
|
5
|
+
# literal in prose (contract header, sync skill, dispatcher). Adding a command
|
|
6
|
+
# left those literals stale while every gate stayed green, because the gates
|
|
7
|
+
# asserted the literal instead of the tree. This gate computes the count from
|
|
8
|
+
# pipeline/commands/multi-agent/*/ and requires the prose to agree.
|
|
9
|
+
#
|
|
10
|
+
# Verifies:
|
|
11
|
+
# 1. contract header count == number of command directories
|
|
12
|
+
# 2. every command directory appears in the contract inventory block
|
|
13
|
+
# 3. both sync surfaces (command + Copilot skill) carry the same count twice
|
|
14
|
+
# ("N commands are synced" + "instructions + N sub-command skills")
|
|
15
|
+
# 4. every command directory appears in both sync inventory blocks
|
|
16
|
+
# 5. every command directory has its shared/core skill counterpart
|
|
17
|
+
#
|
|
18
|
+
# Exit: 0 all green, 1 any failure.
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
23
|
+
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
24
|
+
CMD_DIR="$ROOT/pipeline/commands/multi-agent"
|
|
25
|
+
CONTRACT="$ROOT/pipeline/multi-agent-refs/cross-cli-contract.md"
|
|
26
|
+
SYNC="$ROOT/pipeline/commands/multi-agent/sync/SKILL.md"
|
|
27
|
+
SYNC_SKILL="$ROOT/pipeline/skills/shared/core/multi-agent-sync/SKILL.md"
|
|
28
|
+
SKILLS_DIR="$ROOT/pipeline/skills/shared/core"
|
|
29
|
+
|
|
30
|
+
PASS=0
|
|
31
|
+
FAIL=0
|
|
32
|
+
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
33
|
+
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
34
|
+
|
|
35
|
+
echo "══ smoke-command-inventory ══"
|
|
36
|
+
|
|
37
|
+
for f in "$CONTRACT" "$SYNC" "$SYNC_SKILL"; do
|
|
38
|
+
[ -f "$f" ] || { echo " ✗ missing file: ${f#$ROOT/}"; exit 1; }
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
COMMANDS=$(find "$CMD_DIR" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort)
|
|
42
|
+
N=$(printf '%s\n' "$COMMANDS" | grep -c . || true)
|
|
43
|
+
|
|
44
|
+
echo "→ 1. derived count from the command tree: $N commands"
|
|
45
|
+
[ "$N" -gt 0 ] && pass "found $N command directories" || fail "no command directories under ${CMD_DIR#$ROOT/}"
|
|
46
|
+
|
|
47
|
+
echo "→ 2. contract header agrees"
|
|
48
|
+
if grep -Fq "## 1. Command Inventory ($N commands)" "$CONTRACT"; then
|
|
49
|
+
pass "contract header = $N"
|
|
50
|
+
else
|
|
51
|
+
fail "contract header does not say ($N commands): $(grep -o '## 1\. Command Inventory ([0-9]* commands)' "$CONTRACT" || echo 'header not found')"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo "→ 3. sync surfaces agree (2 counts x 2 files)"
|
|
55
|
+
for f in "$SYNC" "$SYNC_SKILL"; do
|
|
56
|
+
rel="${f#$ROOT/}"
|
|
57
|
+
grep -Fq "**$N commands are synced**" "$f" && pass "$rel: synced count = $N" || fail "$rel: 'N commands are synced' != $N"
|
|
58
|
+
grep -Fq "instructions + $N sub-command skills" "$f" && pass "$rel: step-2 count = $N" || fail "$rel: 'instructions + N sub-command skills' != $N"
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
echo "→ 4. every command is listed in the contract + both sync inventories"
|
|
62
|
+
MISSING_LIST=0
|
|
63
|
+
while IFS= read -r cmd; do
|
|
64
|
+
[ -n "$cmd" ] || continue
|
|
65
|
+
for f in "$CONTRACT" "$SYNC" "$SYNC_SKILL"; do
|
|
66
|
+
grep -Eq "(^|[ ,])$cmd([ ,]|$)" "$f" || { fail "${f#$ROOT/}: inventory missing '$cmd'"; MISSING_LIST=1; }
|
|
67
|
+
done
|
|
68
|
+
done <<< "$COMMANDS"
|
|
69
|
+
[ "$MISSING_LIST" -eq 0 ] && pass "all $N commands listed on all 3 surfaces"
|
|
70
|
+
|
|
71
|
+
echo "→ 5. shared/core skill counterpart exists for every command"
|
|
72
|
+
MISSING_SKILL=0
|
|
73
|
+
while IFS= read -r cmd; do
|
|
74
|
+
[ -n "$cmd" ] || continue
|
|
75
|
+
[ -f "$SKILLS_DIR/multi-agent-$cmd/SKILL.md" ] || { fail "no shared/core skill for '$cmd'"; MISSING_SKILL=1; }
|
|
76
|
+
done <<< "$COMMANDS"
|
|
77
|
+
[ "$MISSING_SKILL" -eq 0 ] && pass "all $N commands have a shared/core skill"
|
|
78
|
+
|
|
79
|
+
echo ""
|
|
80
|
+
echo "══ command-inventory smoke: $PASS passed, $FAIL failed ══"
|
|
81
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -36,7 +36,7 @@ fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
|
36
36
|
# Underscore-prefixed files (e.g. _account-picker.md) are internal fragments
|
|
37
37
|
# loaded by other commands via Read; they're not invocable slash commands and
|
|
38
38
|
# intentionally have no matching skill.
|
|
39
|
-
CMDS=$(find "$CMDS_DIR" -mindepth 2 -maxdepth 2 -name "SKILL.md" -exec dirname
|
|
39
|
+
CMDS=$(find "$CMDS_DIR" -mindepth 2 -maxdepth 2 -name "SKILL.md" -exec sh -c 'for f do basename "$(dirname "$f")"; done' sh {} + | sort)
|
|
40
40
|
|
|
41
41
|
# help.md in commands maps to multi-agent-help skill; the main "multi-agent"
|
|
42
42
|
# skill covers the behavior documented across multiple command files, so we
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# that the 4 claimed consumer surfaces actually reference them (not just promise).
|
|
6
6
|
#
|
|
7
7
|
# Covers:
|
|
8
|
-
# 1. apple-archive-compliance/SKILL.md exists, has frontmatter,
|
|
8
|
+
# 1. apple-archive-compliance/SKILL.md exists, has frontmatter, 18-rule catalog,
|
|
9
9
|
# EN + TR humanizer blocks, severity table, prerequisite detection.
|
|
10
10
|
# 2. google-play-compliance/SKILL.md exists, has frontmatter, 21-rule catalog
|
|
11
11
|
# across 4 categories, EN + TR humanizer blocks, severity table, prereq detection.
|
|
@@ -43,8 +43,8 @@ grep -q "^name: apple-archive-compliance$" "$APPLE_SKILL" && pass "apple frontma
|
|
|
43
43
|
grep -q "^user-invocable: true$" "$APPLE_SKILL" && pass "apple user-invocable" || fail "apple user-invocable"
|
|
44
44
|
grep -q "argument-hint:" "$APPLE_SKILL" && pass "apple argument-hint" || fail "apple argument-hint"
|
|
45
45
|
# Count rule rows in the catalog table (excluding header + separator)
|
|
46
|
-
APPLE_RULES=$(awk '/^##
|
|
47
|
-
[ "$APPLE_RULES" -eq
|
|
46
|
+
APPLE_RULES=$(awk '/^## 18-Rule Catalog/,/^## Severity mapping/' "$APPLE_SKILL" | grep -cE '^\| *[0-9]+ *\|' || true)
|
|
47
|
+
[ "$APPLE_RULES" -eq 18 ] && pass "apple catalog has 18 rule rows" || fail "apple catalog has $APPLE_RULES rule rows (want 18)"
|
|
48
48
|
|
|
49
49
|
echo "→ 2. apple skill humanizer blocks EN + TR + severity table"
|
|
50
50
|
# v8.2 split humanizer language from picker language: gating moved from
|
|
@@ -59,7 +59,7 @@ echo "→ 3. apple skill prerequisite detection + archive resolution (v8.4.0+ us
|
|
|
59
59
|
grep -q 'ios_app_store_audit' "$APPLE_SKILL" && pass "apple references ios_app_store_audit MCP tool" || fail "apple ios_app_store_audit reference missing"
|
|
60
60
|
grep -q '.xcarchive' "$APPLE_SKILL" && pass "apple references .xcarchive" || fail "apple .xcarchive missing"
|
|
61
61
|
grep -q '@mmerterden/dev-toolkit-mcp' "$APPLE_SKILL" && pass "apple references @mmerterden/dev-toolkit-mcp package" || fail "apple dev-toolkit-mcp reference missing"
|
|
62
|
-
grep -q '
|
|
62
|
+
grep -q 'mcp__dev-toolkit__ios_app_store_audit' "$APPLE_SKILL" && pass "apple shows MCP-call invocation form" || fail "apple MCP-call invocation form missing"
|
|
63
63
|
|
|
64
64
|
echo "→ 4. google-play-compliance SKILL.md frontmatter + catalog shape"
|
|
65
65
|
grep -q "^name: google-play-compliance$" "$GOOGLE_SKILL" && pass "google frontmatter name" || fail "google frontmatter name"
|
|
@@ -61,6 +61,16 @@ fi
|
|
|
61
61
|
# ──────────────────────────────────────────────────────────────────────────
|
|
62
62
|
echo ""
|
|
63
63
|
echo "→ 2. No deprecated placeholders in Claude source"
|
|
64
|
+
# Corpus guard: the scans below can only be trusted if there is something to
|
|
65
|
+
# scan. Without this, an empty or missing command tree reports every pattern
|
|
66
|
+
# clean instead of reporting that it found nothing to check.
|
|
67
|
+
SKILL_COUNT=$(find "$CLAUDE_CMDS" -name SKILL.md 2>/dev/null | wc -l | tr -d ' ')
|
|
68
|
+
if [ "$SKILL_COUNT" -gt 0 ]; then
|
|
69
|
+
pass "command corpus non-empty ($SKILL_COUNT SKILL.md)"
|
|
70
|
+
else
|
|
71
|
+
fail "no SKILL.md under $CLAUDE_CMDS - placeholder and hardcode scans below are vacuous"
|
|
72
|
+
fi
|
|
73
|
+
|
|
64
74
|
# Per refs/cross-cli-contract.md Section 2.5
|
|
65
75
|
DEPRECATED=(
|
|
66
76
|
"{github-username}"
|
|
@@ -72,7 +82,7 @@ DEPRECATED=(
|
|
|
72
82
|
)
|
|
73
83
|
for p in "${DEPRECATED[@]}"; do
|
|
74
84
|
# grep -l returns filenames; empty = no matches
|
|
75
|
-
FOUND=$(grep -l -F -- "$p"
|
|
85
|
+
FOUND=$(find "$CLAUDE_CMDS" -name SKILL.md -exec grep -l -F -- "$p" {} + 2>/dev/null || true)
|
|
76
86
|
if [ -z "$FOUND" ]; then
|
|
77
87
|
pass "no '$p' in Claude commands"
|
|
78
88
|
else
|
|
@@ -88,7 +98,7 @@ PERSONAL_HARDCODES=(
|
|
|
88
98
|
"${USER}_tkgithub"
|
|
89
99
|
)
|
|
90
100
|
for p in "${PERSONAL_HARDCODES[@]}"; do
|
|
91
|
-
FOUND=$(grep -l -F -- "$p"
|
|
101
|
+
FOUND=$(find "$CLAUDE_CMDS" -name SKILL.md -exec grep -l -F -- "$p" {} + 2>/dev/null || true)
|
|
92
102
|
if [ -z "$FOUND" ]; then
|
|
93
103
|
pass "no '$p' leaked into Claude commands"
|
|
94
104
|
else
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# 5. Convention mining + createmeta + Agile sprint endpoints present.
|
|
10
10
|
# 6. UTF-8 verbatim POST path (--rawfile + --data-binary) present.
|
|
11
11
|
# 7. Standard template vocab: Task/Story (Scope/Acceptance/Test Scenarios), Bug (Steps/Expected/Actual/Environment), Swagger.
|
|
12
|
-
# 8. Registration: dispatcher rows, inventory lists, count
|
|
12
|
+
# 8. Registration: dispatcher rows, inventory lists, count derived from the command tree.
|
|
13
13
|
# 9. Forbidden strings absent (em-dash, section sign, the M-word, unicode_escape).
|
|
14
14
|
|
|
15
15
|
set -euo pipefail
|
|
@@ -21,6 +21,7 @@ REF="$ROOT/pipeline/multi-agent-refs/generate-issue.md"
|
|
|
21
21
|
SKILL="$ROOT/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md"
|
|
22
22
|
DISPATCHER="$ROOT/pipeline/commands/multi-agent/SKILL.md"
|
|
23
23
|
CONTRACT="$ROOT/pipeline/multi-agent-refs/cross-cli-contract.md"
|
|
24
|
+
CMD_COUNT=$(find "$ROOT/pipeline/commands/multi-agent" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
|
|
24
25
|
SYNC="$ROOT/pipeline/commands/multi-agent/sync/SKILL.md"
|
|
25
26
|
HELP="$ROOT/pipeline/commands/multi-agent/help/SKILL.md"
|
|
26
27
|
|
|
@@ -88,14 +89,14 @@ grep -Fqi "auto-sizing" "$REF" && pass "auto-sizing rule present" || fail "auto-
|
|
|
88
89
|
grep -Fqi "swagger" "$REF" && pass "swagger context present" || fail "swagger context missing"
|
|
89
90
|
|
|
90
91
|
echo ""
|
|
91
|
-
echo "→ 8. registration + count consistency (
|
|
92
|
+
echo "→ 8. registration + count consistency (derived)"
|
|
92
93
|
grep -Fq '| `create-jira ' "$DISPATCHER" && pass "dispatcher routing: create-jira" || fail "dispatcher routing missing: generate"
|
|
93
94
|
grep -Fq "create-jira/SKILL.md" "$DISPATCHER" && pass "dispatcher loading row" || fail "dispatcher loading row missing"
|
|
94
|
-
grep -Fq "## 1. Command Inventory (
|
|
95
|
+
grep -Fq "## 1. Command Inventory ($CMD_COUNT commands)" "$CONTRACT" && pass "contract count = $CMD_COUNT (derived)" || fail "contract count wrong"
|
|
95
96
|
grep -Eq '(^|[ ,])create-jira([ ,]|$)' "$CONTRACT" && pass "contract list has create-jira" || fail "contract list missing generate"
|
|
96
|
-
grep -Fq "
|
|
97
|
+
grep -Fq "**$CMD_COUNT commands are synced**" "$SYNC" && pass "sync count = $CMD_COUNT (derived)" || fail "sync count wrong"
|
|
97
98
|
SYNC_SKILL="$ROOT/pipeline/skills/shared/core/multi-agent-sync/SKILL.md"
|
|
98
|
-
grep -Fq "
|
|
99
|
+
grep -Fq "**$CMD_COUNT commands are synced**" "$SYNC_SKILL" && pass "sync SKILL count = $CMD_COUNT (derived)" || fail "sync SKILL count wrong (Copilot parity)"
|
|
99
100
|
grep -Fq "create-jira" "$ROOT/pipeline/skills/shared/core/multi-agent-help/SKILL.md" && pass "help SKILL lists create-jira" || fail "help SKILL missing create-jira (Copilot parity)"
|
|
100
101
|
grep -c "multi-agent:create-jira" "$HELP" | awk '{exit !($1>=2)}' && pass "help.md has EN + TR entries" || fail "help.md entries incomplete"
|
|
101
102
|
# no stale split-command references anywhere in the synced surfaces
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-learning-curve.sh - contract for learning-curve.mjs (time-bucketed
|
|
3
|
+
# trend over metrics.jsonl).
|
|
4
|
+
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
LC="$SCRIPT_DIR/learning-curve.mjs"
|
|
9
|
+
|
|
10
|
+
PASS=0
|
|
11
|
+
FAIL=0
|
|
12
|
+
record_pass() { PASS=$((PASS + 1)); printf ' \342\234\223 %s\n' "$1"; }
|
|
13
|
+
record_fail() { FAIL=$((FAIL + 1)); printf ' \342\234\227 %s\n' "$1"; }
|
|
14
|
+
|
|
15
|
+
printf '\342\206\222 smoke-learning-curve: metrics trend contract\n'
|
|
16
|
+
|
|
17
|
+
tmp=$(mktemp -d "${TMPDIR:-/tmp}/lc.XXXXXX")
|
|
18
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
19
|
+
M="$tmp/metrics.jsonl"
|
|
20
|
+
|
|
21
|
+
# Two early tasks (noisy: 3/2 review cycles, low cache) + two later tasks
|
|
22
|
+
# (clean: 1 cycle, high cache) -> the curve should improve.
|
|
23
|
+
cat > "$M" <<'EOF'
|
|
24
|
+
{"ts":"2026-06-02T10:00:00Z","task_id":"t1","phase":"4","event":"review.completed","details":{"review_cycles":3,"tokens_in":100000,"tokens_out":8000,"tokens_cached":10000}}
|
|
25
|
+
{"ts":"2026-06-02T10:05:00Z","task_id":"t1","phase":"3","event":"rework.started","details":{}}
|
|
26
|
+
{"ts":"2026-06-03T09:00:00Z","task_id":"t2","phase":"4","event":"review.completed","details":{"review_cycles":2,"tokens_in":90000,"tokens_out":7000,"tokens_cached":20000}}
|
|
27
|
+
{"ts":"2026-06-24T09:00:00Z","task_id":"t3","phase":"4","event":"review.completed","details":{"review_cycles":1,"tokens_in":40000,"tokens_out":4000,"tokens_cached":30000}}
|
|
28
|
+
{"ts":"2026-06-25T09:00:00Z","task_id":"t4","phase":"4","event":"review.completed","details":{"review_cycles":1,"tokens_in":38000,"tokens_out":3500,"tokens_cached":32000}}
|
|
29
|
+
EOF
|
|
30
|
+
|
|
31
|
+
# 1. parses
|
|
32
|
+
bash -n "$SCRIPT_DIR/smoke-learning-curve.sh" >/dev/null 2>&1 || true
|
|
33
|
+
if node --check "$LC" 2>/dev/null; then record_pass "learning-curve.mjs parses"; else record_fail "syntax error"; fi
|
|
34
|
+
|
|
35
|
+
# 2. JSON: buckets + improving trend
|
|
36
|
+
out=$(METRICS_FILE="$M" node "$LC" --json 2>/dev/null)
|
|
37
|
+
nbuckets=$(printf '%s' "$out" | python3 -c "import json,sys;print(len(json.load(sys.stdin)['buckets']))")
|
|
38
|
+
[ "$nbuckets" -ge 2 ] && record_pass "produces >=2 time buckets" || record_fail "expected >=2 buckets, got $nbuckets"
|
|
39
|
+
|
|
40
|
+
tokens_delta=$(printf '%s' "$out" | python3 -c "import json,sys;print(json.load(sys.stdin)['trend']['tokensPerTask'])")
|
|
41
|
+
case "$tokens_delta" in
|
|
42
|
+
-*) record_pass "tokens/task trend is negative (improving)" ;;
|
|
43
|
+
*) record_fail "tokens/task trend should drop, got $tokens_delta" ;;
|
|
44
|
+
esac
|
|
45
|
+
cache_delta=$(printf '%s' "$out" | python3 -c "import json,sys;print(json.load(sys.stdin)['trend']['cacheRatio'])")
|
|
46
|
+
case "$cache_delta" in
|
|
47
|
+
-*|0|0.0) record_fail "cache ratio should rise, got $cache_delta" ;;
|
|
48
|
+
*) record_pass "cache ratio trend rises (improving)" ;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
# 3. markdown renders a table + trend
|
|
52
|
+
md=$(METRICS_FILE="$M" node "$LC" 2>/dev/null)
|
|
53
|
+
printf '%s' "$md" | grep -q "Learning curve" && record_pass "markdown has a header" || record_fail "no markdown header"
|
|
54
|
+
printf '%s' "$md" | grep -q "Trend" && record_pass "markdown shows a trend block" || record_fail "no trend block"
|
|
55
|
+
|
|
56
|
+
# 4. missing metrics -> graceful, exit 0
|
|
57
|
+
METRICS_FILE="/nope/none.jsonl" node "$LC" --json >/dev/null 2>&1
|
|
58
|
+
[ "$?" -eq 0 ] && record_pass "missing metrics is a graceful no-op (exit 0)" || record_fail "missing metrics should exit 0"
|
|
59
|
+
|
|
60
|
+
printf '\n\342\225\220\342\225\220 smoke-learning-curve: %d passed, %d failed \342\225\220\342\225\220\n' "$PASS" "$FAIL"
|
|
61
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -35,7 +35,7 @@ repo="$sandbox/repo"
|
|
|
35
35
|
mkdir -p "$repo"
|
|
36
36
|
|
|
37
37
|
# --- 1: pref off → silent exit 0 ---
|
|
38
|
-
printf '{"global":{"perRepoMemory":false}}' > "$HOME/.claude/preferences.json"
|
|
38
|
+
printf '{"global":{"perRepoMemory":false}}' > "$HOME/.claude/multi-agent-preferences.json"
|
|
39
39
|
out=$(bash "$LOAD" "$repo" 2>&1 || true)
|
|
40
40
|
if [ -z "$out" ]; then
|
|
41
41
|
record_pass "load exits silent when pref off"
|
|
@@ -44,7 +44,7 @@ else
|
|
|
44
44
|
fi
|
|
45
45
|
|
|
46
46
|
# --- 2: pref on + no memory → silent exit 0 ---
|
|
47
|
-
printf '{"global":{"perRepoMemory":true}}' > "$HOME/.claude/preferences.json"
|
|
47
|
+
printf '{"global":{"perRepoMemory":true}}' > "$HOME/.claude/multi-agent-preferences.json"
|
|
48
48
|
out=$(bash "$LOAD" "$repo" 2>&1 || true)
|
|
49
49
|
if [ -z "$out" ]; then
|
|
50
50
|
record_pass "load exits silent when pref on but no memory present"
|
|
@@ -21,6 +21,7 @@ SK_J="$ROOT/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md"
|
|
|
21
21
|
SK_I="$ROOT/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md"
|
|
22
22
|
DISPATCHER="$ROOT/pipeline/commands/multi-agent/SKILL.md"
|
|
23
23
|
CONTRACT="$ROOT/pipeline/multi-agent-refs/cross-cli-contract.md"
|
|
24
|
+
CMD_COUNT=$(find "$ROOT/pipeline/commands/multi-agent" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
|
|
24
25
|
HELP="$ROOT/pipeline/commands/multi-agent/help/SKILL.md"
|
|
25
26
|
|
|
26
27
|
PASS=0; FAIL=0
|
|
@@ -61,12 +62,12 @@ grep -Fqi "confirm" "$REF" && pass "ref has confirm-before-post gate" || fail "r
|
|
|
61
62
|
grep -Fqi "rubric" "$REF" && pass "ref has readiness rubric" || fail "ref missing rubric"
|
|
62
63
|
|
|
63
64
|
echo ""
|
|
64
|
-
echo "→ 5. registration + count (
|
|
65
|
+
echo "→ 5. registration + count (derived)"
|
|
65
66
|
grep -Fq '| `review-jira ' "$DISPATCHER" && pass "dispatcher desc: review-jira" || fail "dispatcher desc missing review-jira"
|
|
66
67
|
grep -Fq '| `review-issue ' "$DISPATCHER" && pass "dispatcher desc: review-issue" || fail "dispatcher desc missing review-issue"
|
|
67
68
|
grep -Fq "review-jira/SKILL.md" "$DISPATCHER" && pass "dispatcher routing: review-jira" || fail "dispatcher routing missing review-jira"
|
|
68
69
|
grep -Fq "review-issue/SKILL.md" "$DISPATCHER" && pass "dispatcher routing: review-issue" || fail "dispatcher routing missing review-issue"
|
|
69
|
-
grep -Fq "## 1. Command Inventory (
|
|
70
|
+
grep -Fq "## 1. Command Inventory ($CMD_COUNT commands)" "$CONTRACT" && pass "contract count = $CMD_COUNT (derived)" || fail "contract count wrong"
|
|
70
71
|
grep -Eq '(^|[ ,])review-jira([ ,]|$)' "$CONTRACT" && pass "contract inventory has review-jira" || fail "contract missing review-jira"
|
|
71
72
|
grep -Eq '(^|[ ,])review-issue([ ,]|$)' "$CONTRACT" && pass "contract inventory has review-issue" || fail "contract missing review-issue"
|
|
72
73
|
grep -Fq "multi-agent:review-jira" "$HELP" && pass "help lists review-jira" || fail "help missing review-jira"
|
|
@@ -87,17 +87,31 @@ done
|
|
|
87
87
|
|
|
88
88
|
# ──────────────────────────────────────────────────────────────────────────
|
|
89
89
|
echo ""
|
|
90
|
-
echo "→ 4. prefs.schema.json - keychainMapping
|
|
90
|
+
echo "→ 4. prefs.schema.json - keychainMapping covers every resolved service"
|
|
91
|
+
# A floor, not a pin: new credentials get added over time, and pinning the count
|
|
92
|
+
# made every addition a false failure. What matters is that the keys shipped
|
|
93
|
+
# flows resolve are all declared.
|
|
91
94
|
KM_COUNT=$(node -e "
|
|
92
95
|
const s = JSON.parse(require('fs').readFileSync('$PREFS_SCHEMA', 'utf8'));
|
|
93
96
|
const km = s.properties.global.properties.keychainMapping.properties;
|
|
94
97
|
console.log(Object.keys(km).length);
|
|
95
98
|
" 2>/dev/null)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
pass "keychainMapping has $KM_COUNT services (≥12 expected)"
|
|
99
|
+
if [ "${KM_COUNT:-0}" -ge 12 ] 2>/dev/null; then
|
|
100
|
+
pass "keychainMapping declares $KM_COUNT services (≥12 expected)"
|
|
99
101
|
else
|
|
100
|
-
fail "keychainMapping
|
|
102
|
+
fail "keychainMapping declares ${KM_COUNT:-0} services, expected ≥12"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
MISSING_KEYS=$(node -e "
|
|
106
|
+
const s = JSON.parse(require('fs').readFileSync('$PREFS_SCHEMA', 'utf8'));
|
|
107
|
+
const km = Object.keys(s.properties.global.properties.keychainMapping.properties);
|
|
108
|
+
const required = ['jira','bitbucket_token','bitbucket_user','github','confluence','figma','figma_pat','figma_mcp','firebase','npm'];
|
|
109
|
+
console.log(required.filter((k) => !km.includes(k)).join(','));
|
|
110
|
+
" 2>/dev/null)
|
|
111
|
+
if [ -z "$MISSING_KEYS" ]; then
|
|
112
|
+
pass "every credential the pipeline resolves is declared"
|
|
113
|
+
else
|
|
114
|
+
fail "keychainMapping missing declared-and-used key(s): $MISSING_KEYS"
|
|
101
115
|
fi
|
|
102
116
|
|
|
103
117
|
# ──────────────────────────────────────────────────────────────────────────
|
|
@@ -59,7 +59,9 @@ fi
|
|
|
59
59
|
|
|
60
60
|
# 4-9. Lifecycle on a temp project
|
|
61
61
|
TMP=$(mktemp -d)
|
|
62
|
-
cd
|
|
62
|
+
# A failed cd here would run the relative `rm -rf DerivedData` below in the
|
|
63
|
+
# caller's directory instead of the sandbox.
|
|
64
|
+
cd "$TMP" || { record_fail "cannot cd into sandbox $TMP"; exit 1; }
|
|
63
65
|
git init --quiet
|
|
64
66
|
echo "node_modules/" > .gitignore
|
|
65
67
|
echo "hello" > a.txt
|
|
@@ -178,7 +180,7 @@ else
|
|
|
178
180
|
record_fail "~/.claude/state is GONE after unsafe prune attempts"
|
|
179
181
|
fi
|
|
180
182
|
|
|
181
|
-
cd - >/dev/null
|
|
183
|
+
cd - >/dev/null || true
|
|
182
184
|
rm -rf "$TMP"
|
|
183
185
|
|
|
184
186
|
# 10-12. Prefs schema
|
|
@@ -63,9 +63,27 @@ function die(msg, code = 1) {
|
|
|
63
63
|
process.exit(code);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Resolves to the MAIN repository name even when called from inside a pipeline
|
|
67
|
+
// worktree. `--show-toplevel` returns the worktree root, whose basename is the
|
|
68
|
+
// task id, which would shard the per-repo store one directory per task and make
|
|
69
|
+
// prior-art recall always miss. `--git-common-dir` points at the main
|
|
70
|
+
// repository's .git for every linked worktree.
|
|
66
71
|
function repoSlug() {
|
|
67
72
|
if (flags["repo-slug"]) return String(flags["repo-slug"]);
|
|
68
73
|
if (process.env.MULTI_AGENT_REPO_SLUG) return process.env.MULTI_AGENT_REPO_SLUG;
|
|
74
|
+
try {
|
|
75
|
+
const commonDir = execFileSync(
|
|
76
|
+
"git",
|
|
77
|
+
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
78
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
79
|
+
).trim();
|
|
80
|
+
if (commonDir) {
|
|
81
|
+
const base = basename(commonDir);
|
|
82
|
+
const root = base === ".git" ? dirname(commonDir) : commonDir.replace(/\.git$/, "");
|
|
83
|
+
const slug = basename(root);
|
|
84
|
+
if (slug) return slug;
|
|
85
|
+
}
|
|
86
|
+
} catch { /* not a git repo, or git too old for --path-format */ }
|
|
69
87
|
try {
|
|
70
88
|
const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
71
89
|
encoding: "utf8",
|
|
@@ -84,6 +102,33 @@ function ensureDir(p) {
|
|
|
84
102
|
mkdirSync(dirname(p), { recursive: true });
|
|
85
103
|
}
|
|
86
104
|
|
|
105
|
+
// Freshness gate (P1): stamp each finding with the git blob SHA of its file at
|
|
106
|
+
// ingest time; on query, if the file's current content hash differs, the hit is
|
|
107
|
+
// flagged stale so a reused finding is verified, not trusted blindly.
|
|
108
|
+
function repoTop() {
|
|
109
|
+
try {
|
|
110
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
111
|
+
encoding: "utf8",
|
|
112
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
113
|
+
}).trim() || null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function fileSha(file, top) {
|
|
120
|
+
if (!file || !top) return null;
|
|
121
|
+
try {
|
|
122
|
+
return execFileSync("git", ["hash-object", "--", file], {
|
|
123
|
+
cwd: top,
|
|
124
|
+
encoding: "utf8",
|
|
125
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
126
|
+
}).trim() || null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
87
132
|
const STOP = new Set([
|
|
88
133
|
"the","a","an","and","or","but","of","in","on","at","to","for","with","by","is","are",
|
|
89
134
|
"be","been","being","this","that","these","those","it","its","as","not","no","do","does",
|
|
@@ -157,6 +202,7 @@ function ingest() {
|
|
|
157
202
|
catch (e) { die(`cannot parse triage JSON: ${e.message}`, 1); }
|
|
158
203
|
|
|
159
204
|
const ts = new Date().toISOString();
|
|
205
|
+
const top = repoTop();
|
|
160
206
|
const corpus = readCorpus(slug);
|
|
161
207
|
const seen = new Set(corpus.map((r) => `${r.task_id}::${r.classification}::${r.file}::${r.issue}`));
|
|
162
208
|
|
|
@@ -188,6 +234,7 @@ function ingest() {
|
|
|
188
234
|
fix: f.fix || null,
|
|
189
235
|
reason: f._reason || null,
|
|
190
236
|
reviewer: f.reviewer || null,
|
|
237
|
+
file_sha: fileSha(file, top),
|
|
191
238
|
};
|
|
192
239
|
writeRow(slug, row);
|
|
193
240
|
seen.add(key);
|
|
@@ -212,21 +259,30 @@ function query() {
|
|
|
212
259
|
const qTokens = tokenize(issueText);
|
|
213
260
|
const fileRe = fileGlob ? globToRegExp(fileGlob) : null;
|
|
214
261
|
const filtered = fileRe ? corpus.filter((r) => fileRe.test(r.file || "")) : corpus.slice();
|
|
262
|
+
const top = repoTop();
|
|
215
263
|
const ranked = filtered
|
|
216
264
|
.map((r) => ({ row: r, score: score(qTokens, r) }))
|
|
217
265
|
.filter((x) => x.score > 0 || qTokens.size === 0)
|
|
218
266
|
.sort((a, b) => b.score - a.score)
|
|
219
267
|
.slice(0, topN)
|
|
220
|
-
.map(({ row, score }) =>
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
268
|
+
.map(({ row, score }) => {
|
|
269
|
+
// Freshness: null when we cannot compare (no stored sha, or file gone /
|
|
270
|
+
// not in a git repo); true means the file changed since the finding was
|
|
271
|
+
// recorded, so verify before reusing.
|
|
272
|
+
const cur = row.file_sha ? fileSha(row.file, top) : null;
|
|
273
|
+
const stale = row.file_sha && cur ? cur !== row.file_sha : null;
|
|
274
|
+
return {
|
|
275
|
+
taskId: row.task_id,
|
|
276
|
+
classification: row.classification,
|
|
277
|
+
severity: row.severity,
|
|
278
|
+
file: row.file,
|
|
279
|
+
line: row.line,
|
|
280
|
+
issue: row.issue,
|
|
281
|
+
reason: row.reason,
|
|
282
|
+
stale,
|
|
283
|
+
score: Math.round(score * 1000) / 1000,
|
|
284
|
+
};
|
|
285
|
+
});
|
|
230
286
|
process.stdout.write(JSON.stringify({ ok: true, slug, total: filtered.length, hits: ranked }) + "\n");
|
|
231
287
|
process.exit(0);
|
|
232
288
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-07-
|
|
4
|
-
"skillCount":
|
|
3
|
+
"generatedAt": "2026-07-25T10:41:38Z",
|
|
4
|
+
"skillCount": 193,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"path": "shared/core/apple-archive-compliance/SKILL.md",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "d3826905606b6ec7d86d3434b03bfdbbd52a424f7108e58ca12a8cb490f1e4ad"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "shared/core/google-play-compliance/SKILL.md",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"path": "shared/core/multi-agent-create-jira/SKILL.md",
|
|
36
36
|
"sha256": "40caf0489ad8322cd00d792a244e772d4ddb6ccfd7c3807729d6b17590e7f81c"
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
"path": "shared/core/multi-agent-design-check/SKILL.md",
|
|
40
|
+
"sha256": "a004cf24661789d2a3595c5246dfebe00b76aaee6ece057b458748f502a8cd5c"
|
|
41
|
+
},
|
|
38
42
|
{
|
|
39
43
|
"path": "shared/core/multi-agent-dev-autopilot/SKILL.md",
|
|
40
44
|
"sha256": "82b1cf4a4bbd84fcfef54fcdbcdefa27f2747df6d4fac5c86b42e861013c6d58"
|
|
@@ -59,13 +63,17 @@
|
|
|
59
63
|
"path": "shared/core/multi-agent-finish/SKILL.md",
|
|
60
64
|
"sha256": "3e8e430f7aee626be96129a9d0ae6c9ac5ef0dbf80cbe580dde2d7fb5fccfed1"
|
|
61
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"path": "shared/core/multi-agent-forget/SKILL.md",
|
|
68
|
+
"sha256": "68f6a92a00844ed34c336548888d3e623534c12b5e85afe5762e7a9c8d41aae4"
|
|
69
|
+
},
|
|
62
70
|
{
|
|
63
71
|
"path": "shared/core/multi-agent-garbage-collect/SKILL.md",
|
|
64
72
|
"sha256": "24736f163e645e2bee7d552e555ae6e32cd3fc3d15c844813a75b908a58d52fd"
|
|
65
73
|
},
|
|
66
74
|
{
|
|
67
75
|
"path": "shared/core/multi-agent-help/SKILL.md",
|
|
68
|
-
"sha256": "
|
|
76
|
+
"sha256": "330780854ab3b48f788e34f918ecad585bd46ffbb44f14fd38de7b48a4b568aa"
|
|
69
77
|
},
|
|
70
78
|
{
|
|
71
79
|
"path": "shared/core/multi-agent-issue/SKILL.md",
|
|
@@ -109,7 +117,7 @@
|
|
|
109
117
|
},
|
|
110
118
|
{
|
|
111
119
|
"path": "shared/core/multi-agent-refactor/SKILL.md",
|
|
112
|
-
"sha256": "
|
|
120
|
+
"sha256": "60766ee3a6b974b3872417db5c40d1fc03bfddd99daf4aca11d7a59b57a41b57"
|
|
113
121
|
},
|
|
114
122
|
{
|
|
115
123
|
"path": "shared/core/multi-agent-resume/SKILL.md",
|
|
@@ -127,6 +135,14 @@
|
|
|
127
135
|
"path": "shared/core/multi-agent-review/SKILL.md",
|
|
128
136
|
"sha256": "b1fd57d2b80ee0fe2490439d9f2688fcf415857c2aab60549c5456e27693b9a9"
|
|
129
137
|
},
|
|
138
|
+
{
|
|
139
|
+
"path": "shared/core/multi-agent-routines/SKILL.md",
|
|
140
|
+
"sha256": "91d3bdb15da7addadd1afd0b170057daf3d3fc335da3f6e1feb4feff1af0881e"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"path": "shared/core/multi-agent-save/SKILL.md",
|
|
144
|
+
"sha256": "f9c6e2ba1dd1b224b80cabe35f4327b5bcc0b1e05868b378547bec616193ef1b"
|
|
145
|
+
},
|
|
130
146
|
{
|
|
131
147
|
"path": "shared/core/multi-agent-scan/SKILL.md",
|
|
132
148
|
"sha256": "266e4e7c9b702bc3205b7d926455c5863e144cddb1e4b883aa81e570a362fd3f"
|
|
@@ -149,11 +165,11 @@
|
|
|
149
165
|
},
|
|
150
166
|
{
|
|
151
167
|
"path": "shared/core/multi-agent-sync/SKILL.md",
|
|
152
|
-
"sha256": "
|
|
168
|
+
"sha256": "3a9f7d8caa47d41d1a4759ce6673bfb94a1ef9f62f7113a4cd3b1e9451464601"
|
|
153
169
|
},
|
|
154
170
|
{
|
|
155
171
|
"path": "shared/core/multi-agent-test/SKILL.md",
|
|
156
|
-
"sha256": "
|
|
172
|
+
"sha256": "2b662ecd9497149c4c2e87cc79e906cc1cab7ea4a7d0efe300b0b018559960c4"
|
|
157
173
|
},
|
|
158
174
|
{
|
|
159
175
|
"path": "shared/core/multi-agent-uninstall/SKILL.md",
|
|
@@ -425,7 +441,7 @@
|
|
|
425
441
|
},
|
|
426
442
|
{
|
|
427
443
|
"path": "shared/external/humanizer/SKILL.md",
|
|
428
|
-
"sha256": "
|
|
444
|
+
"sha256": "2fc8fe79a5832b9e1316f515c73f0545023153e85aba4645ba42060a0afdd049"
|
|
429
445
|
},
|
|
430
446
|
{
|
|
431
447
|
"path": "shared/external/ios-accessibility/SKILL.md",
|