skill-gate / gate (pull_request) Waiting to run
- scripts/scheduled-task.sh:daily/weekly/monthly 只做确定性动作,需模型判断的部分生成 queue 文件 - scripts/install-schedulers.sh:装成用户级 launchd(daily 07:20 / weekly 周一 10:00 / monthly 1 日 10:00) - .gitea issue/PR 模板:新技能提案走同一套 SCOPE 门槛问题 - README 一键部署 2 步 → 4 步;MAINTENANCE 增加定时任务一节与边界说明
163 lines
6.1 KiB
Bash
Executable File
163 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 技能治理定时任务入口(由 launchd 调用,见 scripts/install-schedulers.sh)
|
||
#
|
||
# 设计原则:脚本只做「确定性」的事——跑门禁、扫漂移、生成清单。
|
||
# 需要模型判断的部分(真跑 evals、人工审阅 routing)不在脚本里做,只生成 queue 文件,
|
||
# 由项目经理 Bot 在 7:30 项目日报里接手执行。这样节拍不依赖任何会话的记忆。
|
||
#
|
||
# 用法:
|
||
# ./scripts/scheduled-task.sh daily # 每天 07:20,先于 7:30 项目日报
|
||
# ./scripts/scheduled-task.sh weekly # 每周一 10:00
|
||
# ./scripts/scheduled-task.sh monthly # 每月 1 日 10:00(1/4/7/10 月附带季度清单)
|
||
set -uo pipefail
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
cd "$ROOT" || exit 1
|
||
|
||
JOB="${1:-daily}"
|
||
DAY="$(date '+%F')"
|
||
HEALTH_DIR="$ROOT/reports/skill-health"
|
||
EVALS_DIR="$ROOT/reports/evals"
|
||
MONTHLY_DIR="$ROOT/reports/monthly"
|
||
mkdir -p "$HEALTH_DIR" "$EVALS_DIR" "$MONTHLY_DIR"
|
||
|
||
log() { printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*"; }
|
||
|
||
# ---------- 公共采集:漂移 / 入库扫描 / 覆盖率 / 第三方 ----------
|
||
DRIFT_LINE=""; SCAN_LINE=""; COV_LINE=""; THIRD_LINE=""
|
||
DRIFT_N="0"; HIGH_N="0"
|
||
collect_health() {
|
||
local audit
|
||
audit="$(./install-skills.sh --audit 2>&1)"
|
||
DRIFT_LINE="$(printf '%s\n' "$audit" | grep -E '[0-9]+ 个漂移' | tail -1)"
|
||
SCAN_LINE="$(./scripts/scan-skill.sh 2>&1 | grep '扫描完成' | tail -1)"
|
||
COV_LINE="$(/usr/bin/python3 scripts/validate-skills.py --coverage 2>&1 | grep '覆盖率' | tail -1)"
|
||
THIRD_LINE="$(./scripts/scan-installed.sh 2>&1 | grep '扫描完成' | tail -1)"
|
||
DRIFT_N="$(printf '%s' "$DRIFT_LINE" | grep -oE '[0-9]+ 个漂移' | grep -oE '^[0-9]+' || true)"
|
||
HIGH_N="$(printf '%s' "$SCAN_LINE" | grep -oE '[0-9]+ 个高危' | grep -oE '^[0-9]+' || true)"
|
||
DRIFT_N="${DRIFT_N:-0}"
|
||
HIGH_N="${HIGH_N:-0}"
|
||
AUDIT_OUT="$audit"
|
||
}
|
||
|
||
# ---------- 每日:漂移 + 入库扫描,写一份可被日报直接读的健康快照 ----------
|
||
run_daily() {
|
||
local md="$HEALTH_DIR/daily-$DAY.md" todo=0
|
||
collect_health
|
||
{
|
||
echo "# 技能健康 $DAY"
|
||
echo
|
||
echo "- 漂移:${DRIFT_LINE:-未采集}"
|
||
echo "- 入库扫描:${SCAN_LINE:-未采集}"
|
||
echo "- 覆盖率:${COV_LINE:-未采集}"
|
||
echo "- 第三方技能:${THIRD_LINE:-未采集}"
|
||
echo
|
||
echo "## 需要处理"
|
||
if [ "$DRIFT_N" != "0" ]; then
|
||
echo "- 漂移 ${DRIFT_N} 个(未安装 / 版本落后),按 MAINTENANCE「每日」处理"
|
||
todo=1
|
||
fi
|
||
if [ "$HIGH_N" != "0" ]; then
|
||
echo "- 入库扫描高危 ${HIGH_N} 个,立即处理,不等日报"
|
||
todo=1
|
||
fi
|
||
[ "$todo" = "0" ] && echo "无"
|
||
echo
|
||
echo "## 原始输出(audit 尾部)"
|
||
echo '```'
|
||
printf '%s\n' "$AUDIT_OUT" | tail -15
|
||
echo '```'
|
||
} > "$md"
|
||
cp "$md" "$HEALTH_DIR/latest.md"
|
||
log "daily -> ${md#$ROOT/}"
|
||
log "漂移 ${DRIFT_N} / 高危 ${HIGH_N} / ${SCAN_LINE:-扫描未采集}"
|
||
}
|
||
|
||
# ---------- 每周:生成 evals 抽样队列(执行仍需模型,见 MAINTENANCE「每周一」)----------
|
||
run_weekly() {
|
||
local md="$EVALS_DIR/queue-$DAY.md"
|
||
local changed changed_total highrisk reports
|
||
changed_total="$(git log --since='7 days ago' --name-only --pretty=format: 2>/dev/null \
|
||
| grep -oE '^skills-[a-z]+/[^/]+-plugin/' | sort -u | wc -l | tr -d ' ')"
|
||
changed="$(git log --since='7 days ago' --name-only --pretty=format: 2>/dev/null \
|
||
| grep -oE '^skills-[a-z]+/[^/]+-plugin/' \
|
||
| sed -E 's#^skills-[a-z]+/##; s#-plugin/$##' \
|
||
| sort | uniq -c | sort -rn | head -5 | awk '{print $2}' | tr '\n' ' ')"
|
||
highrisk="dev-coding dev-review req-design"
|
||
reports="$(ls "$EVALS_DIR"/*.md 2>/dev/null | grep -vE 'TEMPLATE|queue-' | wc -l | tr -d ' ')"
|
||
{
|
||
echo "# 本周 evals 抽样队列 $DAY"
|
||
echo
|
||
echo "现有回归报告:${reports:-0} 份(目标:每周至少 +1 份)"
|
||
echo
|
||
echo "## 范围"
|
||
echo
|
||
echo "- 上周改动技能共 ${changed_total:-0} 个,按改动次数取前 5:${changed:-(无)}"
|
||
echo "- 高风险固定项:$highrisk"
|
||
echo
|
||
echo "## 做法(每个技能)"
|
||
echo
|
||
echo "1. 取 1 条关键用例,跑「有技能 / 无技能」两遍;"
|
||
echo "2. 按 rubric 打分,写增量;"
|
||
echo "3. 结果写入 \`reports/evals/$DAY-<技能名>.md\`(模板 \`reports/evals/TEMPLATE.md\`)。"
|
||
echo
|
||
echo "## 判读"
|
||
echo
|
||
echo "- 连续两次无增量 → 按 \`SCOPE.md\` 评估降级。"
|
||
echo "- 全量只在治理大改时做,不做每周全量。"
|
||
echo
|
||
echo "> 本文件由 launchd 每周一 10:00 自动生成;脚本不调模型,执行由 agent 接手。"
|
||
} > "$md"
|
||
log "weekly -> ${md#$ROOT/}(报告现有 ${reports:-0} 份)"
|
||
}
|
||
|
||
# ---------- 每月:一条命令出齐审阅项;季度月附治理清单 ----------
|
||
run_monthly() {
|
||
local mo md mon q
|
||
mo="$(date '+%Y-%m')"
|
||
mon="$(date '+%m')"
|
||
md="$MONTHLY_DIR/review-$mo.md"
|
||
{
|
||
echo "# 技能月度审阅 $mo"
|
||
echo
|
||
./scripts/monthly-review.sh 2>&1
|
||
} > "$md"
|
||
collect_health
|
||
{
|
||
echo
|
||
echo "## 补充:漂移与第三方"
|
||
echo
|
||
echo "- ${DRIFT_LINE:-漂移未采集}"
|
||
echo "- ${THIRD_LINE:-第三方未采集}"
|
||
} >> "$md"
|
||
log "monthly -> ${md#$ROOT/}"
|
||
case "$mon" in
|
||
01|04|07|10)
|
||
q="$(date '+%Y')-Q$(( (10#$mon - 1) / 3 + 1 ))"
|
||
local mq="$MONTHLY_DIR/quarterly-$q.md"
|
||
{
|
||
echo "# 季度治理复核 $q"
|
||
echo
|
||
echo "按 MAINTENANCE「每季度首月 1 日 10:30」逐项打勾:"
|
||
echo
|
||
echo "- [ ] 过一遍 \`docs/skill-routing.md\`:删掉不再用的引用、补上新技能"
|
||
echo "- [ ] 清理 \`deprecated\`:满 3 个月且无回滚需求 → 删除插件目录"
|
||
echo "- [ ] 复核分桶与调用模式:还有没有命令型技能占着 \`model-invoked\`"
|
||
echo "- [ ] 复核覆盖率:\`--coverage\` 两项缺口是否按路线图收敛"
|
||
echo "- [ ] 更新 \`MAINTENANCE.md\` 与 \`CHANGELOG.md\`"
|
||
} > "$mq"
|
||
log "quarterly -> ${mq#$ROOT/}"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
case "$JOB" in
|
||
daily) run_daily ;;
|
||
weekly) run_weekly ;;
|
||
monthly) run_monthly ;;
|
||
*)
|
||
echo "用法: $0 daily|weekly|monthly" >&2
|
||
exit 2
|
||
;;
|
||
esac
|