@seanyao/roll 2.602.1 → 2.602.3
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 +29 -0
- package/bin/roll +184 -25
- package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
- package/lib/model_prices.py +10 -2
- package/lib/prices/snapshot-2026-05-23-kimi.json +4 -3
- package/package.json +1 -1
- package/skills/roll-design/SKILL.md +10 -4
- package/skills/roll-loop/SKILL.md +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.602.3
|
|
4
|
+
|
|
5
|
+
### 可见性
|
|
6
|
+
|
|
7
|
+
- **deepseek 的 cycle 也能看到 token 和成本(FIX-164)** — 之前 deepseek 跑的 cycle 明明提了 PR、有耗时,token/成本却是空的(压根没采到用量);现在每轮都按当轮实际 agent 落账,deepseek 和 kimi 一样看得见用量和成本 `[loop]`
|
|
8
|
+
|
|
9
|
+
### 稳定性
|
|
10
|
+
|
|
11
|
+
- **roll update 后不再被反向提示"升级"回旧版本(FIX-166)** — 升级后旧的版本检查缓存没清,偶尔还会冒出叫你装回旧号的提示;现在升级会顺手清掉缓存,这点残留也没了 `[loop]`
|
|
12
|
+
|
|
13
|
+
## v2.602.2
|
|
14
|
+
|
|
15
|
+
### 自动化流水线
|
|
16
|
+
|
|
17
|
+
- **自家 PR 跑红了会自动后台修(US-LOOP-062a)** — loop 开的 PR 一旦 CI 变红,会在后台 checkout 交给 agent 修(有次数上限、不重复触发),修不好或关了自愈才告警,不再烂成没人管的僵尸 PR
|
|
18
|
+
- **你批准过的绿 PR 会被自动合并(US-LOOP-062b)** — 你 review 通过、CI 又绿的 PR,loop 直接帮你合并删分支,不用再等仓库级 auto-merge、也不用手点
|
|
19
|
+
|
|
20
|
+
### 可见性
|
|
21
|
+
|
|
22
|
+
- **kimi / deepseek 成本按人民币 ¥ 显示(FIX-162)** — 之前 kimi 的成本被误标成美元 $,现在和 deepseek 一样按 ¥ 显示,成本总账不再混币种
|
|
23
|
+
|
|
24
|
+
### 稳定性
|
|
25
|
+
|
|
26
|
+
- **升级提示不再反向叫你装回旧版本(FIX-163)** — 换上更短的新版本号后,`roll loop on` 等命令一度提示"升级"回旧的年份版本号;现在按 GitHub 最新发布判断,装的是最新就不再误报,发版遇到新号比线上"看起来小"也不再卡住
|
|
27
|
+
|
|
28
|
+
### 工程和测试
|
|
29
|
+
|
|
30
|
+
- **roll-design 中等复杂度也过一道 peer(US-SKILL-018)** — 以前只有大改或跨边界的设计才自动触发 peer 评审,现在中等复杂度也会过一道,方向隐患能在拆故事前被独立挑一次;10 秒内可跳过
|
|
31
|
+
|
|
3
32
|
## v2.602.1
|
|
4
33
|
|
|
5
34
|
### 新功能
|
package/bin/roll
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# Roll — AI Agent Convention Manager
|
|
5
5
|
# Single source of truth for how all AI coding agents behave.
|
|
6
6
|
|
|
7
|
-
VERSION="2.602.
|
|
7
|
+
VERSION="2.602.3"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -1998,6 +1998,10 @@ cmd_update() {
|
|
|
1998
1998
|
_check_installed_version_or_retry
|
|
1999
1999
|
fi
|
|
2000
2000
|
|
|
2001
|
+
# FIX-166: the running version just changed — invalidate the stale update-check
|
|
2002
|
+
# cache so we don't reverse-nag the new version to "upgrade" to the old latest.
|
|
2003
|
+
_invalidate_update_cache
|
|
2004
|
+
|
|
2001
2005
|
echo ""
|
|
2002
2006
|
info "$(msg update.re_syncing_to_ai_tools)"
|
|
2003
2007
|
echo ""
|
|
@@ -9022,14 +9026,21 @@ fi
|
|
|
9022
9026
|
# first, shared fallback) so the emitter appends to the same file the reader
|
|
9023
9027
|
# consumes. Dispatch by agent so each emitter reads the right session format
|
|
9024
9028
|
# (pi.usage_from_session vs kimi.usage_from_session).
|
|
9025
|
-
|
|
9029
|
+
# FIX-164: dispatch on the per-cycle ROUTED agent (CYCLE_AGENT), not the project
|
|
9030
|
+
# default (_project_agent). With the project default, a kimi-default project ran
|
|
9031
|
+
# kimi_emit for EVERY cycle — so deepseek/pi cycles emitted no usage event at all
|
|
9032
|
+
# (kimi_emit finds no kimi session for a pi cycle), and the dashboard showed them
|
|
9033
|
+
# with empty token/cost. CYCLE_AGENT (set above by the router) is the agent that
|
|
9034
|
+
# actually ran this cycle, so the right emitter reads the right session format.
|
|
9035
|
+
_emit_agent="\${CYCLE_AGENT:-\$(_project_agent)}"
|
|
9036
|
+
if [ "\$_emit_agent" != "claude" ]; then
|
|
9026
9037
|
_pi_rt=\$(_loop_runtime_dir "${slug}" 2>/dev/null || echo "")
|
|
9027
9038
|
if [ -n "\$_pi_rt" ]; then
|
|
9028
9039
|
_pi_evfile="\${_pi_rt}/events.ndjson"
|
|
9029
9040
|
else
|
|
9030
9041
|
_pi_evfile="\${_SHARED_ROOT:-\$HOME/.shared/roll}/loop/events-${slug}.ndjson"
|
|
9031
9042
|
fi
|
|
9032
|
-
case "\$
|
|
9043
|
+
case "\$_emit_agent" in
|
|
9033
9044
|
kimi)
|
|
9034
9045
|
if [ -f "${kimi_emit_script}" ]; then
|
|
9035
9046
|
python3 "${kimi_emit_script}" --cwd "\$WT" --cycle "\${CYCLE_ID}" \\
|
|
@@ -11295,6 +11306,142 @@ _loop_heal_dir() {
|
|
|
11295
11306
|
printf '%s\n' "${ROLL_LOOP_DIR:-${HOME}/.shared/roll/loop}/heal"
|
|
11296
11307
|
}
|
|
11297
11308
|
|
|
11309
|
+
# US-LOOP-062a: deduped [TYPE:loop-pr-ci-red] ALERT for a red loop/* PR. One
|
|
11310
|
+
# line per PR until the alert file is consumed — never silently drops.
|
|
11311
|
+
_loop_pr_ci_red_alert() {
|
|
11312
|
+
local num="$1" head_ref="$2" msg="${3:-own loop PR CI red — needs heal}"
|
|
11313
|
+
local alert="${_LOOP_ALERT}"
|
|
11314
|
+
[ -n "$alert" ] || return 0
|
|
11315
|
+
mkdir -p "$(dirname "$alert")" 2>/dev/null || true
|
|
11316
|
+
grep -qF "[TYPE:loop-pr-ci-red] PR #${num} " "$alert" 2>/dev/null && return 0
|
|
11317
|
+
printf '[%s] [error] [TYPE:loop-pr-ci-red] PR #%s %s: %s\n' \
|
|
11318
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$num" "$head_ref" "$msg" >> "$alert"
|
|
11319
|
+
}
|
|
11320
|
+
|
|
11321
|
+
# US-LOOP-062a: upsert "key: value" in the per-slug loop state file (same flat
|
|
11322
|
+
# format the HEAD-CI heal counter uses).
|
|
11323
|
+
_loop_state_set() {
|
|
11324
|
+
local key="$1" val="$2" state="${_LOOP_STATE}"
|
|
11325
|
+
[ -n "$state" ] || return 0
|
|
11326
|
+
mkdir -p "$(dirname "$state")" 2>/dev/null || true
|
|
11327
|
+
if [ -f "$state" ] && grep -q "^${key}:" "$state" 2>/dev/null; then
|
|
11328
|
+
local _tmp; _tmp=$(mktemp)
|
|
11329
|
+
grep -v "^${key}:" "$state" > "$_tmp" 2>/dev/null || true
|
|
11330
|
+
printf '%s: %s\n' "$key" "$val" >> "$_tmp"
|
|
11331
|
+
mv "$_tmp" "$state"
|
|
11332
|
+
else
|
|
11333
|
+
printf '%s: %s\n' "$key" "$val" >> "$state"
|
|
11334
|
+
fi
|
|
11335
|
+
}
|
|
11336
|
+
|
|
11337
|
+
# US-LOOP-062a: background-heal a red loop/* PR (loop_self_ci_red verdict).
|
|
11338
|
+
# Bounded by heal_count.pr:<num> (max ROLL_LOOP_HEAL_MAX, default 2);
|
|
11339
|
+
# ROLL_LOOP_NO_HEAL=1 disables. A per-PR lock (pid marker) prevents duplicate
|
|
11340
|
+
# concurrent heals; a stale lock from a dead pid is reclaimed. On disabled /
|
|
11341
|
+
# budget-exhausted / nothing-to-do → a deduped ALERT (never silent). The heal
|
|
11342
|
+
# agent is chosen via _project_agent() (no bare `claude -p`). Non-blocking: the
|
|
11343
|
+
# actual checkout→fix→push runs in a background subshell so the PR tick returns
|
|
11344
|
+
# immediately.
|
|
11345
|
+
_loop_pr_heal_self() {
|
|
11346
|
+
local num="$1" head_ref="$2" slug="${3:-}"
|
|
11347
|
+
[ -n "$num" ] || return 0
|
|
11348
|
+
|
|
11349
|
+
local heal_max="${ROLL_LOOP_HEAL_MAX:-2}"
|
|
11350
|
+
if [ "${ROLL_LOOP_NO_HEAL:-}" = "1" ] || [ "${heal_max:-0}" -le 0 ]; then
|
|
11351
|
+
_loop_pr_ci_red_alert "$num" "$head_ref" "auto-heal off (ROLL_LOOP_NO_HEAL) — fix manually"
|
|
11352
|
+
return 0
|
|
11353
|
+
fi
|
|
11354
|
+
|
|
11355
|
+
local heal_dir; heal_dir="$(_loop_heal_dir)"
|
|
11356
|
+
mkdir -p "$heal_dir" 2>/dev/null || true
|
|
11357
|
+
local lock="${heal_dir}/pr-${num}.lock"
|
|
11358
|
+
if [ -f "$lock" ]; then
|
|
11359
|
+
local lpid; lpid=$(cat "$lock" 2>/dev/null || echo "")
|
|
11360
|
+
if [ -n "$lpid" ] && kill -0 "$lpid" 2>/dev/null; then
|
|
11361
|
+
return 0 # heal already in flight for this PR
|
|
11362
|
+
fi
|
|
11363
|
+
rm -f "$lock" # stale lock (dead pid) — reclaim
|
|
11364
|
+
fi
|
|
11365
|
+
|
|
11366
|
+
local key="heal_count.pr:${num}"
|
|
11367
|
+
local count=0
|
|
11368
|
+
[ -f "${_LOOP_STATE}" ] && count=$(grep "^${key}:" "${_LOOP_STATE}" 2>/dev/null | awk '{print $2}' | head -1)
|
|
11369
|
+
count=$(( ${count:-0} + 0 ))
|
|
11370
|
+
if [ "$count" -ge "$heal_max" ]; then
|
|
11371
|
+
_loop_pr_ci_red_alert "$num" "$head_ref" "auto-heal budget exhausted (${count}/${heal_max}) — fix manually"
|
|
11372
|
+
return 0
|
|
11373
|
+
fi
|
|
11374
|
+
_loop_state_set "$key" "$(( count + 1 ))"
|
|
11375
|
+
|
|
11376
|
+
local agent; agent="$(_project_agent 2>/dev/null)"; agent="${agent:-claude}"
|
|
11377
|
+
|
|
11378
|
+
( echo "$BASHPID" > "$lock"
|
|
11379
|
+
_loop_pr_do_heal "$num" "$head_ref" "$slug" "$agent" >/dev/null 2>&1
|
|
11380
|
+
rm -f "$lock"
|
|
11381
|
+
) &
|
|
11382
|
+
disown 2>/dev/null || true
|
|
11383
|
+
info "PR #${num}: background heal $(( count + 1 ))/${heal_max} dispatched (agent=${agent})"
|
|
11384
|
+
return 0
|
|
11385
|
+
}
|
|
11386
|
+
|
|
11387
|
+
# US-LOOP-062a: the actual heal work (runs in the background subshell). Gathers
|
|
11388
|
+
# the failing-CI context, checks out the PR branch in a throwaway worktree, hands
|
|
11389
|
+
# the fix to the dynamically-selected agent via _agent_argv (no bare claude -p),
|
|
11390
|
+
# and pushes back to the SAME PR branch. Best-effort: any failure leaves the PR
|
|
11391
|
+
# untouched for the next tick (the heal budget caps retries). Overridable in
|
|
11392
|
+
# tests.
|
|
11393
|
+
_loop_pr_do_heal() {
|
|
11394
|
+
local num="$1" head_ref="$2" slug="${3:-}" agent="${4:-claude}"
|
|
11395
|
+
[ -n "$num" ] && [ -n "$head_ref" ] || return 1
|
|
11396
|
+
command -v gh >/dev/null 2>&1 || return 1
|
|
11397
|
+
[ -n "$slug" ] || _gh_resolve slug || return 1
|
|
11398
|
+
|
|
11399
|
+
# Capture failing-run context for the fix prompt.
|
|
11400
|
+
local ctx="/tmp/roll-heal-pr-${num}.log"
|
|
11401
|
+
{
|
|
11402
|
+
printf '=== CI heal context: PR #%s (%s) ===\n\n' "$num" "$head_ref"
|
|
11403
|
+
gh -R "$slug" pr checks "$num" 2>/dev/null || true
|
|
11404
|
+
local _run
|
|
11405
|
+
_run=$(gh -R "$slug" pr checks "$num" --json link --jq '.[]|select(.state=="FAILURE")|.link' 2>/dev/null \
|
|
11406
|
+
| grep -oE 'runs/[0-9]+' | head -1 | cut -d/ -f2)
|
|
11407
|
+
if [ -n "$_run" ]; then
|
|
11408
|
+
printf '\n--- failing run log (tail) ---\n'
|
|
11409
|
+
gh -R "$slug" run view "$_run" --log-failed 2>/dev/null | tail -200 || true
|
|
11410
|
+
fi
|
|
11411
|
+
} > "$ctx" 2>&1
|
|
11412
|
+
|
|
11413
|
+
# Isolated worktree on the PR branch.
|
|
11414
|
+
local wt; wt="$(mktemp -d)/pr-${num}"
|
|
11415
|
+
git fetch origin "$head_ref" >/dev/null 2>&1 || return 1
|
|
11416
|
+
git worktree add "$wt" "origin/${head_ref}" >/dev/null 2>&1 || { rm -rf "$(dirname "$wt")"; return 1; }
|
|
11417
|
+
|
|
11418
|
+
local prompt="[roll PR 自愈] PR #${num} (${head_ref}) 的 CI 红了。失败上下文见 ${ctx}。请只修使 CI 转绿所需的最小改动,保持 TCR 微提交节奏,改完直接 commit。不要改无关代码,不要反问。"
|
|
11419
|
+
_agent_argv "$agent" text "$prompt"
|
|
11420
|
+
( cd "$wt" && "${_AGENT_ARGV[@]}" ) >/dev/null 2>&1 || true
|
|
11421
|
+
|
|
11422
|
+
# Push back to the same PR branch if the agent produced commits.
|
|
11423
|
+
if [ -n "$(cd "$wt" && git rev-list "origin/${head_ref}..HEAD" 2>/dev/null)" ]; then
|
|
11424
|
+
( cd "$wt" && git push origin "HEAD:${head_ref}" ) >/dev/null 2>&1 || true
|
|
11425
|
+
fi
|
|
11426
|
+
git worktree remove --force "$wt" >/dev/null 2>&1 || true
|
|
11427
|
+
rm -rf "$(dirname "$wt")" 2>/dev/null || true
|
|
11428
|
+
}
|
|
11429
|
+
|
|
11430
|
+
# US-LOOP-062b: merge a human-approved PR directly when CI is green and the PR
|
|
11431
|
+
# is conflict-free, instead of waiting for repo-level auto-merge (which may be
|
|
11432
|
+
# disabled). Mirrors the bot-approved eager-merge. Merge failure is NON-fatal:
|
|
11433
|
+
# the PR is left open and the next PR-loop tick retries.
|
|
11434
|
+
_loop_pr_merge_approved() {
|
|
11435
|
+
local num="$1" ci_state="$2" mergeable="$3" slug="$4"
|
|
11436
|
+
[ -n "$num" ] && [ -n "$slug" ] || return 0
|
|
11437
|
+
[ "$ci_state" = "success" ] && [ "$mergeable" = "MERGEABLE" ] || return 0
|
|
11438
|
+
if gh -R "$slug" pr merge "$num" --squash --delete-branch >/dev/null 2>&1; then
|
|
11439
|
+
info "PR #${num}: human-approved + CI green — merged"
|
|
11440
|
+
else
|
|
11441
|
+
warn "PR #${num}: merge failed (human-approved + CI green) — left open, will retry"
|
|
11442
|
+
fi
|
|
11443
|
+
}
|
|
11444
|
+
|
|
11298
11445
|
# REFACTOR-030: removed `_loop_self_heal_ci` and `_loop_clear_heal_state`.
|
|
11299
11446
|
# REFACTOR-023 merged the CI self-heal counter into the main state.yaml flow,
|
|
11300
11447
|
# but the two helpers themselves were left behind as dead code. Their job
|
|
@@ -11364,7 +11511,12 @@ _loop_check_depends_on() {
|
|
|
11364
11511
|
[ -n "$row" ] || return 1
|
|
11365
11512
|
|
|
11366
11513
|
local deps
|
|
11367
|
-
|
|
11514
|
+
# FIX-167: include lowercase letters so lettered sub-story ids (the a/b/c
|
|
11515
|
+
# split pattern, e.g. US-LOOP-062c) aren't truncated to their numeric base
|
|
11516
|
+
# (US-LOOP-062). The old class [A-Z0-9,-] stopped at the lowercase suffix,
|
|
11517
|
+
# dropping the suffix AND every dep after it — so a story depending on a Done
|
|
11518
|
+
# sub-story was matched against its (often Hold) parent and wrongly blocked.
|
|
11519
|
+
deps=$(echo "$row" | grep -oE 'depends-on:[A-Za-z][A-Za-z0-9,-]+' | head -1 | sed 's/depends-on://' || true)
|
|
11368
11520
|
[ -n "$deps" ] || return 0
|
|
11369
11521
|
|
|
11370
11522
|
local unsatisfied=""
|
|
@@ -11717,22 +11869,19 @@ _loop_pr_inbox() {
|
|
|
11717
11869
|
_loop_pr_merge_self_eager "$num" "$ci_state" "$mergeable" "$slug"
|
|
11718
11870
|
;;
|
|
11719
11871
|
loop_self_ci_red)
|
|
11720
|
-
#
|
|
11721
|
-
#
|
|
11722
|
-
#
|
|
11723
|
-
#
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
mkdir -p "$(dirname "$_ci_red_alert")" 2>/dev/null || true
|
|
11729
|
-
if ! grep -qF "[TYPE:loop-pr-ci-red] PR #${num} " "$_ci_red_alert" 2>/dev/null; then
|
|
11730
|
-
printf '[%s] [error] [TYPE:loop-pr-ci-red] PR #%s %s: own loop PR CI red — needs heal (FIX-158)\n' \
|
|
11731
|
-
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$num" "$head_ref" >> "$_ci_red_alert"
|
|
11732
|
-
fi
|
|
11872
|
+
# US-LOOP-062a: a red loop/* PR (classified by US-LOOP-049) is now
|
|
11873
|
+
# background-healed: bounded retries via heal budget + dynamic agent,
|
|
11874
|
+
# falling back to the deduped [TYPE:loop-pr-ci-red] ALERT (FIX-158's
|
|
11875
|
+
# surfacing) when heal is disabled/exhausted. Re-wires US-LOOP-050.
|
|
11876
|
+
_loop_pr_heal_self "$num" "$head_ref" "$slug" || true
|
|
11877
|
+
;;
|
|
11878
|
+
blocked_human_request_changes)
|
|
11879
|
+
: # skip — last human review requested changes; wait for the author
|
|
11733
11880
|
;;
|
|
11734
|
-
|
|
11735
|
-
:
|
|
11881
|
+
blocked_human_approved)
|
|
11882
|
+
# US-LOOP-062b: human approved — merge directly when green + mergeable
|
|
11883
|
+
# (don't wait for repo auto-merge, which may be off).
|
|
11884
|
+
_loop_pr_merge_approved "$num" "$ci_state" "$mergeable" "$slug" || true
|
|
11736
11885
|
;;
|
|
11737
11886
|
stale)
|
|
11738
11887
|
_loop_pr_rebase_circuit "$num" || true
|
|
@@ -15418,6 +15567,15 @@ _show_changelog() {
|
|
|
15418
15567
|
}
|
|
15419
15568
|
|
|
15420
15569
|
# ─── Version check (background, non-blocking, 24h cache) ─────────────────────
|
|
15570
|
+
# FIX-166: drop the cached `latest` so the next run refetches it. Called after a
|
|
15571
|
+
# successful `roll update`: without this, the 24h cache TTL keeps the pre-upgrade
|
|
15572
|
+
# `latest` around, and _notify_update reverse-nags the freshly-installed (newer)
|
|
15573
|
+
# version to "upgrade" to the older cached one for up to a day. With the file
|
|
15574
|
+
# gone, _notify_update stays silent until the async refetch repopulates it.
|
|
15575
|
+
_invalidate_update_cache() {
|
|
15576
|
+
rm -f "${ROLL_HOME}/.update-check"
|
|
15577
|
+
}
|
|
15578
|
+
|
|
15421
15579
|
_check_update_async() {
|
|
15422
15580
|
local cache="${ROLL_HOME}/.update-check"
|
|
15423
15581
|
local now; now=$(date +%s)
|
|
@@ -15440,12 +15598,13 @@ _notify_update() {
|
|
|
15440
15598
|
[[ -f "$cache" ]] || return 0
|
|
15441
15599
|
local latest; latest=$(awk '{print $2}' "$cache" 2>/dev/null || true)
|
|
15442
15600
|
[[ -z "$latest" || "$latest" == "$VERSION" ]] && return
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
|
|
15447
|
-
|
|
15448
|
-
|
|
15601
|
+
# FIX-163: the cached `latest` is GitHub's releases/latest — the newest
|
|
15602
|
+
# release by created_at, NOT by semver. Under the MAJOR.MMDD scheme a plain
|
|
15603
|
+
# `sort -V` mis-ranks versions across the year-based→MAJOR.MMDD transition
|
|
15604
|
+
# (2026.601.4 > 2.602.1) and the Jan-1 MMDD wrap (2.1231.N > 2.101.1), which
|
|
15605
|
+
# previously (a) reverse-nagged users to "upgrade" to an older release and
|
|
15606
|
+
# (b) silently suppressed real updates after the wrap. Trust GitHub's
|
|
15607
|
+
# chronological latest: if it differs from what's running, surface it.
|
|
15449
15608
|
echo ""
|
|
15450
15609
|
warn "$(msg update.available "$latest")"
|
|
15451
15610
|
}
|
|
Binary file
|
package/lib/model_prices.py
CHANGED
|
@@ -152,12 +152,20 @@ def _resolve_name(model: Optional[str],
|
|
|
152
152
|
return fallback
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
_NO_CURRENCY_MATCH = "\x00__no_currency_match__\x00"
|
|
156
|
+
|
|
157
|
+
|
|
155
158
|
def currency_for(model: Optional[str]) -> str:
|
|
156
159
|
"""Return the native currency code (USD/CNY) for a model.
|
|
157
160
|
|
|
158
|
-
Falls back to 'USD' when the model isn't in any snapshot.
|
|
161
|
+
Falls back to 'USD' when the model isn't in any snapshot. FIX-162: resolve
|
|
162
|
+
with a sentinel default so a *genuinely unknown* model returns USD instead
|
|
163
|
+
of inheriting the global DEFAULT model's currency (which is a CNY kimi
|
|
164
|
+
entry — that would mislabel unrelated unknown models as CNY).
|
|
159
165
|
"""
|
|
160
|
-
name = _resolve_name(model)
|
|
166
|
+
name = _resolve_name(model, default=_NO_CURRENCY_MATCH)
|
|
167
|
+
if name == _NO_CURRENCY_MATCH:
|
|
168
|
+
return "USD"
|
|
161
169
|
return _CURRENCY.get(name, "USD")
|
|
162
170
|
|
|
163
171
|
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
"effective_at": "2026-05-23",
|
|
4
4
|
"source_url": "https://platform.kimi.com/docs/pricing/chat",
|
|
5
5
|
"vendor": "kimi",
|
|
6
|
-
"currency": "
|
|
6
|
+
"currency": "CNY",
|
|
7
7
|
"default_model": "kimi-k2.5",
|
|
8
|
-
"notes": "Rates per million tokens (
|
|
8
|
+
"notes": "Rates per million tokens (CNY). cache_create estimated at 1.25x input. Prices from public Kimi API platform docs — verify with `roll prices refresh` if page layout changes. Model names: kimi-k2 (prior gen), kimi-k2.5 (current), kimi-k2.6 (latest). kimi-for-coding is the kimi-code CLI's model id (alias of the current K2 line) — FIX-162 so usage events tagged `kimi-code/kimi-for-coding` resolve to a real CNY entry instead of falling back to USD.",
|
|
9
9
|
"prices": {
|
|
10
10
|
"kimi-k2": {"in": 1.00, "out": 4.00, "cache_create": 1.25, "cache_read": 0.25},
|
|
11
11
|
"kimi-k2.5": {"in": 1.00, "out": 4.00, "cache_create": 1.25, "cache_read": 0.25},
|
|
12
|
-
"kimi-k2.6": {"in": 1.00, "out": 4.00, "cache_create": 1.25, "cache_read": 0.25}
|
|
12
|
+
"kimi-k2.6": {"in": 1.00, "out": 4.00, "cache_create": 1.25, "cache_read": 0.25},
|
|
13
|
+
"kimi-for-coding": {"in": 1.00, "out": 4.00, "cache_create": 1.25, "cache_read": 0.25}
|
|
13
14
|
}
|
|
14
15
|
}
|
package/package.json
CHANGED
|
@@ -227,7 +227,7 @@ User Input
|
|
|
227
227
|
│ Approach confirmed
|
|
228
228
|
▼
|
|
229
229
|
┌─────────────────────────────┐
|
|
230
|
-
│ [peer] Direction Review │ ← if complexity=large or cross-context; 10s opt-out
|
|
230
|
+
│ [peer] Direction Review │ ← if complexity=medium/large or cross-context; 10s opt-out
|
|
231
231
|
│ Skill("roll-peer", │
|
|
232
232
|
│ tag="architecture") │
|
|
233
233
|
└─────────────┬───────────────┘
|
|
@@ -298,7 +298,7 @@ User Input
|
|
|
298
298
|
│
|
|
299
299
|
▼
|
|
300
300
|
┌─────────────────────────────────────────┐
|
|
301
|
-
│ [peer] Plan Review │ ← if complexity=large; 10s opt-out
|
|
301
|
+
│ [peer] Plan Review │ ← if complexity=medium/large; 10s opt-out
|
|
302
302
|
│ Skill("roll-peer", tag="architecture")│
|
|
303
303
|
└──────────────────┬──────────────────────┘
|
|
304
304
|
│ AGREE / skipped
|
|
@@ -840,13 +840,19 @@ Two checkpoints, both with 10s opt-out:
|
|
|
840
840
|
```
|
|
841
841
|
1. After Discuss — Direction Review
|
|
842
842
|
Approach confirmed → [peer, tag=architecture] → challenge the direction before DDD
|
|
843
|
-
Trigger: complexity=large OR requirement touches multiple Bounded Contexts
|
|
843
|
+
Trigger: complexity=medium OR complexity=large OR requirement touches multiple Bounded Contexts
|
|
844
844
|
|
|
845
845
|
2. After Solution Design — Plan Review
|
|
846
846
|
Plan written → [peer, tag=architecture] → full plan review before story split
|
|
847
|
-
Trigger: complexity=large (greenfield always qualifies)
|
|
847
|
+
Trigger: complexity=medium OR complexity=large (greenfield always qualifies)
|
|
848
848
|
```
|
|
849
849
|
|
|
850
|
+
Rationale (US-SKILL-018): medium-complexity designs also routinely carry
|
|
851
|
+
direction/plan risks worth one independent challenge before story split — the
|
|
852
|
+
cost of one bounded peer pass is small next to reworking a misaimed design after
|
|
853
|
+
it ships. So peer now triggers at medium as well as large; the 10s opt-out
|
|
854
|
+
stays, so you can always skip when you're confident.
|
|
855
|
+
|
|
850
856
|
On AGREE or user skip → continue to the next step normally.
|
|
851
857
|
On REFINE/OBJECT → incorporate feedback, regenerate the relevant output, re-trigger peer.
|
|
852
858
|
On ESCALATE → present both proposals to user for final call.
|
|
@@ -157,9 +157,10 @@ Call `_loop_pr_inbox` after the pre-run CI check passes. It walks
|
|
|
157
157
|
|
|
158
158
|
| Classification | Action |
|
|
159
159
|
|---|---|
|
|
160
|
-
| `loop_self` (head ref starts with `loop
|
|
160
|
+
| `loop_self` (head ref starts with `loop/`, CI not red) | Skip — let GitHub auto-merge handle it; never AI-review your own commit |
|
|
161
|
+
| `loop_self_ci_red` (loop/* PR whose CI went red) | **US-LOOP-062a**: `_loop_pr_heal_self` — background-heal (per-PR lock + heal budget `ROLL_LOOP_HEAL_MAX`, default 2, via `_project_agent`); on `ROLL_LOOP_NO_HEAL=1` / budget exhausted → deduped `[TYPE:loop-pr-ci-red]` ALERT (never silently dropped) |
|
|
161
162
|
| `blocked_human_request_changes` | Skip — last human review requested changes; wait for the author to push fixes |
|
|
162
|
-
| `blocked_human_approved` |
|
|
163
|
+
| `blocked_human_approved` | **US-LOOP-062b**: `_loop_pr_merge_approved` — merge directly (`gh pr merge --squash`) when CI green + mergeable, instead of relying on repo auto-merge (which may be off); merge failure is non-fatal (retried next tick) |
|
|
163
164
|
| `stale` (CI failed or branch behind/conflicting) | Try `_loop_pr_rebase_stale` after the circuit breaker allows it |
|
|
164
165
|
| `eligible` (clean external PR, no blocking review) | Invoke `_loop_pr_review_external` — the actual decision is provided by US-AUTO-035's GitHub Action |
|
|
165
166
|
|