@event4u/agent-config 2.18.0 → 2.20.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/.agent-src/commands/agent-status.md +29 -0
- package/.agent-src/commands/onboard.md +221 -81
- package/.agent-src/commands/refine-ticket.md +3 -0
- package/.agent-src/packs/README.md +49 -0
- package/.agent-src/packs/agency-delivery.yml +63 -0
- package/.agent-src/packs/content-engine.yml +53 -0
- package/.agent-src/packs/founder-mvp.yml +51 -0
- package/.agent-src/personas/README.md +8 -0
- package/.agent-src/presets/README.md +26 -0
- package/.agent-src/presets/balanced.yml +34 -0
- package/.agent-src/presets/fast.yml +31 -0
- package/.agent-src/presets/strict.yml +38 -0
- package/.agent-src/profiles/README.md +29 -0
- package/.agent-src/profiles/agency.yml +27 -0
- package/.agent-src/profiles/content_creator.yml +25 -0
- package/.agent-src/profiles/developer.yml +26 -0
- package/.agent-src/profiles/finance.yml +24 -0
- package/.agent-src/profiles/founder.yml +25 -0
- package/.agent-src/profiles/ops.yml +25 -0
- package/.agent-src/rules/no-cheap-questions.md +25 -17
- package/.agent-src/skills/adr-create/SKILL.md +78 -68
- package/.agent-src/skills/refine-ticket/SKILL.md +3 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +33 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.agent-src/templates/skill-archive-note.md +101 -0
- package/.agent-src/user-types/README.md +124 -0
- package/.agent-src/user-types/_template/user-type.md +95 -0
- package/.agent-src/user-types/galabau-field-crew.md +100 -0
- package/.agent-src/user-types/metalworking-shop.md +105 -0
- package/.agent-src/user-types/truck-driver.md +113 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +91 -30
- package/README.md +68 -72
- package/config/agent-settings.template.yml +22 -0
- package/docs/adrs/caveman/0001-default-off-until-bench.md +93 -0
- package/docs/adrs/caveman/README.md +9 -0
- package/docs/adrs/cost/0001-hard-stop-hook.md +114 -0
- package/docs/adrs/cost/README.md +9 -0
- package/docs/adrs/memory/0001-consumer-side-snapshot.md +111 -0
- package/docs/adrs/memory/README.md +9 -0
- package/docs/adrs/router/0001-three-tier-routing.md +119 -0
- package/docs/adrs/router/README.md +9 -0
- package/docs/adrs/schema/0001-json-schema-frontmatter.md +102 -0
- package/docs/adrs/schema/README.md +9 -0
- package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +99 -0
- package/docs/adrs/smoke/README.md +9 -0
- package/docs/architecture/current-onboard-baseline.md +126 -0
- package/docs/architecture/current-safety-behavior.md +137 -0
- package/docs/archive/CHANGELOG-pre-2.16.0.md +48 -0
- package/docs/contracts/adr-layout.md +108 -0
- package/docs/contracts/adr-mcp-runtime.md +128 -0
- package/docs/contracts/adr-user-types-axis.md +127 -0
- package/docs/contracts/benchmark-corpus-spec.md +97 -0
- package/docs/contracts/benchmark-report-schema.md +111 -0
- package/docs/contracts/command-clusters.md +1 -0
- package/docs/contracts/command-taxonomy.md +137 -0
- package/docs/contracts/compression-default-kill-criterion.md +69 -0
- package/docs/contracts/config-presets.md +144 -0
- package/docs/contracts/cost-dashboard.md +143 -0
- package/docs/contracts/cost-enforcement.md +134 -0
- package/docs/contracts/file-ownership-matrix.json +0 -7
- package/docs/contracts/mcp-tool-inventory.md +53 -0
- package/docs/contracts/measurement-baseline.md +102 -0
- package/docs/contracts/namespace.md +125 -0
- package/docs/contracts/profile-system.md +142 -0
- package/docs/contracts/safety-model.md +129 -0
- package/docs/contracts/smoke-contracts.md +144 -0
- package/docs/contracts/user-type-schema.md +146 -0
- package/docs/contracts/workflow-packs.md +121 -0
- package/docs/decisions/ADR-010-profile-pack-preset-boundary.md +132 -0
- package/docs/decisions/INDEX.md +1 -0
- package/docs/featured-commands.md +27 -0
- package/docs/parity/bench-ruflo.json +58 -0
- package/docs/parity/bench.json +41 -0
- package/docs/parity/ruflo.md +46 -0
- package/docs/profiles.md +91 -0
- package/docs/recruits/_template.md +81 -0
- package/package.json +1 -1
- package/scripts/_cli/cmd_explain.py +250 -0
- package/scripts/_lib/bench_cost.py +138 -0
- package/scripts/_lib/bench_quality.py +118 -0
- package/scripts/_lib/bench_report.py +150 -0
- package/scripts/agent-config +13 -0
- package/scripts/audit_adr_coverage.py +175 -0
- package/scripts/audit_mcp_tools.py +146 -0
- package/scripts/bench_baseline_ready.py +108 -0
- package/scripts/bench_drift_check.py +151 -0
- package/scripts/bench_per_tool.py +216 -0
- package/scripts/bench_run.py +155 -0
- package/scripts/compress.py +48 -2
- package/scripts/config/__init__.py +9 -0
- package/scripts/config/presets.py +206 -0
- package/scripts/config/profiles.py +173 -0
- package/scripts/cost/budget.mjs +73 -12
- package/scripts/cost/preflight.mjs +89 -0
- package/scripts/lint_archived_skills.py +143 -0
- package/scripts/lint_bench_corpus.py +161 -0
- package/scripts/lint_namespace.py +135 -0
- package/scripts/schemas/user-type.schema.json +35 -0
- package/scripts/skill_linter.py +139 -4
- package/scripts/skill_overlap.py +204 -0
- package/scripts/skill_tools/audit_user_type_coverage.py +148 -0
- package/scripts/skill_usage_collect.py +191 -0
- package/scripts/skill_usage_report.py +162 -0
- package/scripts/smoke/kernel.sh +101 -0
- package/scripts/smoke/router.sh +129 -0
- package/scripts/smoke/schema.sh +71 -0
- package/scripts/smoke/skills.sh +101 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/smoke/router.sh — router-tier smoke (step-11 Phase 3 Step 3).
|
|
3
|
+
#
|
|
4
|
+
# Asserts router.json structural integrity:
|
|
5
|
+
# 1. 75 ids = 9 kernel + 24 tier_1 + 42 tier_2 (locked count).
|
|
6
|
+
# 2. Every id resolves to .agent-src/rules/<id>.md (0 broken).
|
|
7
|
+
# 3. Every routes_to ref resolves through its prefix
|
|
8
|
+
# (skill:, command:, guideline:, contract:); missing-contract
|
|
9
|
+
# count locked at ≤ EXPECTED_MISSING_CONTRACTS.
|
|
10
|
+
#
|
|
11
|
+
# Runtime ceiling: 30 s.
|
|
12
|
+
# Output: table by default, baseline line on stdout last; SMOKE_QUIET=1
|
|
13
|
+
# suppresses the table.
|
|
14
|
+
# Contract: docs/contracts/smoke-contracts.md § 3.2
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
EXPECTED_TOTAL_IDS=75
|
|
19
|
+
EXPECTED_MISSING_CONTRACTS=2
|
|
20
|
+
|
|
21
|
+
quiet="${SMOKE_QUIET:-0}"
|
|
22
|
+
log() { [ "$quiet" = "1" ] || printf '%s\n' "$*"; }
|
|
23
|
+
|
|
24
|
+
result=$(python3 <<'PY'
|
|
25
|
+
import json, os, sys, pathlib
|
|
26
|
+
|
|
27
|
+
d = json.load(open("router.json"))
|
|
28
|
+
kernel = d.get("kernel", [])
|
|
29
|
+
tier1 = d.get("tier_1", [])
|
|
30
|
+
tier2 = d.get("tier_2", [])
|
|
31
|
+
ids = list(kernel) + [r["id"] for r in tier1] + [r["id"] for r in tier2]
|
|
32
|
+
total = len(ids)
|
|
33
|
+
|
|
34
|
+
# Rule-file resolution
|
|
35
|
+
missing_rules = [i for i in ids if not os.path.exists(f".agent-src/rules/{i}.md")]
|
|
36
|
+
|
|
37
|
+
# routes_to resolution
|
|
38
|
+
def resolve(ref):
|
|
39
|
+
if ":" not in ref:
|
|
40
|
+
return f".agent-src.uncompressed/skills/{ref}/SKILL.md", "skill"
|
|
41
|
+
kind, rest = ref.split(":", 1)
|
|
42
|
+
if kind == "skill":
|
|
43
|
+
return f".agent-src.uncompressed/skills/{rest}/SKILL.md", "skill"
|
|
44
|
+
if kind == "command":
|
|
45
|
+
for p in (
|
|
46
|
+
f".agent-src.uncompressed/commands/{rest}.md",
|
|
47
|
+
f".agent-src.uncompressed/commands/{rest}/INDEX.md",
|
|
48
|
+
):
|
|
49
|
+
if os.path.exists(p):
|
|
50
|
+
return p, "command"
|
|
51
|
+
return f".agent-src.uncompressed/commands/{rest}.md", "command"
|
|
52
|
+
if kind == "guideline":
|
|
53
|
+
return f"docs/guidelines/{rest}.md", "guideline"
|
|
54
|
+
if kind == "contract":
|
|
55
|
+
return f"docs/contracts/{rest}.md", "contract"
|
|
56
|
+
return None, kind
|
|
57
|
+
|
|
58
|
+
refs = set()
|
|
59
|
+
for r in tier1 + tier2:
|
|
60
|
+
for ref in r.get("routes_to", []):
|
|
61
|
+
refs.add(ref)
|
|
62
|
+
|
|
63
|
+
missing_by_kind = {"skill": [], "command": [], "guideline": [], "contract": []}
|
|
64
|
+
for ref in refs:
|
|
65
|
+
path, kind = resolve(ref)
|
|
66
|
+
if path is None or not os.path.exists(path):
|
|
67
|
+
missing_by_kind.setdefault(kind, []).append(ref)
|
|
68
|
+
|
|
69
|
+
print(f"TOTAL_IDS={total}")
|
|
70
|
+
print(f"KERNEL={len(kernel)}")
|
|
71
|
+
print(f"TIER1={len(tier1)}")
|
|
72
|
+
print(f"TIER2={len(tier2)}")
|
|
73
|
+
print(f"MISSING_RULES={len(missing_rules)}")
|
|
74
|
+
print(f"ROUTES_TO_REFS={len(refs)}")
|
|
75
|
+
for kind, items in missing_by_kind.items():
|
|
76
|
+
print(f"MISSING_{kind.upper()}={len(items)}")
|
|
77
|
+
for r in items:
|
|
78
|
+
print(f" - {kind}: {r}")
|
|
79
|
+
PY
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Parse out the counters
|
|
83
|
+
TOTAL_IDS=$(echo "$result" | grep '^TOTAL_IDS=' | cut -d= -f2)
|
|
84
|
+
KERNEL=$(echo "$result" | grep '^KERNEL=' | cut -d= -f2)
|
|
85
|
+
TIER1=$(echo "$result" | grep '^TIER1=' | cut -d= -f2)
|
|
86
|
+
TIER2=$(echo "$result" | grep '^TIER2=' | cut -d= -f2)
|
|
87
|
+
MISSING_RULES=$(echo "$result" | grep '^MISSING_RULES=' | cut -d= -f2)
|
|
88
|
+
ROUTES_TO_REFS=$(echo "$result" | grep '^ROUTES_TO_REFS=' | cut -d= -f2)
|
|
89
|
+
MISSING_SKILL=$(echo "$result" | grep '^MISSING_SKILL=' | cut -d= -f2)
|
|
90
|
+
MISSING_COMMAND=$(echo "$result" | grep '^MISSING_COMMAND=' | cut -d= -f2)
|
|
91
|
+
MISSING_GUIDELINE=$(echo "$result" | grep '^MISSING_GUIDELINE=' | cut -d= -f2)
|
|
92
|
+
MISSING_CONTRACT=$(echo "$result" | grep '^MISSING_CONTRACT=' | cut -d= -f2)
|
|
93
|
+
|
|
94
|
+
log "## Router smoke"
|
|
95
|
+
log ""
|
|
96
|
+
log "| Check | Value |"
|
|
97
|
+
log "|---|---:|"
|
|
98
|
+
log "| Total router ids | $TOTAL_IDS (kernel $KERNEL · tier_1 $TIER1 · tier_2 $TIER2) |"
|
|
99
|
+
log "| Broken rule pointers | $MISSING_RULES |"
|
|
100
|
+
log "| routes_to refs | $ROUTES_TO_REFS |"
|
|
101
|
+
log "| missing skill targets | $MISSING_SKILL |"
|
|
102
|
+
log "| missing command targets | $MISSING_COMMAND |"
|
|
103
|
+
log "| missing guideline targets | $MISSING_GUIDELINE |"
|
|
104
|
+
log "| missing contract targets | $MISSING_CONTRACT (locked ≤ $EXPECTED_MISSING_CONTRACTS) |"
|
|
105
|
+
|
|
106
|
+
fail=0
|
|
107
|
+
if [ "$TOTAL_IDS" -ne "$EXPECTED_TOTAL_IDS" ]; then
|
|
108
|
+
echo "ℹ️ router id count drifted: $TOTAL_IDS (was $EXPECTED_TOTAL_IDS)"
|
|
109
|
+
fi
|
|
110
|
+
if [ "$MISSING_RULES" -gt 0 ]; then
|
|
111
|
+
echo "❌ broken rule pointers: $MISSING_RULES"
|
|
112
|
+
echo "$result" | grep '^ - skill:\|^ - guideline:' || true
|
|
113
|
+
fail=1
|
|
114
|
+
fi
|
|
115
|
+
if [ "$MISSING_SKILL" -gt 0 ] || [ "$MISSING_COMMAND" -gt 0 ] || [ "$MISSING_GUIDELINE" -gt 0 ]; then
|
|
116
|
+
echo "❌ broken routes_to targets:"
|
|
117
|
+
echo "$result" | grep -E '^ - (skill|command|guideline):' || true
|
|
118
|
+
fail=1
|
|
119
|
+
fi
|
|
120
|
+
if [ "$MISSING_CONTRACT" -gt "$EXPECTED_MISSING_CONTRACTS" ]; then
|
|
121
|
+
echo "❌ missing contracts: $MISSING_CONTRACT > $EXPECTED_MISSING_CONTRACTS (regression)"
|
|
122
|
+
echo "$result" | grep '^ - contract:' || true
|
|
123
|
+
fail=1
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
log ""
|
|
127
|
+
echo "BASELINE: $TOTAL_IDS router ids · $MISSING_RULES broken rule pointers · $ROUTES_TO_REFS routes_to refs · $MISSING_CONTRACT missing contracts"
|
|
128
|
+
|
|
129
|
+
exit $fail
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/smoke/schema.sh — schema-tier smoke (step-11 Phase 3 Step 4).
|
|
3
|
+
#
|
|
4
|
+
# Runs scripts/skill_linter.py --all over every lintable artefact and
|
|
5
|
+
# asserts:
|
|
6
|
+
# 1. 0 schema FAILs (hard).
|
|
7
|
+
# 2. Warns ≤ EXPECTED_WARNS (regression lock).
|
|
8
|
+
# 3. Total ≥ EXPECTED_MIN_TOTAL (catches accidental skill deletion).
|
|
9
|
+
#
|
|
10
|
+
# v2 schema (step-5) fields are deferred — when step-5 Phase 1 closes,
|
|
11
|
+
# this smoke gains a `model_tier` presence check; Phase 3 adds
|
|
12
|
+
# `schema_version: "2"`. See docs/contracts/smoke-contracts.md § 3.3.
|
|
13
|
+
#
|
|
14
|
+
# Runtime ceiling: 30 s.
|
|
15
|
+
# Output: table by default, baseline line on stdout last; SMOKE_QUIET=1
|
|
16
|
+
# suppresses the table.
|
|
17
|
+
# Contract: docs/contracts/smoke-contracts.md § 3.3
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
EXPECTED_WARNS=92
|
|
22
|
+
EXPECTED_MIN_TOTAL=438
|
|
23
|
+
|
|
24
|
+
quiet="${SMOKE_QUIET:-0}"
|
|
25
|
+
log() { [ "$quiet" = "1" ] || printf '%s\n' "$*"; }
|
|
26
|
+
|
|
27
|
+
# Run the linter and capture summary
|
|
28
|
+
out=$(python3 scripts/skill_linter.py --all --quiet 2>&1 || true)
|
|
29
|
+
summary=$(printf '%s\n' "$out" | grep -E '^Summary: ' | tail -1)
|
|
30
|
+
|
|
31
|
+
if [ -z "$summary" ]; then
|
|
32
|
+
echo "❌ skill_linter.py produced no summary line"
|
|
33
|
+
printf '%s\n' "$out" | tail -5
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Parse: "Summary: 346 pass, 92 warn, 0 fail, 438 total"
|
|
38
|
+
pass=$(echo "$summary" | sed -E 's/.*Summary: ([0-9]+) pass.*/\1/')
|
|
39
|
+
warn=$(echo "$summary" | sed -E 's/.*, ([0-9]+) warn.*/\1/')
|
|
40
|
+
fail=$(echo "$summary" | sed -E 's/.*, ([0-9]+) fail.*/\1/')
|
|
41
|
+
total=$(echo "$summary" | sed -E 's/.*, ([0-9]+) total.*/\1/')
|
|
42
|
+
|
|
43
|
+
log "## Schema smoke"
|
|
44
|
+
log ""
|
|
45
|
+
log "| Check | Value |"
|
|
46
|
+
log "|---|---:|"
|
|
47
|
+
log "| Total artefacts | $total (≥ $EXPECTED_MIN_TOTAL) |"
|
|
48
|
+
log "| Pass | $pass |"
|
|
49
|
+
log "| Warn | $warn (locked ≤ $EXPECTED_WARNS) |"
|
|
50
|
+
log "| Fail | $fail (hard 0) |"
|
|
51
|
+
log "| v2 schema enforcement | deferred (see step-5-schema-rigor.md) |"
|
|
52
|
+
|
|
53
|
+
exit_code=0
|
|
54
|
+
if [ "$fail" -gt 0 ]; then
|
|
55
|
+
echo "❌ schema FAILs: $fail (must be 0)"
|
|
56
|
+
printf '%s\n' "$out" | grep -E '^\[FAIL\]' | head -10 || true
|
|
57
|
+
exit_code=1
|
|
58
|
+
fi
|
|
59
|
+
if [ "$warn" -gt "$EXPECTED_WARNS" ]; then
|
|
60
|
+
echo "❌ schema warns: $warn > $EXPECTED_WARNS (regression)"
|
|
61
|
+
exit_code=1
|
|
62
|
+
fi
|
|
63
|
+
if [ "$total" -lt "$EXPECTED_MIN_TOTAL" ]; then
|
|
64
|
+
echo "❌ artefact total $total < $EXPECTED_MIN_TOTAL (unexpected deletion?)"
|
|
65
|
+
exit_code=1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
log ""
|
|
69
|
+
echo "BASELINE: $total lintable artefacts · $fail schema FAIL(s) · $warn warn(s)"
|
|
70
|
+
|
|
71
|
+
exit $exit_code
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/smoke/skills.sh — skills-tier smoke (step-11 Phase 3 Step 5).
|
|
3
|
+
#
|
|
4
|
+
# Picks 5 random skills (deterministic seed = epoch day) from
|
|
5
|
+
# .agent-src.uncompressed/skills/*/SKILL.md and asserts:
|
|
6
|
+
# 1. SKILL.md exists.
|
|
7
|
+
# 2. Frontmatter parses + validates against scripts/schemas/skill.schema.json.
|
|
8
|
+
# 3. `name:` field matches the parent directory name.
|
|
9
|
+
# 4. Total skill count ≥ EXPECTED_MIN_SKILLS (regression lock against
|
|
10
|
+
# accidental directory removal).
|
|
11
|
+
#
|
|
12
|
+
# Runtime ceiling: 30 s.
|
|
13
|
+
# Output: table by default, baseline line on stdout last; SMOKE_QUIET=1
|
|
14
|
+
# suppresses the table.
|
|
15
|
+
# Contract: docs/contracts/smoke-contracts.md § 3.4
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
EXPECTED_MIN_SKILLS=205
|
|
20
|
+
SAMPLE_SIZE=5
|
|
21
|
+
|
|
22
|
+
quiet="${SMOKE_QUIET:-0}"
|
|
23
|
+
log() { [ "$quiet" = "1" ] || printf '%s\n' "$*"; }
|
|
24
|
+
|
|
25
|
+
result=$(python3 <<'PY'
|
|
26
|
+
import os, sys, time, hashlib, pathlib, glob
|
|
27
|
+
sys.path.insert(0, "scripts")
|
|
28
|
+
from validate_frontmatter import parse_frontmatter, load_schema, validate
|
|
29
|
+
|
|
30
|
+
root = ".agent-src.uncompressed/skills"
|
|
31
|
+
skills = sorted(
|
|
32
|
+
d for d in os.listdir(root)
|
|
33
|
+
if os.path.isdir(os.path.join(root, d))
|
|
34
|
+
and os.path.exists(os.path.join(root, d, "SKILL.md"))
|
|
35
|
+
)
|
|
36
|
+
total = len(skills)
|
|
37
|
+
print(f"TOTAL_SKILLS={total}")
|
|
38
|
+
|
|
39
|
+
# Deterministic seed = epoch day → same sample within 24h, drifts daily.
|
|
40
|
+
seed = int(time.time() // 86400)
|
|
41
|
+
import random
|
|
42
|
+
rng = random.Random(seed)
|
|
43
|
+
sample = rng.sample(skills, min(5, total))
|
|
44
|
+
|
|
45
|
+
schema = load_schema("skill")
|
|
46
|
+
|
|
47
|
+
failures = []
|
|
48
|
+
for name in sample:
|
|
49
|
+
path = os.path.join(root, name, "SKILL.md")
|
|
50
|
+
text = open(path, encoding="utf-8").read()
|
|
51
|
+
fm, _ = parse_frontmatter(text)
|
|
52
|
+
if fm is None:
|
|
53
|
+
failures.append(f"{name}: no frontmatter")
|
|
54
|
+
continue
|
|
55
|
+
errs = validate(fm, schema)
|
|
56
|
+
if errs:
|
|
57
|
+
for e in errs:
|
|
58
|
+
failures.append(f"{name}: {e.format()}")
|
|
59
|
+
continue
|
|
60
|
+
declared = fm.get("name")
|
|
61
|
+
if declared != name:
|
|
62
|
+
failures.append(f"{name}: name field='{declared}' ≠ directory='{name}'")
|
|
63
|
+
|
|
64
|
+
print(f"SAMPLE={','.join(sample)}")
|
|
65
|
+
print(f"SAMPLE_PASS={len(sample) - len([f for f in failures])}")
|
|
66
|
+
print(f"SAMPLE_TOTAL={len(sample)}")
|
|
67
|
+
for f in failures:
|
|
68
|
+
print(f" FAIL: {f}")
|
|
69
|
+
PY
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
TOTAL_SKILLS=$(echo "$result" | grep '^TOTAL_SKILLS=' | cut -d= -f2)
|
|
73
|
+
SAMPLE=$(echo "$result" | grep '^SAMPLE=' | cut -d= -f2)
|
|
74
|
+
SAMPLE_PASS=$(echo "$result" | grep '^SAMPLE_PASS=' | cut -d= -f2)
|
|
75
|
+
SAMPLE_TOTAL=$(echo "$result" | grep '^SAMPLE_TOTAL=' | cut -d= -f2)
|
|
76
|
+
FAILS=$(echo "$result" | grep -c '^ FAIL:' || true)
|
|
77
|
+
|
|
78
|
+
log "## Skills smoke"
|
|
79
|
+
log ""
|
|
80
|
+
log "| Check | Value |"
|
|
81
|
+
log "|---|---:|"
|
|
82
|
+
log "| Total skills | $TOTAL_SKILLS (≥ $EXPECTED_MIN_SKILLS) |"
|
|
83
|
+
log "| Sample size | $SAMPLE_TOTAL |"
|
|
84
|
+
log "| Sample (epoch-day seed) | $SAMPLE |"
|
|
85
|
+
log "| Sample pass | $SAMPLE_PASS/$SAMPLE_TOTAL |"
|
|
86
|
+
|
|
87
|
+
exit_code=0
|
|
88
|
+
if [ "$TOTAL_SKILLS" -lt "$EXPECTED_MIN_SKILLS" ]; then
|
|
89
|
+
echo "❌ skill count $TOTAL_SKILLS < $EXPECTED_MIN_SKILLS (unexpected deletion?)"
|
|
90
|
+
exit_code=1
|
|
91
|
+
fi
|
|
92
|
+
if [ "$FAILS" -gt 0 ]; then
|
|
93
|
+
echo "❌ sample failures:"
|
|
94
|
+
echo "$result" | grep '^ FAIL:'
|
|
95
|
+
exit_code=1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
log ""
|
|
99
|
+
echo "BASELINE: $TOTAL_SKILLS skills · $SAMPLE_PASS/$SAMPLE_TOTAL random sample passes (seed=epoch-day)"
|
|
100
|
+
|
|
101
|
+
exit $exit_code
|