docs/HISTORY-LEDGER-VIZ.html
Rama master · réplica de lectura del repositorio original.
<!DOCTYPE html>
<html lang="zh-CN" class="no-js">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WindsurfAPI · 全局历史账本</title>
<meta name="description" content="WindsurfAPI 项目 2026-04-09 → 2026-09-15 的全部历史:1387 个提交、196 个版本、82 个 PR、180 个 issue">
<script>document.documentElement.classList.remove('no-js');</script>
<style>
/* ============ 设计令牌(源自 src/dashboard/index.html 的设计 DNA) ============ */
:root {
--paper: #0a0a0a; --paper-2: #141414; --paper-3: #1f1f1f;
--surface: #141414; --surface-2: #1c1c1e;
--ink: #ededed; --ink-soft: #c4c4c6; --text-muted: #a1a1aa; --text-dim: #71717a;
--border: rgba(255,255,255,.10); --border-strong: rgba(255,255,255,.18);
--accent: #d97757; --accent-hover: #c56245; --accent-soft: rgba(217,119,87,.20);
--success: #788c5d; --success-soft: rgba(120,140,93,.22);
--warn: #d9a257; --warn-soft: rgba(217,162,87,.22);
--error: #c65d4e; --error-soft: rgba(198,93,78,.22);
--info: #6a9bcc; --info-soft: rgba(106,155,204,.22);
--chart-req: #4c9aff; --chart-succ: #4a9d6a; --chart-err: #e0524a;
--radius: 12px; --radius-sm: 8px; --radius-lg: 16px;
--shadow-sm: 0 1px 2px rgba(0,0,0,.4); --shadow: 0 4px 12px rgba(0,0,0,.4); --shadow-lg: 0 8px 32px rgba(0,0,0,.5);
--font: 'Poppins', -apple-system, 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
--mono: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
--ease-spring: cubic-bezier(.16,1,.3,1);
--ease-smooth: cubic-bezier(.2,.8,.2,1);
/* 动画参数(一处调全局) */
--type-speed: 34ms; --caret-color: var(--accent);
--bar-duration: .65s; --bar-stagger: 70ms;
--node-duration: .5s; --node-stagger: 110ms;
}
/* 三层兜底第一层:无 JS 直达终态 */
.no-js [data-reveal] .bar, .no-js .bar { transform: scaleY(1); }
.no-js [data-reveal] .node, .no-js [data-reveal] .stat, .no-js .tl-node { opacity: 1; }
.no-js .hero-title, .no-js .hero-sub { white-space: normal; width: auto; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: var(--paper); color: var(--ink); font-family: var(--font); font-size: 14px; line-height: 1.5; }
::selection { background: var(--accent-soft); }
/* ============ 背景动效(网格 + 光晕 + 粒子) ============ */
.bg { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; background: var(--paper); }
.bg-grid {
position: absolute; inset: 0;
background-image:
linear-gradient(rgba(106,155,204,.045) 1px, transparent 1px),
linear-gradient(90deg, rgba(106,155,204,.045) 1px, transparent 1px);
background-size: 44px 44px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 40%, transparent 100%);
}
.bg-orb { position: absolute; border-radius: 50%; filter: blur(80px); }
.bg-orb.a { width: 460px; height: 460px; top: -140px; left: -100px;
background: radial-gradient(circle, rgba(217,119,87,.14), transparent 65%);
animation: orb-drift 30s ease-in-out infinite alternate; }
.bg-orb.b { width: 380px; height: 380px; bottom: -120px; right: -80px;
background: radial-gradient(circle, rgba(106,155,204,.10), transparent 65%);
animation: orb-drift 40s ease-in-out infinite alternate-reverse; }
@keyframes orb-drift {
from { transform: translate3d(0,0,0) scale(1); }
to { transform: translate3d(64px,48px,0) scale(1.14); }
}
#bg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* ─── 背景语录:弹幕式横穿 + 浮沉(合成器动画,零 JS 逐帧) ─── */
.bg-quotes {
position: absolute; inset: 0; overflow: hidden;
pointer-events: none; user-select: none;
font-family: var(--mono);
}
.bg-quotes .q {
position: absolute; top: var(--y); left: 0;
white-space: nowrap; opacity: var(--o, .05);
color: var(--c, #ededed); font-size: var(--s, 12px);
will-change: transform;
animation: q-drift var(--dur, 160s) linear infinite;
animation-delay: var(--delay, 0s);
}
.bg-quotes .q > span {
display: inline-block;
animation: q-bob var(--bob-dur, 14s) ease-in-out infinite alternate;
animation-delay: var(--bob-delay, 0s);
}
@keyframes q-drift {
from { transform: translateX(-100%); }
to { transform: translateX(100vw); }
}
@keyframes q-bob {
from { transform: translateY(0); }
to { transform: translateY(var(--bob, 16px)); }
}
.wrap { max-width: 1080px; margin: 0 auto; padding: 0 20px 80px; }
/* ============ Hero(打字机 + 大数字) ============ */
.hero { padding: 88px 0 40px; text-align: center; }
.logo-badge {
width: 56px; height: 56px; margin: 0 auto 22px; border-radius: 14px;
background: linear-gradient(180deg, #161b22, #0d1117);
border: 1px solid var(--border);
display: flex; align-items: center; justify-content: center;
box-shadow: 0 0 32px rgba(245,197,24,.12);
animation: logo-in .8s var(--ease-spring) both;
}
.logo-badge svg { width: 32px; height: 32px; }
@keyframes logo-in { from { transform: scale(.6); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.hero-title {
font-size: clamp(30px, 5vw, 44px); font-weight: 700; letter-spacing: -.02em;
white-space: nowrap; overflow: hidden;
}
.hero-title .accent { color: var(--accent); }
.hero-title.caret-on { border-right: 2px solid var(--caret-color); animation: caret-blink .8s steps(1) infinite; }
@keyframes caret-blink { 50% { border-color: transparent; } }
.hero-sub { margin-top: 14px; color: var(--text-muted); font-size: 14px; white-space: nowrap; overflow: hidden; }
.hero-sub.caret-on { border-right: 2px solid var(--caret-color); animation: caret-blink .8s steps(1) infinite; }
.hero-meta { margin-top: 26px; display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.badge {
display: inline-flex; align-items: center; gap: 6px;
font-size: 11px; font-weight: 600; color: var(--ink-soft);
background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 4px 12px;
}
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.accent { color: var(--accent); }
.badge.info { color: var(--info); }
.badge.success { color: var(--success); }
.badge.warn { color: var(--warn); }
.badge.error { color: var(--error); }
/* ============ 统计卡(数字滚动) ============ */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin: 44px 0 8px; }
.stat {
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
padding: 18px 16px; text-align: center; box-shadow: var(--shadow-sm);
opacity: 0; transform: translateY(18px);
transition: transform .25s var(--ease-spring), border-color .2s;
}
.stat:hover { transform: translateY(-2px); border-color: var(--border-strong); }
.stat .num {
display: block; font-family: var(--mono); font-size: 30px; font-weight: 700;
font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.stat .num.c-accent { color: var(--accent); }
.stat .num.c-info { color: var(--chart-req); }
.stat .num.c-success { color: var(--chart-succ); }
.stat .num.c-warn { color: var(--warn); }
.stat .num.c-error { color: var(--chart-err); }
.stat .cap { display: block; margin-top: 4px; font-size: 11px; color: var(--text-muted); letter-spacing: .04em; }
.stat .cap small { display: block; font-size: 10px; color: var(--text-dim); margin-top: 2px; }
[data-reveal].is-on .stat { animation: stat-in .5s var(--ease-spring) both; animation-delay: calc(var(--i) * 70ms); }
@keyframes stat-in { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
/* ============ 柱子生长动画 ============ */
.bar { transform: scaleY(0); transform-origin: bottom; }
[data-reveal].is-on .bar { animation: grow-up var(--bar-duration) var(--ease-spring) forwards; }
@keyframes grow-up { to { transform: scaleY(1); } }
/* ============ 章节卡片 ============ */
.section { margin-top: 48px; }
.card {
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
padding: 22px; box-shadow: var(--shadow-sm);
transition: border-color .2s, box-shadow .25s;
}
.card:hover { border-color: rgba(217,119,87,.35); box-shadow: var(--shadow-lg); }
.card h2 { font-size: 16px; font-weight: 650; display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.card h2::before { content: ''; width: 4px; height: 16px; background: linear-gradient(180deg, var(--accent), rgba(217,119,87,.15)); border-radius: 2px; }
.card .sub { color: var(--text-dim); font-size: 12px; margin-bottom: 16px; }
.card .sub b { color: var(--text-muted); }
svg { width: 100%; height: auto; display: block; }
[data-reveal] svg { opacity: 0; }
[data-reveal].is-on svg { animation: chart-fade .5s ease-out both; }
@keyframes chart-fade { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
/* ============ 四阶段 ============ */
.phases { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; margin-top: 10px; }
.phase {
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 14px 16px; position: relative; border-left: 3px solid var(--accent);
opacity: 0; transform: translateY(14px);
}
[data-reveal].is-on .phase { animation: stat-in .5s var(--ease-spring) both; animation-delay: calc(var(--i) * 90ms); }
.phase.blue { border-left-color: var(--info); }
.phase.green { border-left-color: var(--success); }
.phase.gold { border-left-color: var(--warn); }
.phase b { color: var(--accent); font-size: 13px; }
.phase.blue b { color: var(--info); } .phase.green b { color: var(--success); } .phase.gold b { color: var(--warn); }
.phase .date { font-family: var(--mono); font-size: 10px; color: var(--text-dim); margin-left: 6px; }
.phase small { display: block; color: var(--text-muted); margin-top: 5px; font-size: 11.5px; line-height: 1.6; }
/* ============ 自伤时间线(节点点亮动画) ============ */
.tl-node { cursor: pointer; opacity: 0; }
.tl-node:hover circle { r: 9px; transition: r .15s; }
.bar:hover { filter: drop-shadow(0 0 10px rgba(217,119,87,.4)); }
[data-reveal].is-on .tl-node {
animation: node-in var(--node-duration) ease-out both;
animation-delay: calc(var(--i) * var(--node-stagger));
}
@keyframes node-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; } }
.tl-axis { stroke: var(--border-strong); stroke-width: 2; }
.tl-tick { stroke: var(--border); stroke-width: 1; }
.tl-lbl { fill: var(--text-dim); font-size: 9px; font-family: var(--mono); }
/* ============ ESC 纯净模式(隐藏全部内容,露出背景流动字幕) ============ */
body.clean-mode .wrap, body.clean-mode .ledger-nav { display: none; }
body.clean-mode { cursor: default; }
.esc-hint {
position: fixed; right: 14px; bottom: 12px; z-index: 70;
font-size: 10px; color: var(--text-dim); font-family: var(--mono);
background: rgba(10,10,10,.6); border: 1px solid var(--border);
border-radius: 999px; padding: 3px 10px; pointer-events: none;
transition: opacity .3s;
}
body.clean-mode .esc-hint { color: var(--accent); border-color: rgba(217,119,87,.4); }
@media (prefers-reduced-motion: reduce) { body.clean-mode .bg-quotes .q { animation: none; } }
/* ============ 浮窗(hover 跟随 + 点击锁定) ============ */
#tl-tip {
position: fixed; z-index: 99; display: none; max-width: 340px;
background: rgba(28,28,30,.96); border: 1px solid var(--border-strong); border-radius: 10px;
padding: 12px 14px; font-size: 12px; box-shadow: var(--shadow-lg);
backdrop-filter: blur(8px); animation: tt-in .15s var(--ease-smooth);
}
@keyframes tt-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
#tl-tip .tt-title { font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
#tl-tip .tt-meta { color: var(--text-muted); font-size: 11px; margin-bottom: 6px; }
#tl-tip .tt-desc { color: var(--ink-soft); line-height: 1.55; margin-bottom: 8px; }
#tl-tip .tt-links { display: flex; gap: 8px; flex-wrap: wrap; }
#tl-tip .tt-links a {
pointer-events: auto; color: var(--accent); text-decoration: none; font-size: 11px;
border: 1px solid var(--border-strong); border-radius: 6px; padding: 3px 9px; background: var(--paper-3);
}
#tl-tip .tt-links a:hover { border-color: var(--accent); background: var(--accent-soft); }
/* ============ 页脚 ============ */
.foot { text-align: center; color: var(--text-dim); font-size: 11.5px; margin-top: 48px; line-height: 2; }
.foot code { background: var(--paper-3); padding: 1px 6px; border-radius: 4px; font-size: 10.5px; font-family: var(--mono); }
.foot a { color: var(--info); text-decoration: none; }
.foot a:hover { text-decoration: underline; }
@keyframes spin { to { transform: rotate(360deg); } }
.tree-node:hover { filter: drop-shadow(0 0 8px rgba(217,119,87,.5)); }
/* ============ reduced-motion:三层兜底第二层 ============ */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important; animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
}
[data-reveal] svg, .stat, .phase, .tl-node { opacity: 1 !important; transform: none !important; }
.bar { transform: scaleY(1) !important; }
.bg-orb, .bg-grid { display: none; }
.bg-quotes { display: none; }
}
/* ============ 语言选择器 ============ */
.lang-select {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px;
color: var(--text-muted); font-size: 11px; font-weight: 600; padding: 4px 10px;
cursor: pointer; font-family: var(--mono); margin-left: 4px; outline: none;
}
.lang-select:hover { color: var(--ink); border-color: var(--border-strong); }
.lang-select:focus { border-color: var(--accent); }
/* ============ 顶部引导 tab ============ */
.ledger-nav {
position: sticky; top: 0; z-index: 60;
display: flex; justify-content: center;
padding: 10px 12px 0;
background: rgba(10,10,10,.72);
backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.ledger-nav-inner {
display: flex; gap: 4px; padding: 4px;
background: var(--surface-2); border: 1px solid var(--border);
border-radius: 999px; box-shadow: var(--shadow);
max-width: 100%; overflow-x: auto; scrollbar-width: none;
}
.ledger-nav-inner::-webkit-scrollbar { display: none; }
.ledger-tab {
flex: 0 0 auto; padding: 6px 16px; border-radius: 999px;
font-size: 12px; font-weight: 600; color: var(--text-muted);
text-decoration: none; white-space: nowrap;
transition: background .18s var(--ease-smooth), color .18s var(--ease-smooth);
}
.ledger-tab:hover { color: var(--ink); background: rgba(255,255,255,.06); }
.ledger-tab.is-active { background: var(--accent); color: #0a0a0a; box-shadow: 0 0 14px var(--accent-soft); }
#stats, #months, #weeks, #kinds, #days, #hours, #tags, #tl, #contributors, #gittree { scroll-margin-top: 64px; }
@media (max-width: 720px) {
.ledger-nav { justify-content: flex-start; padding: 8px 8px 0; }
.ledger-tab { padding: 5px 13px; font-size: 11.5px; }
}
/* ============ 贡献者分析 ============ */
.c-stats { margin: 8px 0 14px; }
.share-card { margin: 6px 0 18px; }
.share-label { font-size: 11px; color: var(--text-dim); margin-bottom: 8px; letter-spacing: .05em; }
.share-bar { display: flex; height: 22px; border-radius: 6px; overflow: hidden; background: var(--paper-3); }
.share-seg { height: 100%; cursor: pointer; transition: filter .15s; }
.share-seg:hover { filter: brightness(1.25); }
.rows { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.c-row {
display: grid; grid-template-columns: 28px 42px 1fr auto 90px; grid-template-rows: auto 6px; align-items: center; column-gap: 10px;
padding: 7px 12px; border-radius: 8px; background: var(--surface-2);
border: 1px solid var(--border); cursor: pointer; opacity: 0;
transition: border-color .15s, transform .15s;
}
.c-row:hover { border-color: var(--border-strong); transform: translateX(2px); }
.c-row:hover .c-rank { color: var(--accent); }
[data-reveal].is-on .c-row { animation: stat-in .45s var(--ease-spring) both; animation-delay: calc(var(--i) * 60ms); }
.c-rank { font-family: var(--mono); font-size: 11px; color: var(--text-dim); text-align: right; }
.c-avatar {
width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 700; color: #fff;
box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.14);
}
.c-name { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; min-width: 0; }
.c-name .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.c-name .auth { color: var(--text-dim); font-size: 11px; font-weight: 400; }
.c-meta { font-family: var(--mono); font-size: 11px; color: var(--text-muted); text-align: right; }
.c-bar { height: 5px; border-radius: 3px; background: var(--paper-3); overflow: hidden; }
.c-bar i { display: block; height: 100%; border-radius: 3px; transform: scaleX(0); transform-origin: left; }
[data-reveal].is-on .c-bar i { animation: grow-x .7s var(--ease-spring) both; animation-delay: calc(var(--i) * 80ms + .2s); }
@keyframes grow-x { to { transform: scaleX(1); } }
.grade-badge {
display: inline-block; min-width: 26px; text-align: center; padding: 2px 6px; border-radius: 5px;
font-size: 10px; font-weight: 700; font-family: var(--mono); line-height: 1.4;
border: 1px solid; cursor: help;
}
.grade-god { color: var(--accent); border-color: var(--accent); background: linear-gradient(135deg, rgba(217,119,87,.28), rgba(217,119,87,.12)); }
.grade-s { color: var(--accent); border-color: rgba(217,119,87,.5); box-shadow: 0 0 6px rgba(217,119,87,.18); }
.grade-splus { color: var(--accent); border-color: var(--accent); box-shadow: 0 0 10px var(--accent-soft); }
.grade-s { box-shadow: 0 0 6px rgba(217,119,87,.18); }
.grade-a { color: var(--info); border-color: rgba(106,155,204,.5); }
.grade-b { color: var(--text-muted); border-color: var(--border-strong); }
.longtail { margin-top: 12px; border: 1px solid var(--border); border-radius: 8px; padding: 8px 12px; background: var(--surface-2); }
.longtail summary { cursor: pointer; font-size: 12px; color: var(--text-muted); list-style: none; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.longtail summary::-webkit-details-marker { display: none; }
.longtail summary::before { content: '▸'; color: var(--accent); transition: transform .2s; }
.longtail[open] summary::before { transform: rotate(90deg); }
.lt-avatar { width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; color: #fff; margin-left: -6px; }
.lt-avatar:first-of-type { margin-left: 0; }
.longtail .lt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 6px; margin-top: 10px; }
.lt-chip { font-size: 11px; color: var(--ink-soft); background: var(--paper-3); border-radius: 6px; padding: 4px 8px; }
.c-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-top: 18px; }
.c-card {
background: var(--surface-2); border: 1px solid var(--border); border-left: 3px solid var(--accent);
border-radius: var(--radius-sm); padding: 14px; opacity: 0; cursor: pointer;
transition: transform .2s var(--ease-spring), border-color .2s;
}
.c-card:hover { transform: translateY(-2px); border-color: var(--border-strong); }
[data-reveal].is-on .c-card { animation: stat-in .5s var(--ease-spring) both; animation-delay: calc(var(--i) * 100ms); }
.c-card.blue { border-left-color: var(--info); } .c-card.gold { border-left-color: var(--warn); } .c-card.green { border-left-color: var(--success); }
.c-card .cc-top { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.c-card .cc-name { font-size: 13px; font-weight: 700; }
.c-card .cc-nums { font-family: var(--mono); font-size: 10px; color: var(--text-muted); margin-bottom: 6px; }
.c-card .cc-sum { font-size: 11.5px; color: var(--ink-soft); line-height: 1.6; }
.c-card .cc-themes { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 8px; }
.cc-chip { font-size: 10px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--text-muted); }
/* ============ Git 树 ============ */
.tree-list-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
.tree-list-head select, .tree-list-head input {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px;
color: var(--text-muted); font-size: 11px; padding: 4px 8px; outline: none; font-family: var(--mono);
}
.tree-list-head select:focus, .tree-list-head input:focus { border-color: var(--accent); }
.tree-list-head input { flex: 1; min-width: 140px; }
#list-count { font-family: var(--mono); font-size: 10px; color: var(--text-dim); white-space: nowrap; }
.tree-list { max-height: 260px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); }
.tree-list::-webkit-scrollbar { width: 6px; }
.tree-list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
.tl-row {
display: grid; grid-template-columns: 60px 1fr auto; gap: 8px; align-items: center;
padding: 5px 10px; font-size: 11px; cursor: pointer; border-bottom: 1px solid rgba(255,255,255,.04);
transition: background .12s;
}
.tl-row:last-child { border-bottom: none; }
.tl-row:hover { background: rgba(217,119,87,.08); }
.tl-row .h { font-family: var(--mono); color: var(--text-dim); }
.tl-row .s { color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-row .a { font-family: var(--mono); font-size: 10px; color: var(--text-dim); }
.tl-row .k { font-size: 9px; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--border); color: var(--text-dim); }
.tl-row .k.merge { color: var(--accent); border-color: rgba(217,119,87,.4); }
.tl-row .k.pr { color: var(--warn); border-color: rgba(217,162,87,.4); }
.tree-node.hl { filter: drop-shadow(0 0 8px rgba(245,197,24,.8)); }
/* ============ Git 树 ============ */
.tree-controls { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.tree-filters { display: flex; gap: 12px; font-size: 11px; color: var(--text-muted); }
.tree-filters label { display: flex; align-items: center; gap: 4px; cursor: pointer; user-select: none; }
.tree-filters input { accent-color: var(--accent); cursor: pointer; }
.tree-zoom { display: flex; align-items: center; gap: 4px; }
.tree-zoom button, #time-play {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px;
color: var(--text-muted); font-size: 12px; min-width: 26px; height: 24px;
cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all .15s;
}
.tree-zoom button:hover, #time-play:hover { color: var(--ink); border-color: var(--border-strong); }
.tree-zoom button.is-active { background: var(--accent); color: #0a0a0a; }
#zoom-val { font-family: var(--mono); font-size: 11px; color: var(--text-muted); min-width: 40px; text-align: center; }
.tree-time { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
#time-slider { flex: 1; accent-color: var(--accent); cursor: pointer; }
#time-label { font-family: var(--mono); font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.tree-scroll.tree-zoomed svg { transform-origin: left top; }
/* ============ Git 树 ============ */
.tree-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.tree-head h2 { margin-bottom: 0; }
.tree-tools { display: flex; gap: 4px; }
.tree-tools button {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px;
color: var(--text-muted); font-size: 11px; padding: 3px 10px; cursor: pointer;
font-family: var(--mono); transition: all .15s;
}
.tree-tools button:hover { color: var(--ink); border-color: var(--border-strong); }
.tree-tools button.is-active { background: var(--accent); color: #0a0a0a; border-color: var(--accent); }
.tree-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11px; color: var(--text-muted); margin-bottom: 10px; }
.tree-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tree-scroll.scrollable svg { width: 3200px; max-width: none; }
.tree-scroll:not(.scrollable) svg { width: 100%; }
.tree-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 4px; vertical-align: -1px; }
.tree-line { cursor: pointer; }
.tree-node { cursor: pointer; }
@media (max-width: 720px) { .c-row { grid-template-columns: 22px 30px 1fr auto; } .c-meta { display: none; } }
</style>
</head>
<body>
<div class="bg" aria-hidden="true">
<div class="bg-grid"></div>
<div class="bg-orb a"></div>
<div class="bg-orb b"></div>
<canvas id="bg-canvas"></canvas>
<div class="bg-quotes" aria-hidden="true"></div>
</div>
<nav class="ledger-nav" id="ledgerNav" aria-label="章节导航">
<div class="ledger-nav-inner">
<a class="ledger-tab" href="#stats" data-i18n="nav_overview">总览</a>
<a class="ledger-tab" href="#months" data-i18n="nav_rhythm">节奏</a>
<a class="ledger-tab" href="#kinds" data-i18n="nav_components">构成</a>
<a class="ledger-tab" href="#contributors" data-i18n="nav_contributors">贡献者</a>
<a class="ledger-tab" href="#gittree" data-i18n="nav_gittree">Git 树</a>
<a class="ledger-tab" href="#tl" data-i18n="nav_selfharm">自伤</a>
<select class="lang-select" id="langSelect" aria-label="Language">
<option value="zh">中</option>
<option value="en">EN</option>
<option value="zh-TW">繁</option>
<option value="ja">日</option>
<option value="ko">한</option>
<option value="fr">FR</option>
<option value="de">DE</option>
<option value="es">ES</option>
</select>
</div>
</nav>
<div class="wrap">
<header class="hero">
<div class="logo-badge">
<svg viewBox="0 0 128 128" width="34" height="34" aria-hidden="true">
<defs>
<linearGradient id="lg-gold" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#f5c518"/>
<stop offset="100%" stop-color="#d99e0b"/>
</linearGradient>
</defs>
<path d="M22 40 L42 90 L60 40 L78 90 L96 40" fill="none" stroke="url(#lg-gold)" stroke-width="13" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="96" cy="20" r="4.5" fill="#10a37f"/>
<circle cx="112" cy="20" r="4.5" fill="#d97757"/>
<circle cx="112" cy="36" r="4.5" fill="#4285f4"/>
</svg>
</div>
<h1 class="hero-title" data-i18n="hero_title" data-i18n-html="1" data-text="WindsurfAPI · 全局<span class='accent'>历史账本</span>">WindsurfAPI · 全局<span class="accent">历史账本</span></h1>
<p class="hero-sub" data-i18n="hero_sub" data-text="2026-04-09 → 2026-09-15 · 从单机反代脚本到 2996 星的网关 · 五个月的全部历史">2026-04-09 → 2026-09-15 · 从单机反代脚本到 2996 星的网关 · 五个月的全部历史</p>
<div class="hero-meta">
<span class="badge accent" data-i18n="badge_commits">1387 提交</span>
<span class="badge info" data-i18n="badge_versions">196 版本</span>
<span class="badge success" data-i18n="badge_prs">82 PR</span>
<span class="badge warn" data-i18n="badge_issues">180 issue</span>
<span class="badge error" data-i18n="badge_selfharm">29 自伤</span>
</div>
</header>
<section class="stats" data-reveal id="stats"></section>
<section class="section card" data-reveal>
<h2 data-i18n="months_title">月度提交节奏</h2>
<div class="sub" data-i18n="months_sub">4 月成型冲刺(339)· 5 月停滞深水(98)· 6 月换轨(141)· 7-8 月高速期(328/412)· 9 月收口(69)—— 悬停柱子看详情,点击锁定可点链接</div>
<svg id="months" viewBox="0 0 1000 240"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="weeks_title">每周脉搏</h2>
<div class="sub" data-i18n="weeks_sub">W17(04-21 前后)提交洪峰 229 条 · W20-W21(5 月中旬)空窗 · W32(08-04)爆发 247 条 · W37 审计收口 34 条</div>
<svg id="weeks" viewBox="0 0 1000 290"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="kinds_title">提交类型构成</h2>
<div class="sub" data-i18n="kinds_sub">fix 占 40% —— 这个项目修 bug 的时间比写功能多</div>
<svg id="kinds" viewBox="0 0 1000 250"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="days_title">单日峰值 Top 10</h2>
<div class="sub" data-i18n="days_sub">08-04 单日 81 条(v3.9.10→v3.9.14 五连发)· 全史最短发版间隔 08-05 v3.9.16→v3.9.17 仅 14 分 49 秒</div>
<svg id="days" viewBox="0 0 1000 290"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="hours_title">一天 24 小时</h2>
<div class="sub" data-i18n="hours_sub">凌晨 2 点还在提交 —— 该项目没有作息表</div>
<svg id="hours" viewBox="0 0 1000 170"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="tags_title">版本节奏</h2>
<div class="sub" data-i18n="tags_sub">196 个 tag:4-5 月 52/52 个(每日一版),8 月 22 个(v3.9.8→v3.9.29),9 月 5 个(v3.9.30→v3.9.34)</div>
<svg id="tags" viewBox="0 0 1000 240"></svg>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="phases_title">四个阶段</h2>
<div class="sub" data-i18n="phases_sub">从脚本到网关的演化轨迹</div>
<div class="phases">
<div class="phase" style="--i:0"><b data-i18n="phase1_t">成型期</b><span class="date">04-09 ~ 04-30</span><small data-i18n="phase1_d">单机脚本→完整网关 · issue 洪峰 82 个 · 98% 解决率 · 单日最高 53 条</small></div>
<div class="phase gold" style="--i:1"><b data-i18n="phase2_t">深水区</b><span class="date">05-01 ~ 06-05</span><small data-i18n="phase2_d">工具兼容/上下文/粘性会话 · 中旬 8 天空窗(离线研究)· #115 六天四稿</small></div>
<div class="phase green" style="--i:2"><b data-i18n="phase3_t">换轨期</b><span class="date">06-05 ~ 06-30</span><small data-i18n="phase3_d">Cascade bridge 爆发 → devin-connect 桥 24 小时诞生 · 探针烧账户事故</small></div>
<div class="phase blue" style="--i:3"><b data-i18n="phase4_t">高速期</b><span class="date">07-01 ~ 09-15</span><small data-i18n="phase4_d">v3.0-v3.9.34 密集发布 · 对抗 review 文化 · OTA 跟 tag · 审计收口</small></div>
</div>
</section>
<section class="section card" data-reveal id="contributors">
<h2 data-i18n="contribs_title">贡献者分析</h2>
<div class="sub" data-i18n="contribs_sub">34 位外部贡献者 · 8 位多次贡献者(2+ PR)· 26 位单发过客 —— 悬停排行看详情,点击锁定</div>
<div class="stats c-stats" id="c-stats"></div>
<div class="share-card" id="c-share-wrap"><div class="share-label" data-i18n="share_label">代码归属(合并 PR 行数占比)</div><div class="share-bar" id="c-share"></div></div>
<div class="rows" id="c-rows"></div>
<details class="longtail" id="c-longtail"></details>
<div class="c-cards" id="c-cards"></div>
</section>
<section class="section card" data-reveal id="gittree">
<div class="tree-head">
<h2 data-i18n="tree_title">Git 树形态</h2>
<div class="tree-tools" role="group" aria-label="树布局切换">
<button type="button" data-tree-mode="vertical" class="is-active" title="纵向时间轴" data-i18n="tree_v">竖</button>
<button type="button" data-tree-mode="horizontal" title="横向时间轴" data-i18n="tree_h">横</button>
<button type="button" data-tree-mode="ring" title="环形时间轮" data-i18n="tree_ring">环</button>
<button type="button" class="tree-collapse is-active" title="竖模式收缩/展开">展开</button>
</div>
</div>
<div class="sub" data-i18n="tree_sub">1387 提交 · 66 个合并 · 主干采样节点 —— 悬停节点/线段看详情,点击锁定</div>
<div class="tree-controls">
<div class="tree-filters" id="tree-filters">
<label><input type="checkbox" id="f-node" checked> 周点</label>
<label><input type="checkbox" id="f-tip" checked> 分支标签</label>
<label><input type="checkbox" id="f-tag" checked> tag 标牌</label>
</div>
<div class="tree-zoom">
<button type="button" id="zoom-out" title="缩小">−</button>
<span id="zoom-val">100%</span>
<button type="button" id="zoom-in" title="放大">+</button>
<button type="button" id="zoom-reset" title="重置">重置</button>
</div>
</div>
<div class="tree-time">
<button type="button" id="time-play" title="播放"><svg viewBox="0 0 12 12" width="10" height="10"><path d="M2 1 L10 6 L2 11 Z" fill="currentColor"/></svg></button>
<input type="range" id="time-slider" min="0" max="1000" value="1000" aria-label="时间游标">
<span id="time-label"></span>
</div>
<div class="tree-list-head">
<select id="list-type" aria-label="类型筛选">
<option value="">全部</option>
<option value="merge">merge</option>
<option value="pr">PR</option>
<option value="tag">tag</option>
<option value="fix">修复</option>
<option value="feat">功能</option>
<option value="docs">文档</option>
<option value="test">测试</option>
</select>
<input type="text" id="list-q" placeholder="搜索提交…" aria-label="搜索">
<span id="list-count"></span>
</div>
<div class="tree-list" id="tree-list"></div>
<div class="tree-legend" id="tree-legend"></div>
<div class="tree-scroll" id="tree-scroll"><svg id="tree" viewBox="0 0 1000 4200"></svg></div>
</section>
<section class="section card" data-reveal>
<h2 data-i18n="tl_title">自伤与返工(29 起,按时间)</h2>
<div class="sub" data-i18n="tl_sub">红 = 事故级(烧钱/安全/生产挂)· 橙 = 合并后自毁/返工 · 悬停节点看详情(AI 总结 + 账本/Git 链接),点击锁定浮窗</div>
<svg id="tl" viewBox="0 0 1000 240"></svg>
</section>
<div class="esc-hint" data-i18n="esc_hint">ESC 切换纯净字幕视图</div>
<section class="section card" data-reveal id="stars">
<h2>Star History</h2>
<div class="sub">2,996 星 · 五个月从 0 到近三千 —— 每天都有新面孔点下那颗星</div>
<img src="assets/star-history.svg" alt="Star History" style="width:100%;border-radius:8px" />
</section>
<footer class="foot">
<span data-i18n="foot_1">数据源:git log 全量 1387 条(数字跟到 v3.9.34 / 2026-09-15)· 逐条账见 <code>docs/HISTORY-LEDGER-2026-*.md</code>(正文仍截在 08-11)· 总账</span> <a href="https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md">HISTORY-LEDGER.md</a><br>
<span data-i18n="foot_2">零依赖纯原生 · 设计 DNA 取自仓库自带 Dashboard · 每个 hash 都可 <code>git show</code> 验证</span>
</footer>
</div>
<div id="tl-tip"></div>
<script>
(() => {
'use strict';
/* ============ 环境检测(三层兜底第二、三层) ============ */
const REDUCED = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)').matches : false;
const HAS_RAF = 'requestAnimationFrame' in window;
const HAS_IO = 'IntersectionObserver' in window;
/* ============ 数据 ============ */
const D = {
months: [
{m:"2026-04", v:339, label:"成型冲刺",
desc:"单机脚本→完整网关的冲刺月:/v1/messages、模型 tier、Dashboard 雏形;issue 洪峰 82 个、98% 解决率;单日最高 53 条。",
link:"https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md"},
{m:"2026-05", v:98, label:"停滞深水",
desc:"社区 issue 洪峰但提交骤降;中旬 8 天空窗(疑似离线研究);#115 六天四稿的方言战;NLU 七连 hotfix。",
link:"https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md"},
{m:"2026-06", v:141, label:"换轨",
desc:"Cascade native bridge 爆发 → 6-30 devin-connect 桥 24 小时诞生;探针烧账户事故、cutover 双 P0。",
link:"https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md"},
{m:"2026-07", v:328, label:"高速",
desc:"v3.0-v3.9 密集发布;7-27 对抗 review 风暴日(v3.9.0 当天被打穿);审计台账建立;gpt-5.6-sol 烧账号。",
link:"https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md"},
{m:"2026-08", v:412, label:"高速",
desc:"会话保真三线落地;OTA 跟 annotated tag;#257 令牌分类;#258 旱灾选择器;发版 v3.9.8→v3.9.29。",
link:"https://github.com/dwgx/WindsurfAPI/blob/master/docs/HISTORY-LEDGER.md"},
{m:"2026-09", v:69, label:"收口",
desc:"审计根因 --test-force-exit;合 #261/#264/#265/#266/#270;发 v3.9.30→v3.9.34;Pages 文案与 fail-closed 对齐。",
link:"https://github.com/dwgx/WindsurfAPI/releases/tag/v3.9.34"}
],
weeks: [
{w:"W15",v:15},{w:"W16",v:8},{w:"W17",v:233,note:"洪峰",desc:"issue 洪峰驱动的一周:04-20~26 提交 233 条、当周 issue 报告 50 个(4 月全月 82 个的一半以上)。"},
{w:"W18",v:127},{w:"W19",v:29},{w:"W20",v:7,note:"空窗",desc:"5 月中旬八天空窗(疑似离线研究期),研究线在 6-05 总兑现。"},
{w:"W21",v:4,note:"空窗",desc:"继续空窗;下一周起 sticky 深水区自查风暴。"},
{w:"W22",v:14},{w:"W23",v:68},{w:"W24",v:6},{w:"W25",v:2,note:"空窗",desc:"5-30~6-02 四天空窗。"},
{w:"W26",v:12},{w:"W27",v:91},{w:"W28",v:136},{w:"W29",v:51},{w:"W30",v:40},{w:"W31",v:66},
{w:"W32",v:247,note:"爆发",desc:"08-04 前后功能爆发:v3.9.10→v3.9.14 五连发、会话保真三线、eleven defects 大 patch。"},
{w:"W33",v:112,note:"账本",desc:"可视化账本收尾 + 工具族作战地图 + OTA 跟 tag(v3.9.22)。"},
{w:"W34",v:33},{w:"W35",v:16,note:"v3.9.29",desc:"#257 令牌分类、#258 旱灾选择器、dashboard 401 空表,发到 v3.9.29。"},
{w:"W36",v:10,note:"v3.9.30",desc:"v3.9.30/31:/v1/models 列出 glm-5.2;Connect 名单 glm-5.1/5.2 当一条。"},
{w:"W37",v:34,note:"审计",desc:"--test-force-exit 根因;合 #261;P2/P3/L3;发 v3.9.32/33。"},
{w:"W38",v:26,note:"v3.9.34",desc:"合 #270/#265/#266/#264;发 v3.9.34;Pages fail-closed;OPEN PR 清零。"}
],
kinds: [
{k:"fix",v:539,desc:"修 bug 占 39%——这个项目修 bug 的时间比写功能多。539 个修复提交贯穿五个月,含大量当晚修 issue 的记录。"},
{k:"feat",v:228,desc:"功能:devin-connect 桥、会话保真三线、/v1/responses、Dashboard、模型同步、swe-2 目录。"},
{k:"docs",v:135,desc:"文档:8 月补课轮集中爆发(ENV-SWITCHES 五批、CHANGELOG、README 中英对齐、审计台账);9 月 Pages fail-closed。"},
{k:"test",v:120,desc:"测试:守卫文化成型——突变验证 49 specs / 588 突变、docs 一致性守卫、#270 合并守卫钉。"},
{k:"chore",v:136,desc:"杂务:版本号、依赖、工具链。"},
{k:"other",v:77,desc:"其他。"},
{k:"merge",v:59,desc:"合并:外部 PR 合并 + 内部分支合并。"},
{k:"release",v:48,desc:"发版:196 个 tag、release notes 185 份、4-5 月每日一版。"},
{k:"refactor",v:14,desc:"重构。"},
{k:"ci",v:11,desc:"CI:node24、pre-commit hook、版本守卫。"},
{k:"revert",v:8,desc:"回退:含 04-23 三连 revert、sticky 当天 revert 等自伤收尾。"},
{k:"perf",v:9,desc:"性能:含工具前言阶梯不再构造注定丢弃的那一档。"},
{k:"style",v:2,desc:"样式。"},
{k:"build",v:1,desc:"构建。"}
],
days: [
{d:"08-04",v:81,desc:"单日峰值:v3.9.10→v3.9.14 五连发、会话保真三线、eleven defects 大 patch。"},
{d:"08-12",v:56,desc:"可视化树收到 1a4fbcce + 工具族作战地图 P1/P3/P4。"},
{d:"04-24",v:53,desc:"v2.0 革新日:测试 + connect-rpc + 8 issue(#42 云候选、Retry-After、TOOL_PARSE_MODE)。"},
{d:"04-23",v:51,desc:"三连 revert 同日:三个「聪明改动」全踩中自己记录的「不能改」。"},
{d:"08-10",v:46,desc:"文档补课收尾:ENV-SWITCHES、守卫盲点四连补、PR #249/#252 合并后修补。"},
{d:"08-05",v:45,desc:"v3.9.15→v3.9.19 五连发;rescue digest 钳两个半边;#241 合并。"},
{d:"04-21",v:37,desc:"issue 洪峰日:#9-#22 一天 14 个 + 外部 PR #13 + 两轮安全审计同日叠加。"},
{d:"08-03",v:34,desc:"v3.9.8/9 双发;干旱门禁结构性不可达修复。"},
{d:"07-08",v:34,desc:"v3.0 发布日后的高速修补日。"},
{d:"08-06",v:34,desc:"v3.9.x 中段:守卫与发版同一天叠。"}
],
hours: [
{h:"00",v:70,desc:"午夜还在写代码。"},{h:"01",v:58,desc:"凌晨 1 点。"},{h:"02",v:56,desc:"凌晨 2 点还在提交——该项目没有作息表。"},
{h:"03",v:70,desc:"凌晨 3 点(发版高峰时段)。"},{h:"04",v:36,desc:"凌晨 4 点(最低谷)。"},{h:"05",v:53,desc:"凌晨 5 点。"},
{h:"06",v:51,desc:"清晨 6 点。"},{h:"07",v:60,desc:"早上 7 点。"},{h:"08",v:52,desc:"早上 8 点。"},
{h:"09",v:43,desc:"上午 9 点(最低谷之一)。"},{h:"10",v:50,desc:"上午 10 点。"},{h:"11",v:49,desc:"上午 11 点。"},
{h:"12",v:53,desc:"中午 12 点。"},{h:"13",v:47,desc:"下午 1 点。"},{h:"14",v:55,desc:"下午 2 点。"},
{h:"15",v:49,desc:"下午 3 点。"},{h:"16",v:72,desc:"下午 4 点。"},{h:"17",v:62,desc:"下午 5 点。"},
{h:"18",v:60,desc:"晚上 6 点。"},{h:"19",v:77,desc:"晚上 7 点(全天峰值)。"},{h:"20",v:76,desc:"晚上 8 点。"},
{h:"21",v:55,desc:"晚上 9 点。"},{h:"22",v:61,desc:"晚上 10 点。"},{h:"23",v:72,desc:"晚上 11 点。"}
],
tags: [
{t:"2026-04", v:52, desc:"每日一版的节奏:4 月 52 个 tag(v1.4.0→v2.0.x 早期),成型期高频发版。"},
{t:"2026-05", v:52, desc:"5 月 52 个 tag(v2.0.45→v2.0.97),社区洪峰驱动,NLU 七连 hotfix 都在这个月。"},
{t:"2026-06", v:24, desc:"6 月 24 个(v2.0.98→v2.0.146 隐式内嵌 + devin-connect 桥),发版节奏放缓但桥诞生。"},
{t:"2026-07", v:41, desc:"7 月 41 个(v3.0.0→v3.9.6),v3.0 发布当天三连版、7-27 一天 4 版。"},
{t:"2026-08", v:22, desc:"8 月 22 个(v3.9.8→v3.9.29),前 6 天 14 版,后半月 8 版收到 v3.9.29。"},
{t:"2026-09", v:5, desc:"9 月 5 个(v3.9.30→v3.9.34):glm-5.2 目录、审计收口、#270/#265/#266/#264 进 OTA。"}
],
selfharm: [
{d:"04-17", t:"PR #1 撞车+致谢反转", lv:"m", h:"4ff1baf",
desc:"同一 commit 走两遍 PR 流程(merge 第二父就是作者自己的 commit);随后 credits 移除又 revert,对外部贡献的致谢决策反复横跳。"},
{d:"04-21", t:"kimi/qwen 目录三连撤", lv:"m", h:"ef55e7a",
desc:"上游目录没核实就上架。kimi-k2 六分钟内三连改(enum→string→回退);qwen-3-coder 82 秒内三次提交全撤回。"},
{d:"04-22", t:"auth bypass 自己引入", lv:"h", h:"cc60746",
desc:"为前端免密访问加的「空 password header 放行」成为严重 Dashboard auth bypass(同日恢复原逻辑,fail-closed 深化)。"},
{d:"04-23", t:"三连 revert 踩中'不能改'", lv:"m", h:"410d867",
desc:"gRPC 压缩头 / planner READ_ONLY / 版本号——三个「聪明改动」全踩中 dfb979a 当天自己写的「不能改」清单,全部导致 LS proto wire-type 解析错误。"},
{d:"04-24", t:"GRPC_PROTOCOL 生产全挂", lv:"h", h:"4b555bb",
desc:"v2.0.19 把默认 legacy→Connect,v2.0.21 生产全挂(cascade_id 空串)回退。作者写了 postmortem:单测只验 const 取值,没起 fake LS 端到端验 Connect framing。"},
{d:"05-02", t:"#115 根因误判六天四稿", lv:"m", h:"1026cd3",
desc:"自称 root cause 是 gpt_native 方言 → 当天修的是自己引入的 body is not defined → 三稿「真修」→ 5-03 承认翻方向 → 换 NLU 层 → 6-05 以带遥测+测试+canary 的形态终章。"},
{d:"05-03", t:"NLU 七连 hotfix", lv:"m", h:"a4216be",
desc:"NLU 协议转换层引入后同一层一周 8 个补丁(allThinking 通道漏、Claude 工具卡死回归、误抠 placeholder、入口扩宽…),5-07 仍在打补丁。"},
{d:"05-04", t:"#129 wnfilm 回归三连", lv:"m", h:"80d6079",
desc:"2.0.85 上 fallback 直接打挂 wnfilm 用户 → 86 紧急默认 OFF → 87 真修(fingerprint 锁 modelKey 双挂)后才重开。"},
{d:"05-25", t:"sticky 自查风暴", lv:"m", h:"626f71b",
desc:"5-12 引入 #162,5-25 一天 11 条 debug+修复(双重打标、body.user 优先级、流式漏传 callerKey、dashboard 401)——自己实现的功能自己发现 bug 自己修。"},
{d:"06-07", t:"假上游冷却", lv:"m", h:"736eefb",
desc:"native bridge 尾巴:上片引入的「假上游冷却」换成真实冷却。"},
{d:"06-29", t:"探针烧账户(最重事故)", lv:"h", h:"21393b9",
desc:"强制探针 Step 2/3 对每个模型发真实请求,约 12 个模型扫完把活着的免费账户 free→expired——线上事故。修法:计费 canary 改 opt-in,普通探测变零额度。"},
{d:"06-30", t:"cutover 双 P0", lv:"h", h:"27d31de",
desc:"桥上线当天:P0-1 re-login 风暴无节流(force:true 绕过 60s 冷却);P0-2 池耗尽静默降级到无记账的 env token,零 RPM 记账把单账户锤向封禁。"},
{d:"06-30", t:"alias 漂移:要 GPT 拿 SWE", lv:"h", h:"a8091d5",
desc:"catalog alias 未全量解析——客户端按广告发 gpt-5.5/gemini-3.0-flash,selector 未命中静默降级到免费 swe-1-6-slow。要 GPT 拿到 SWE。快照 fixture 同日入库防漂移。"},
{d:"07-04", t:"jimp 违反零依赖铁律", lv:"m", h:"6cce2ae",
desc:"项目核心卖点是零 npm 运行时依赖,引入 jimp 当日 CI 红,次日 vendor 化回收(jpeg-js/png.js,package-lock -796 行)。"},
{d:"07-04", t:"host/key 泄漏进公开仓", lv:"h", h:"413106e",
desc:"历史遗留:README/4 份 release notes/2 份 RE 文档/3 处测试烙有真实上游 host/key。redact 13 文件 + gitignore 屏蔽 + 删 1401 行内部笔记(内部资料反复混入公开仓)。"},
{d:"07-09", t:"v3.0.0 页面 ban 自己", lv:"h", h:"f8a6b95",
desc:"Dashboard 打开即发 ~12 个空密码 401,每个都计入失败——页面一加载自己的 IP 就被 ban。v3.0.0 发布当天暴露、v3.0.2 收口。"},
{d:"07-12", t:"gpt-5.6-sol 烧账号", lv:"h", h:"7a8d658",
desc:"live-catalog 把 family alias 也折进 selectors → 原样透传 → UPSTREAM_INTERNAL 烧 homecloud 单账号(真号坐实)。根治:只折 canonical + fail closed,2547 测试绿。"},
{d:"07-13", t:"Windows 热更新致命 bug", lv:"m", h:"9afc1dd",
desc:"v3.2.2 上线的 Windows 脚本套件热更新退出码致命(stop.ps1 杀不掉 supervisor 副 bug),v3.2.6 当天 6 个 ps1 改 114 行收掉。"},
{d:"07-27", t:"v3.9.0 当天被打穿", lv:"h", h:"2ced8a2",
desc:"对抗复核第二轮:1 blocker + 4 major 全部是 30485c3 上一批修复自己引入的(response-store 重写区 167 行 diff)。当天自伤-自愈闭环。"},
{d:"07-28", t:"v3.9.2 只修了 1/3", lv:"m", h:"161c88d",
desc:"合成终止帧漏洞只修了 responses 路由,messages/gemini 两条路由两天后补全——v3.9.2 发布说明自己承认「只修了 1/3」。"},
{d:"07-29", t:"检索端点 8 小时不可用", lv:"m", h:"4a4653d",
desc:"GET/DELETE /v1/responses/{id} 自 v3.9.1 发布起对所有客户端不可用,8 小时后才修(v3.9.4)。测试曾把 bug 固化成契约。"},
{d:"07-31", t:"8 条假测试清理", lv:"m", h:"50875f5",
desc:"「修 5 条自己写的假测试——源码 grep 冒充行为守卫 + 条件断言 + 恒真断言」。7-27 起累计清理 8 条自产假测试。"},
{d:"08-04", t:"sticky 修复当天 revert", lv:"m", h:"0f1d4f0",
desc:"60 秒 RPM 抖动永久迁走会话的修复当天就被 revert——实测「保留绑定会散射,比清绑定更贵」,结论反转(v3.9.13 notes 写明)。"},
{d:"08-04", t:"守卫三连崩", lv:"m", h:"a98a572",
desc:"默认开开关发现机制三连漏写法(某赋值形式→??→=== '0'),每次修都挖出更多未登记开关(累计 8 个,含凭证开关对守卫隐形)。"},
{d:"08-07", t:"SWE kimi 方言过度推广", lv:"m", h:"898af0f",
desc:"SWE 全家路由到 kimi_k2 方言 → 两天后排除 swe lightning(6cc2886)——过度推广的修正。"},
{d:"08-10", t:"#252 两处互相抵消", lv:"h", h:"0523910",
desc:"抑制逻辑靠检测 <tool_call> 标记触发,同一 PR 把默认值里的标记删了——抑制变永久 no-op,合并时守卫与审查都没发现,合并当天发现并修。"},
{d:"08-10", t:"'86/86 收齐'被推翻", lv:"m", h:"4ada929",
desc:"「35/35」「86/86 收齐」声明被 4ada929 一次补 47 个开关推翻(含全部 5 个 wire 坐标);6202c34 修正则后又补 2 个,数字修正链 156→157→158。"},
{d:"08-11", t:"账本自己踩 sk- 坑", lv:"m", h:"2c90318",
desc:"账本描述 f3e0094(sk- 前缀误报修复)时原文引用了夹具 key 形态,触发 secret-scan 导致 CI 红——「审计工具与被审对象共享盲点」的第三个实例。"},
{d:"09-13", t:"--test-force-exit 绿门说谎", lv:"h", h:"939872b",
desc:"npm test 带 --test-force-exit 会在部分 suite 后以 exit 0 结束。同字节 12 次得到 8 个不同计数;CI 全绿不等于测试跑完。突变基线拿这个数写 ⇒ guard 5 随机跳闸。仓库五处文档把它写成输出竞态。已从 package.json / shard / 三个 harness 摘掉,禁止加回。"}
]
};
/* ============ 新增数据:Git 树 + 贡献者 ============ */
const D_GIT = {"t0":1775727718,"t1":1789432557,"anchors":[{"h":"395311f2","t":1775727718,"a":"dwgx","s":"Initial commit: Windsurf-to-OpenAI compatible API proxy","k":"root","p":[],"spine":1},{"h":"17b8c27e","t":1775943856,"a":"dwgx","s":"用真实 token usage 替换 chars/4 估算 + sanitize/tool-emulation 上架","k":"node","p":["05e85192"],"spine":1},{"h":"4ff1baff","t":1776683773,"a":"dwgx","s":"Merge pull request #1 from dd373156/fix-opus-4-7-tier-access","k":"merge","p":["7411d4cc","b721201b"],"spine":1},{"h":"6d734d9c","t":1776697276,"a":"dwgx","s":"fix: self-update uses process.exit + PM2 autorestart instead of spawn","k":"node","p":["b09abf8a"],"spine":1},{"h":"5c8ba744","t":1776765005,"a":"dwgx","s":"fix: merge 8 fixes from PR #13,补 XSS 转义遗漏处","k":"pr","p":["f058a5e5"],"spine":1},{"h":"2edbf386","t":1776766823,"a":"dwgx","s":"feat: 加致谢面板 + README 贡献者段","k":"node","p":["13c72a09"],"spine":1},{"h":"60e6f15e","t":1776768130,"a":"dwgx","s":"fix: 补 PR #13 + Antigravity 遗漏的 3 项","k":"pr","p":["3760e507"],"spine":1},{"h":"e3271de6","t":1776779304,"a":"dwgx","s":"feat(#18): 启动时自动装 LS binary 无需用户手动操作","k":"node","p":["a6376f83"],"spine":1},{"h":"6002cc6d","t":1776826592,"a":"dwgx","s":"Merge pull request #20 from motto1/fix/dashboard-windsurf-login-auth1-batch","k":"merge","p":["f9678ae3","fb65064d"],"spine":1},{"h":"d1d628c6","t":1776827453,"a":"dwgx","s":"feat(#19): install-ls.sh + config.js 兼容 macOS","k":"node","p":["e4fed17b"],"spine":1},{"h":"2721cce4","t":1776829039,"a":"dwgx","s":"chore: bump version to 1.4.0","k":"tag","p":["72bd2ed3"],"spine":1},{"h":"37d42e65","t":1776837922,"a":"dwgx","s":"fix(#24): reuse 模式也发完整历史 不再只发最后一条","k":"node","p":["ba53e926"],"spine":1},{"h":"25332ffd","t":1776840140,"a":"Bad Child","s":"Merge branch 'dwgx:master' into master","k":"merge","p":["28174cf9","06c2643f"],"spine":0},{"h":"9f1de4b1","t":1776843400,"a":"dwgx","s":"Merge pull request #26 from youfak/master","k":"merge","p":["4fce358a","25332ffd"],"spine":0},{"h":"d3134764","t":1776843406,"a":"dwgx","s":"Merge branch 'master' of https://github.com/dwgx/WindsurfAPI","k":"merge","p":["3ef2061b","9f1de4b1"],"spine":1},{"h":"621888e4","t":1776883457,"a":"dwgx","s":"feat(#34): 账号余额显示日额度/周额度/Prompt 三条进度条","k":"node","p":["cac0b8d1"],"spine":1},{"h":"89512bf5","t":1776926172,"a":"dwgx","s":"Merge pull request #36 from baily-zhang/fix-claude-code-context-reuse","k":"merge","p":["4b6cde8e","66897aab"],"spine":1},{"h":"d200d9f5","t":1776953265,"a":"dwgx","s":"docs: 添加 v1.9.5 代码深度分析审计报告","k":"node","p":["2d6d2c68"],"spine":1},{"h":"740a5a4d","t":1776990112,"a":"Stefan Meinecke","s":"Merge branch 'dwgx:master' into upd/english-translation","k":"merge","p":["2530e946","cd8f6b42"],"spine":0},{"h":"c8a86c5e","t":1777002667,"a":"dwgx","s":"Merge pull request #44 from aict666/fix/preserve-authoritative-tier","k":"merge","p":["cd8f6b42","c32d4d7c"],"spine":1},{"h":"9b3ef694","t":1777002682,"a":"dwgx","s":"Merge pull request #45 from baily-zhang/fix-cascade-reuse-offsets","k":"merge","p":["c8a86c5e","20b3f1b8"],"spine":1},{"h":"ff539c76","t":1777002690,"a":"dwgx","s":"Merge pull request #43 from smeinecke/upd/english-translation","k":"merge","p":["9b3ef694","a4422cc2"],"spine":1},{"h":"ac664dc4","t":1777018211,"a":"dwgx","s":"feat(dashboard): 4-type chart switcher + model pie + quota column i18n fix","k":"node","p":["50a4a5e1"],"spine":1},{"h":"2ae9d158","t":1777032049,"a":"aict666","s":"Merge PR #50 — disable Cascade reuse for tool-emulated turns","k":"pr","p":["1e1d9230"],"spine":1},{"h":"3792678e","t":1777044262,"a":"dwgx","s":"fix: lenient JSON parse for <tool_call> bodies (small-model trailing-brace)","k":"node","p":["4aa4d7bf"],"spine":1},{"h":"ca770ab3","t":1777049036,"a":"dwgx","s":"Merge pull request #51 from aict666/fix/tool-preamble-injection-guard","k":"merge","p":["06fb854a","85cc005e"],"spine":1},{"h":"b3af74a1","t":1777054023,"a":"dwgx","s":"fix(#52): correct cascadeConversationReuse copy + weighted credits panel","k":"node","p":["2557fb8b"],"spine":1},{"h":"ae4067c5","t":1777104993,"a":"AI CT","s":"fix: bring Claude Code tool-call flow back to Anthropic-equivalent behaviour (#54)","k":"pr","p":["f53248cf"],"spine":1},{"h":"79cd990a","t":1777124585,"a":"dwgx","s":"fix(#28): reject empty user message + warn on heavy system prompts","k":"node","p":["ae4067c5"],"spine":1},{"h":"e078f35b","t":1777137724,"a":"Baily","s":"fix: prevent Opus 4.7 context bloat (#61)","k":"pr","p":["5a6e7be1"],"spine":1},{"h":"9dc019e3","t":1777137730,"a":"abwuge","s":"fix: resolve nginx shared memory error and missing join import (#58)","k":"pr","p":["e078f35b"],"spine":1},{"h":"a71bdd1b","t":1777137835,"a":"dwgx","s":"feat(dashboard): add PR #61 #58 #54 #53 to credits panel; promote baily-zhang to S+","k":"pr","p":["9dc019e3"],"spine":1},{"h":"fd34859b","t":1777139233,"a":"dwgx","s":"fix(#59): extend Opus tool-emulated cascade reuse to 4.6 (rename helper)","k":"node","p":["39177e02"],"spine":1},{"h":"4d51b5a9","t":1777140721,"a":"Baily","s":"fix(dashboard): prevent credits syntax crash (#62)","k":"pr","p":["37f0ce18"],"spine":1},{"h":"9c6b6855","t":1777141423,"a":"dwgx","s":"feat(#56,#63): implement /v1/responses endpoint for Codex CLI compatibility","k":"node","p":["4d51b5a9"],"spine":1},{"h":"b5a88569","t":1777141863,"a":"dwgx","s":"chore: bump 2.0.5 + add baily-zhang PR #62 to credits","k":"pr","p":["9c6b6855"],"spine":1},{"h":"a434c400","t":1777144501,"a":"dwgx","s":"fix(#57,#59): harden stream reuse and security edges","k":"node","p":["b5a88569"],"spine":1},{"h":"54e78ad7","t":1777150961,"a":"Baily","s":"fix(#64): recover from canceled cascade panel transport","k":"pr","p":["a434c400"],"spine":1},{"h":"60eab58d","t":1777150993,"a":"abwuge","s":"ci(#65): add release workflow with GHCR + GitHub Release","k":"pr","p":["54e78ad7"],"spine":1},{"h":"4440cfa7","t":1777217602,"a":"dwgx","s":"fix(rate-limit): honor Cascade reset windows","k":"node","p":["072e5e10"],"spine":1},{"h":"5e8c067d","t":1777288335,"a":"dwgx","s":"chore(credits): add sandleft for PR #72 Codex Responses tools layer","k":"pr","p":["efbcf165"],"spine":1},{"h":"e61580e5","t":1777404618,"a":"dwgx","s":"chore(scripts): vps-exec.py 改为 env 驱动,去掉硬编码 SSH 凭据","k":"node","p":["9e6afc21"],"spine":1},{"h":"e2c951b7","t":1777452192,"a":"dwgx","s":"Merge pull request #89 from smeinecke/fix/missing-i18n","k":"merge","p":["41fc303f","850118ec"],"spine":1},{"h":"8d2bfc84","t":1777452232,"a":"dwgx","s":"Merge pull request #88 from smeinecke/feat/allow-private-proxies","k":"merge","p":["e2c951b7","454604ff"],"spine":1},{"h":"b933fb8a","t":1777452674,"a":"dwgx","s":"chore: 2.0.27 — i18n guard 收尾 + PR #88 #89 合并后","k":"pr","p":["8d2bfc84"],"spine":1},{"h":"ce9dfba8","t":1777454266,"a":"dwgx","s":"Merge pull request #90 from smeinecke/feat/add-account-proxy","k":"merge","p":["b933fb8a","a24dbd83"],"spine":1},{"h":"7299d4a0","t":1777455108,"a":"dwgx","s":"feat: 2.0.28 — 本地 Windsurf 凭证导入 (#91) + PR #90 proxy 顺序修正","k":"node","p":["ce9dfba8"],"spine":1},{"h":"4406bd2f","t":1777458583,"a":"dwgx","s":"Merge spark-A: security hardening for local-windsurf import","k":"merge","p":["bb5cc759","810ab0de"],"spine":1},{"h":"1f57babc","t":1777458592,"a":"dwgx","s":"Merge spark-B: 11 new tests + gen-docs idempotency fix","k":"merge","p":["4406bd2f","f6b770a7"],"spine":1},{"h":"92ccf6d8","t":1777458598,"a":"dwgx","s":"Merge spark-C: 3 missing Opus 4.7 aliases + 6 catalog tests","k":"merge","p":["1f57babc","18e9b7b3"],"spine":1},{"h":"1e1f3348","t":1777458749,"a":"dwgx","s":"merge: resolve spark-B/C alias test conflict + regen docs","k":"node","p":["92ccf6d8"],"spine":1},{"h":"1f055a91","t":1777461581,"a":"dwgx","s":"merge: v2.0.31 — GLM/Kimi tool-call dialect (fix #86)","k":"merge","p":["07d3df19","9c2dc30a"],"spine":1},{"h":"5b86ca86","t":1777461675,"a":"dwgx","s":"release: v2.0.31 — GLM/Kimi tool dialect support (#86)","k":"node","p":["1f055a91"],"spine":1},{"h":"946a14ff","t":1777463669,"a":"dwgx","s":"fix: dashboard i18n centralization (PR #92, smeinecke) + Windows path sanitizer (#86 follow-up)","k":"pr","p":["5b86ca86"],"spine":1},{"h":"3ebd6582","t":1777464284,"a":"dwgx","s":"fix(#94): claude-opus-4-7 thinking auto-route disabled","k":"node","p":["946a14ff"],"spine":1},{"h":"6f20680f","t":1777464328,"a":"dwgx","s":"merge: v2.0.32 — Docker self-update graceful unavailable (#87)","k":"merge","p":["3ebd6582","1c40d461"],"spine":1},{"h":"6fe337ee","t":1777464441,"a":"dwgx","s":"release: v2.0.32 — #94 Opus 4.7 thinking + #87 Docker self-update + #86 follow-ups","k":"node","p":["6f20680f"],"spine":1},{"h":"6ab39a44","t":1777475014,"a":"dwgx","s":"merge: #93 cascade reuse for Sonnet 4.6 thinking","k":"merge","p":["6fe337ee","eeff1041"],"spine":1},{"h":"4a882a06","t":1777475019,"a":"dwgx","s":"merge: #96 workspace marker + system-prompt hint","k":"merge","p":["6ab39a44","8539d2e3"],"spine":1},{"h":"8f7a9f5f","t":1777475027,"a":"dwgx","s":"merge: GLM5.1 silence — promote thinking → content for non-reasoning models","k":"merge","p":["4a882a06","09848756"],"spine":1},{"h":"9af5e20d","t":1777475222,"a":"dwgx","s":"release: v2.0.33 — #93 cascade reuse + #96 marker + GLM5.1 silence fallback","k":"node","p":["8f7a9f5f"],"spine":1},{"h":"c5e5be64","t":1777478113,"a":"dwgx","s":"merge: fix tier-unknown 403 race for fresh accounts","k":"merge","p":["9af5e20d","91b24418"],"spine":1},{"h":"8592fb0d","t":1777811188,"a":"dwgx","s":"release: 2.0.75 — Claude 工具卡死回归紧急修 + 登录 cross-host + UI N/A","k":"node","p":["52c75eb0"],"spine":1},{"h":"971eaca1","t":1778554007,"a":"linqicheng","s":"fix: cross-platform language server paths (#175 by @linqichenggg)","k":"pr","p":["c4575934"],"spine":1},{"h":"d72b093b","t":1778554011,"a":"hikki","s":"refactor(dashboard): UI cleanup (#173 by @datfooldive)","k":"pr","p":["971eaca1"],"spine":1},{"h":"549a7757","t":1778554029,"a":"you922","s":"feat: auto-restart crashed LS with exponential backoff (#163 by @you922)","k":"node","p":["d72b093b"],"spine":1},{"h":"06dc8a57","t":1778554226,"a":"dwgx","s":"release: 2.0.95 — merge PRs #162 #163 #173 #175 + dashboard width fix","k":"tag","p":["626f71bf"],"spine":1},{"h":"6b9e6d8e","t":1778662578,"a":"dwgx","s":"fix(#165 #174): inflight stale timeout + LS instance cap — prevent account pool deadlock and memory blowup","k":"node","p":["06dc8a57"],"spine":1},{"h":"80900336","t":1779407993,"a":"lauvww","s":"feat: improve dashboard batch import parsing (#182 by @lauvww)","k":"pr","p":["632afca6"],"spine":1},{"h":"acac78ab","t":1779407996,"a":"ucloudnb666","s":"feat: add Astraflow provider support (#184 by @ucloudnb666)","k":"pr","p":["80900336"],"spine":1},{"h":"a6b79365","t":1779407999,"a":"li shuangqi","s":"feat: Cascade reuse optimization + HTTPS proxy + configurable pool size (#181 by @Fermiz)","k":"pr","p":["acac78ab"],"spine":1},{"h":"54707dd2","t":1779408063,"a":"dwgx","s":"release: 2.0.97","k":"tag","p":["a6b79365"],"spine":1},{"h":"9e1f17f8","t":1780439189,"a":"dwgx","s":"fix: 3 bugs (tool-emulation flush, message ordering, sticky session) + contributors","k":"node","p":["54707dd2"],"spine":1},{"h":"f5425ff2","t":1780439623,"a":"dwgx","s":"fix: sticky session binding breaks on streaming path + multi-user isolation (#188 by @The-five-stooges)","k":"merge","p":["9e1f17f8","c736b6db"],"spine":1},{"h":"6ce85917","t":1780439819,"a":"dwgx","s":"fix(i18n): add missing translations for sticky session dashboard panels","k":"node","p":["f5425ff2"],"spine":1},{"h":"542bb7cd","t":1780439935,"a":"dwgx","s":"chore: add @The-five-stooges to contributors (PR #188)","k":"pr","p":["6ce85917"],"spine":1},{"h":"b4464608","t":1780620116,"a":"dwgx","s":"fix: ship native bridge smoke script in docker","k":"node","p":["71f3f2ca"],"spine":1},{"h":"3e133bbe","t":1780645262,"a":"dwgx","s":"merge pr #192: align ls install sources","k":"merge","p":["8bd4b63c","e5e44423"],"spine":1},{"h":"452871cb","t":1780754741,"a":"dwgx","s":"ci: use bounded release test gate","k":"node","p":["d374b259"],"spine":1},{"h":"7ee509a7","t":1782670594,"a":"Song Jie","s":"fix(tool-emulation): use gpt_native dialect for glm-5.2 (#204)","k":"pr","p":["441baf92"],"spine":1},{"h":"0dc3e232","t":1782671302,"a":"dwgx","s":"test: lock in glm-5.2 gpt_native dialect routing (#204)","k":"pr","p":["7ee509a7"],"spine":1},{"h":"7b816eee","t":1782758955,"a":"dwgx","s":"feat(routing): DEVIN_ONLY kill-switch — force every model onto Devin (Cascade retired)","k":"node","p":["b7c87b62"],"spine":1},{"h":"7f8d4038","t":1782806381,"a":"dwgx","s":"feat(devin-connect): recovery observability + surface rotated cred-key (P2 #36, #37)","k":"pr","p":["060f3f94"],"spine":1},{"h":"413106ec","t":1783135913,"a":"dwgx","s":"docs,test: redact leaked host/key values from tracked files + fix dependency claim","k":"node","p":["4243d936"],"spine":1},{"h":"0c77824f","t":1783181789,"a":"kosonen","s":"fix(tool-emulation): lift tool_calls from thinking when text parser yields none","k":"node","p":["d4c7259d"],"spine":1},{"h":"af0cdab9","t":1783362043,"a":"dwgx","s":"release: v2.0.146 — audit hardening + live-verified model roster","k":"tag","p":["a4d90da1"],"spine":1},{"h":"ea324036","t":1783371145,"a":"dwgx","s":"release: v2.0.147 — tool_calls-in-thinking + opus-4.8 tier ladder + catalog self-heal","k":"tag","p":["5f118e52"],"spine":1},{"h":"1bb1cf27","t":1783470266,"a":"dwgx","s":"feat(dashboard): densify account rows, add settings search and danger confirms","k":"node","p":["482b2cc5"],"spine":1},{"h":"a14c1efa","t":1783544868,"a":"dwgx","s":"release: prepare v3.0.0","k":"tag","p":["78acd1b0"],"spine":1},{"h":"f55ce9f6","t":1783571997,"a":"dwgx","s":"fix(dashboard): stepper position, dotted-key i18n, drop hand-drawn fonts","k":"node","p":["aaf9701b"],"spine":1},{"h":"1616b5eb","t":1783649876,"a":"dwgx","s":"docs: release notes for v3.1.0","k":"tag","p":["45649522"],"spine":1},{"h":"815cf591","t":1783659518,"a":"dwgx","s":"feat(docker): default DEVIN_CONNECT=1 so native tool calling works out of the box (#210)","k":"node","p":["661b6491"],"spine":1},{"h":"4fc3c399","t":1783668818,"a":"dwgx","s":"docs(contributors): credit clarezoe for the catalog-resync fix (#206)","k":"pr","p":["a31afd22"],"spine":1},{"h":"54649136","t":1783719369,"a":"dwgx","s":"feat(a11y): radiogroup/canvas/dialog 语义 + data-i18n-aria-label 运行时","k":"node","p":["a14fb2d4"],"spine":1},{"h":"313bb8ed","t":1783778139,"a":"dwgx","s":"fix(security): #1 dashboard 回退按可信客户端IP判本地 + #7 lockout塌缩告警","k":"pr","p":["dcc2eb6b"],"spine":1},{"h":"4f368d60","t":1783812290,"a":"dwgx","s":"fix(devin-connect): 拆分 idle-TIMEOUT vs absolute-DEADLINE + captureRes 断连短路","k":"node","p":["65240aaf"],"spine":1},{"h":"c40ae89a","t":1783839956,"a":"dwgx","s":"fix(release): esbuild 先打成单 CJS 再 pkg(解 ESM 打包崩溃)","k":"tag","p":["29019112"],"spine":1},{"h":"f2a3f894","t":1783928265,"a":"dwgx","s":"chore(release): v3.2.9","k":"tag","p":["9befb1d5"],"spine":1},{"h":"fac765ac","t":1784102491,"a":"dwgx","s":"docs(credits): 致谢 warelik(#216 MCP-gate + #215 IPv6 ETIMEDOUT)","k":"pr","p":["20476280"],"spine":1},{"h":"28897bd9","t":1784112828,"a":"dwgx","s":"feat(stats): content-policy 拦截可观测 ring buffer(后端)","k":"node","p":["b2988984"],"spine":1},{"h":"81370f55","t":1784155245,"a":"dwgx","s":"chore(release): v3.5.0","k":"tag","p":["24705b58"],"spine":1},{"h":"0b4ffbf2","t":1784177450,"a":"W ARELIK","s":"chore(credits): 为 warelik 贡献记录补 githubId(#216/#215)","k":"pr","p":["81370f55"],"spine":1},{"h":"4a105fd4","t":1784190440,"a":"dwgx","s":"feat(cc-compat): Claude Code 专属可插拔兼容层 /v1/cc/*","k":"node","p":["0b4ffbf2"],"spine":1},{"h":"04c95595","t":1784271595,"a":"dwgx","s":"Merge PR #217: fix(responses): support responses-lite additional_tools","k":"merge","p":["fc25105d","4cf7d1d4"],"spine":1},{"h":"f76fcb26","t":1784271709,"a":"dwgx","s":"fix(responses): 去重按 canonical 序列化比较,修 key 顺序不同误判工具冲突→400","k":"node","p":["04c95595"],"spine":1},{"h":"b8644472","t":1784275838,"a":"dwgx","s":"fix: 按 #217 模式排查修 4 处同类缺陷(默认模型/developer角色/缓存键/schema数组序)","k":"pr","p":["f76fcb26"],"spine":1},{"h":"4824e574","t":1784277158,"a":"dwgx","s":"fix(gemini): 同名并行工具调用 FIFO 配对 + 优先用原生 id,修 tool_result 错配","k":"node","p":["b8644472"],"spine":1},{"h":"175b1dd5","t":1784890986,"a":"dwgx","s":"fix(responses): treat bare {role,content} input items as messages — codex #219","k":"pr","p":["67b83570"],"spine":0},{"h":"f433c85b","t":1784944527,"a":"dwgx","s":"Merge PR #228","k":"merge","p":["4824e574","7d72818c"],"spine":0},{"h":"014ead1c","t":1784944527,"a":"dwgx","s":"Merge PR #224","k":"merge","p":["a335d516","17cdbe56"],"spine":0},{"h":"a335d516","t":1784944527,"a":"dwgx","s":"Merge PR #227","k":"merge","p":["581927c8","4fafb767"],"spine":0},{"h":"581927c8","t":1784944527,"a":"dwgx","s":"Merge PR #229","k":"merge","p":["f433c85b","0de46c0d"],"spine":0},{"h":"1783545b","t":1784944555,"a":"dwgx","s":"Merge PR #225","k":"merge","p":["014ead1c","4895b9f1"],"spine":0},{"h":"f434ef44","t":1784944604,"a":"dwgx","s":"Merge PR #226","k":"merge","p":["1783545b","b0f83305"],"spine":0},{"h":"1be86ec1","t":1784944623,"a":"dwgx","s":"Merge our a7 + tag5 + macOS setup branch","k":"merge","p":["f434ef44","175b1dd5"],"spine":0},{"h":"94356fac","t":1784944977,"a":"dwgx","s":"chore(credits): 致谢 warelik 一轮六连(#224-#229)","k":"pr","p":["1be86ec1"],"spine":0},{"h":"7d3bf3d3","t":1784945238,"a":"dwgx","s":"Merge: warelik #224-#229 + codex content-policy a7 + #220 cache calibration + macOS setup","k":"merge","p":["4824e574","5cf01162"],"spine":1},{"h":"5cf01162","t":1784945238,"a":"dwgx","s":"feat(devin-connect): 默认解码 cache_read_tokens + 致谢 forrinzhao(#219)","k":"pr","p":["94356fac"],"spine":0},{"h":"72a90e72","t":1784965014,"a":"dwgx","s":"fix(chat): model-access 门禁移到后端分支之前 — 修 DEVIN_CONNECT 路径策略绕过","k":"node","p":["21046eb8"],"spine":1},{"h":"d231d44f","t":1785075593,"a":"dwgx","s":"Merge PR #230: fix(chat): DEVIN_CONNECT 路径补上 sticky 绑定 — 缓存亲和此前完全失效","k":"merge","p":["e65a8e91","9b24f981"],"spine":1},{"h":"34198541","t":1785076277,"a":"dwgx","s":"feat(dashboard,account): expose sticky affinity counters on /connect-metrics","k":"node","p":["f2c938eb"],"spine":1},{"h":"db5c9023","t":1785076504,"a":"dwgx","s":"chore(credits): 补记 wangergou777 两连(#217 B+ / #230 S)","k":"pr","p":["9198a858"],"spine":1},{"h":"730eaf94","t":1785091259,"a":"dwgx","s":"fix(identity-neutralize): 覆盖 array/parts 形态 system 消息 + 修型号指纹句被小数点截断","k":"node","p":["db5c9023"],"spine":1},{"h":"ac045ba4","t":1785091480,"a":"dwgx","s":"fix(chat,auth): CAPACITY 冷却写在 connect selector 维度 — #224 同构缺陷第三次","k":"pr","p":["b75eaa36"],"spine":1},{"h":"bd97b895","t":1785107939,"a":"dwgx","s":"fix(chat): UPSTREAM_ERROR 不再把健康账号打下线 — 与 CONTENT_BLOCKED 同族的漏项","k":"node","p":["3397f25d"],"spine":1},{"h":"8be3efd8","t":1785133309,"a":"dwgx","s":"chore(release): v3.9.0","k":"tag","p":["85bc1fb2"],"spine":1},{"h":"9378b5b0","t":1785184107,"a":"dwgx","s":"fix(response-store): 字节预算在数百租户时形同不存在 — 公平份额加下限","k":"node","p":["9586593f"],"spine":1},{"h":"7d7d7acd","t":1785241191,"a":"dwgx","s":"chore(release): v3.9.2","k":"tag","p":["9fb53024"],"spine":1},{"h":"6d51448f","t":1785262754,"a":"dwgx","s":"docs: 记录检索/删除端点的 query 身份参数契约","k":"node","p":["4a4653d2"],"spine":1},{"h":"7c398968","t":1785431374,"a":"dwgx","s":"Merge PR #233: fix(ci): migrate macOS x64 release runner to macos-26-intel","k":"merge","p":["45041fd1","ff9b98f4"],"spine":1},{"h":"d7a175de","t":1785431405,"a":"dwgx","s":"Merge PR #232: feat(models): enforce per-account upstream cloud catalogs","k":"merge","p":["7c398968","f0a0074d"],"spine":1},{"h":"320ffd5d","t":1785431539,"a":"dwgx","s":"docs(contributors): 补 andya1lan #232 (A) 与 #233 (B+)","k":"pr","p":["d7a175de"],"spine":1},{"h":"3cc5aaba","t":1785667539,"a":"dwgx","s":"fix(models): 空/畸形目录响应零确认擦掉 last-known-good + 缩水目录永久楔住","k":"node","p":["68da1250"],"spine":1},{"h":"14745a8b","t":1785739206,"a":"dwgx","s":"Merge PR #238: fix(devin-connect): rescue thinking-only finishes with a corrective nudge","k":"merge","p":["ba9a2051","793ed796"],"spine":1},{"h":"3d46f591","t":1785740541,"a":"dwgx","s":"fix(devin-connect): reasoning 提升成 content 时同一段文字返回两遍","k":"node","p":["14745a8b"],"spine":1},{"h":"438d1d62","t":1785740987,"a":"dwgx","s":"docs(release,credits): v3.9.7 补 #238 一节 + 台账记 warelik #238 (S)","k":"pr","p":["9ef10b4b"],"spine":1},{"h":"36cbf327","t":1785783897,"a":"dwgx","s":"docs: 交接说明 2026-08-05 + 索引重排","k":"node","p":["1671ca48"],"spine":1},{"h":"095df441","t":1785834474,"a":"dwgx","s":"test(mutations): 两个 spec 的 baseline 跟上新增断言(guard 2 抓的)","k":"tag","p":["deb810de"],"spine":1},{"h":"61a74529","t":1785855882,"a":"dwgx","s":"docs(ledger): 第十二轮 —— 并更正上一轮一句假话","k":"node","p":["19636a90"],"spine":1},{"h":"1e2aaeaf","t":1785875674,"a":"dwgx","s":"Merge PR #241: fix(devin-connect): rescue nudge quotes the failed attempt's reasoning tail","k":"merge","p":["628dbd90","0f5a57bd"],"spine":1},{"h":"eedf274f","t":1785875923,"a":"dwgx","s":"fix(devin-connect): digest 上限缺一个钳,`1e9` 会把整段 reasoning 塞进每条 nudge","k":"node","p":["1e2aaeaf"],"spine":1},{"h":"9830571c","t":1785876041,"a":"dwgx","s":"test(mutations): #241 打断了两条既有 anchor,并给新钳补 6 条突变","k":"pr","p":["eedf274f"],"spine":1},{"h":"87bd1e9d","t":1785876388,"a":"dwgx","s":"test(mutations): baseline 改成实测的 79 —— 上一个 commit 里我把它算错了","k":"node","p":["9830571c"],"spine":1},{"h":"45e54997","t":1785878174,"a":"dwgx","s":"docs: 台账第十三轮 + 交接 §6(#241 已合),并修台账\"怎么读\"里两条不实的自救说明","k":"pr","p":["87bd1e9d"],"spine":1},{"h":"d7d043e2","t":1785878639,"a":"dwgx","s":"docs(contributors): #241 记入台账(warelik,S 档)","k":"pr","p":["45e54997"],"spine":1},{"h":"ca6584fd","t":1785903377,"a":"W ARELIK","s":"fix(devin-connect): reasoning continuity 预算钮补上 ceiling 钳 —— 同 #241 DIGEST_MAX_CEILING 的课后作业","k":"pr","p":["322d172e"],"spine":0},{"h":"025bb734","t":1785903377,"a":"W ARELIK","s":"fix(devin-connect): reasoning continuity 预算钮补上 ceiling 钳 —— 同 #241 DIGEST_MAX_CEILING 的课后作业","k":"pr","p":["5acf2cab"],"spine":0},{"h":"e980bc29","t":1785904445,"a":"W ARELIK","s":"docs(session-continuity): 把 #226 的 stable-from-turn-1 注释指回 findExistingState 的根锚分支","k":"pr","p":["025bb734"],"spine":0},{"h":"9265e491","t":1785904445,"a":"W ARELIK","s":"docs(session-continuity): 把 #226 的 stable-from-turn-1 注释指回 findExistingState 的根锚分支","k":"pr","p":["ca6584fd"],"spine":0},{"h":"b067c7d3","t":1785935163,"a":"dwgx","s":"docs: 发版说明照着做会被自己的钩子拒绝 —— 改对,并把一致性做成守卫","k":"node","p":["f143ef11"],"spine":1},{"h":"e81cde70","t":1785973542,"a":"dwgx","s":"Merge fix/nlu-negation-and-arg-slot","k":"merge","p":["967a4e96","c5bb28e5"],"spine":1},{"h":"d89a00c0","t":1785973542,"a":"dwgx","s":"Merge branch 'fix/schema-ref-fanout-budget'","k":"merge","p":["b8e770ad","17099c13"],"spine":1},{"h":"cc4c577f","t":1785973542,"a":"dwgx","s":"Merge branch 'fix/zero-content-stream-death'","k":"merge","p":["d89a00c0","7b99c8ef"],"spine":1},{"h":"714ebbb2","t":1785973542,"a":"dwgx","s":"Merge branch 'fix/response-store-four-defects'","k":"merge","p":["cc4c577f","aa373e41"],"spine":1},{"h":"b8e770ad","t":1785973542,"a":"dwgx","s":"Merge branch 'fix/identity-neutralize-paragraph-bound'","k":"merge","p":["e81cde70","7bca624b"],"spine":1},{"h":"43dc10f1","t":1785978436,"a":"dwgx","s":"test(mutations): D2 那条漏网的前提是实测的,不是「守卫有洞」","k":"node","p":["c70ed2c0"],"spine":1},{"h":"a7326da3","t":1785979784,"a":"dwgx","s":"test(response-store): 「重写不重启绝对时钟」那条测不出来 —— 时序问题","k":"tip","p":["a9921887"],"spine":1},{"h":"69fec136","t":1786028174,"a":"W ARELIK","s":"fix(devin-connect): move leading think-tag reroute to the event level, before the #238 rescue","k":"pr","p":["f7fde90e"],"spine":0},{"h":"b0ea8b3d","t":1786028174,"a":"W ARELIK","s":"fix(devin-connect): move leading think-tag reroute to the event level, before the #238 rescue","k":"pr","p":["e630ba48"],"spine":0},{"h":"b861cefe","t":1786046405,"a":"W ARELIK","s":"test(reasoning-dedup): fix post-divergence prefix assertion and add latch test","k":"tip","p":["b9f405dc"],"spine":0},{"h":"dd0f95e3","t":1786051283,"a":"W ARELIK","s":"style(leak-trace): trailing newlines","k":"tip","p":["af55ca73"],"spine":0},{"h":"36d5a851","t":1786072468,"a":"W ARELIK","s":"chore(think-reroute): review nits — drop dead suffixPrefixLen, one-line emit guard","k":"tip","p":["3b5a9f2f"],"spine":0},{"h":"97046344","t":1786072506,"a":"W ARELIK","s":"fix(session-continuity): root-fallback re-association must fully re-index the new pair window; trailing newline","k":"tip","p":["c3e59ecd"],"spine":0},{"h":"3e7651bb","t":1786072881,"a":"W ARELIK","s":"chore(session-fidelity): review nits — count-ceiling constant, one-line thinking accumulate","k":"tip","p":["cf02426f"],"spine":0},{"h":"1d5c4168","t":1786112104,"a":"dwgx","s":"fix(identity-neutralize): report a length-INCREASING rewrite, not only over-deletion","k":"node","p":["a7326da3"],"spine":1},{"h":"0487ba2d","t":1786112337,"a":"dwgx","s":"chore(mutate): mutation harness for a dirty tree, plus round-15 ledger","k":"tip","p":["b6e7dc9a"],"spine":1},{"h":"51846e0e","t":1786115503,"a":"dwgx","s":"chore(release): 3.9.21","k":"tag","p":["0487ba2d"],"spine":1},{"h":"7524d526","t":1786244716,"a":"dwgx","s":"docs: 修正两处与代码相反的说明 —— 模型目录来源与 CASCADE_REUSE_HASH_SYSTEM 默认值","k":"node","p":["51846e0e"],"spine":1},{"h":"0cfa3771","t":1786245823,"a":"dwgx","s":"Merge PR #247: feat(chat): incremental reasoning/content dedup — release on divergence, suppress only a verbatim full du","k":"merge","p":["7524d526","895b5dcb"],"spine":1},{"h":"b87ac6a1","t":1786246151,"a":"dwgx","s":"feat(reasoning-dedup): WINDSURFAPI_REASONING_DEDUP off-switch —— 默认开,可关","k":"node","p":["0cfa3771"],"spine":1},{"h":"d7ac4e66","t":1786247149,"a":"dwgx","s":"test(mutations): 修正 reasoning-dedup spec baseline 13 → 18 —— 是 #247 off-switch 测试加到 18,不是套件坏了","k":"pr","p":["b87ac6a1"],"spine":1},{"h":"dc5d4878","t":1786247311,"a":"dwgx","s":"Merge PR #243: fix(messages): reroute leading think-tagged content to the thinking channel — break reasoning/content ble","k":"merge","p":["d7ac4e66","a79c58f4"],"spine":1},{"h":"1030b435","t":1786249333,"a":"dwgx","s":"Merge PR #242: feat(devin-connect): session fidelity for multi-turn agentic work — stable ModelConfig + reasoning contin","k":"merge","p":["dc5d4878","64f95059"],"spine":1},{"h":"44eeac50","t":1786249379,"a":"dwgx","s":"test(mutations): retry-rescue baseline → 88(实测) —— #242 与 #243 各自量对了,叠起来都错","k":"pr","p":["1030b435"],"spine":1},{"h":"2b42fe42","t":1786249841,"a":"dwgx","s":"Merge PR #248: feat(session-continuity): survive single-dialog history compaction — root-anchor fallback","k":"merge","p":["44eeac50","d4d416f4"],"spine":1},{"h":"0da8e024","t":1786251981,"a":"dwgx","s":"test(mutations): reasoning-continuity baseline 289 → 300(实测) —— #243/#248 往同一批文件加了测试","k":"pr","p":["2b42fe42"],"spine":1},{"h":"ba4d768c","t":1786273382,"a":"dwgx","s":"docs(contributors): warelik 四条(S/A+/S/A)记入台账 —— #243 #242 #248 #247","k":"pr","p":["0da8e024"],"spine":1},{"h":"db85abe8","t":1786279973,"a":"dwgx","s":"feat(gate): 把三类复发缺陷从「合并后才暴露」变成「CI 拦截」","k":"node","p":["ba4d768c"],"spine":1},{"h":"27cb2306","t":1786284399,"a":"dwgx","s":"fix(login): Firebase API_KEY_HTTP_REFERRER_BLOCKED → ERR_HTTP_REFERRER_BLOCKED + x-client-version headers","k":"tip","p":["db85abe8"],"spine":0},{"h":"ba4b6591","t":1786284636,"a":"dwgx","s":"refactor(auth): rate-limit ring accessor returns whole ring so overflow is observable","k":"tip","p":["489f31d4"],"spine":0},{"h":"6dcd0a91","t":1786285485,"a":"dwgx","s":"fix(devin-connect): CompletionConfiguration #2/#3 tag swap (max_tokens/max_newlines)","k":"tip","p":["db85abe8"],"spine":0},{"h":"9aa22ced","t":1786285551,"a":"dwgx","s":"fix(responses): emit trailing data:[DONE] after terminal event","k":"tip","p":["db85abe8"],"spine":0},{"h":"ebcc99f2","t":1786285642,"a":"dwgx","s":"Merge commit '6dcd0a91a0dbf1c2dc7a88a8b5e5fdf7319d0f31' into feat/research-gaps","k":"merge","p":["db85abe8","6dcd0a91"],"spine":0},{"h":"e0c34388","t":1786285642,"a":"dwgx","s":"Merge commit '9aa22ced1149e6bf05278ed4da695b154951ff8f' into feat/research-gaps","k":"merge","p":["97d16da7","9aa22ced"],"spine":0},{"h":"97d16da7","t":1786285642,"a":"dwgx","s":"Merge commit '27cb2306ae807e2268f04ab8447fa933949d7e46' into feat/research-gaps","k":"merge","p":["2646b405","27cb2306"],"spine":0},{"h":"2646b405","t":1786285642,"a":"dwgx","s":"Merge commit 'ba4b6591a6c85d3533456cbc9e70c2ce6b7e53e8' into feat/research-gaps","k":"merge","p":["ebcc99f2","ba4b6591"],"spine":0},{"h":"e42b4b93","t":1786285935,"a":"dwgx","s":"docs(devin-connect): 更正 max_tokens tag —— #3 是 max_newlines,两个历史探针测的都不是上限","k":"tip","p":["e0c34388"],"spine":0},{"h":"97e06268","t":1786292458,"a":"dwgx","s":"fix(#250): 错误路径释放 accThinking —— 补上对抗 review 挖出的 emulateTools 泄漏","k":"tip","p":["af0db0dc"],"spine":0},{"h":"8e1faf1a","t":1786292659,"a":"dwgx","s":"fix(think-classifier): 把栈式互递归改成迭代循环 —— 深链不再爆栈","k":"tip","p":["af0db0dc"],"spine":0},{"h":"ea2cefa5","t":1786292838,"a":"dwgx","s":"Merge feat/research-gaps: 四修复 + #250 Cascade 防线 + 对抗 review 修正","k":"merge","p":["db85abe8","97e06268"],"spine":1},{"h":"4ee53b89","t":1786293159,"a":"dwgx","s":"Merge commit '8e1faf1'","k":"merge","p":["ea2cefa5","8e1faf1a"],"spine":1},{"h":"d11ce66f","t":1786296856,"a":"dwgx","s":"feat(devin-connect): credit_cost 可从顶层 #14 读 —— 支持 ^ 前缀 + 顶层校准 dump","k":"node","p":["4f8f8011"],"spine":1},{"h":"273c4254","t":1786300333,"a":"dwgx","s":"Merge branch 'pr252' into pr252-merged","k":"merge","p":["fb51a132","337c76d0"],"spine":0},{"h":"8c75d38e","t":1786304820,"a":"dwgx","s":"feat(devin-connect): 显式 prompt 缓存 #13(默认关)—— 前置阻塞已被证伪","k":"pr","p":["fb51a132"],"spine":1},{"h":"b5c2e90d","t":1786325529,"a":"dwgx","s":"fix(secret-scan): 补上仓库自己的令牌格式 —— 扫描器一直漏掉 devin-session-token$","k":"node","p":["c374ec7a"],"spine":1},{"h":"20f0643a","t":1786341182,"a":"dwgx","s":"test(registry): 台账第四个盲点 —— 表驱动的默认开开关一个都没被发现","k":"node","p":["741b469c"],"spine":1},{"h":"5d92339c","t":1786341942,"a":"dwgx","s":"Merge branch 'master' into pr249","k":"merge","p":["d6740e23","3c1a1730"],"spine":0},{"h":"4d8bdc3b","t":1786341949,"a":"dwgx","s":"Merge branch 'master' into pr252","k":"merge","p":["1308ff4a","3c1a1730"],"spine":0},{"h":"08861f7d","t":1786342434,"a":"dwgx","s":"Merge pull request #249 from warelik/pr/reasoning-leak-trace","k":"merge","p":["3c1a1730","d6740e23"],"spine":1},{"h":"ed1ac352","t":1786342448,"a":"dwgx","s":"Merge pull request #252 from wjurkowlaniec/fix/swe-1-7-tool-dialect","k":"merge","p":["08861f7d","1308ff4a"],"spine":1},{"h":"1fbefd0c","t":1786343070,"a":"dwgx","s":"test(docs): 守卫第五个盲点 —— 常量间接引用,PR #249 一进来就漏掉了","k":"pr","p":["f91d54ba"],"spine":1},{"h":"2e3daebb","t":1786343090,"a":"dwgx","s":"docs: 开关数 156 -> 157,补第五种读取形式","k":"tip","p":["05239105"],"spine":1},{"h":"05239105","t":1786343090,"a":"dwgx","s":"fix(prompt): #252 的两处改动互相抵消 —— 还原全局默认值,让方言隔离真正生效","k":"pr","p":["1fbefd0c"],"spine":1},{"h":"d8a2bff2","t":1786367406,"a":"dwgx","s":"docs: 192 个文件只有一个入口 —— 补 docs/ 的用户索引和 CHANGELOG","k":"node","p":["1a95e111"],"spine":1},{"h":"121fb9ba","t":1786432106,"a":"dwgx","s":"fix(test): issue 模板守卫同步 —— 修 CI 红","k":"tip","p":["5c21e43c"],"spine":1},{"h":"681bbc01","t":1786434726,"a":"dwgx","s":"docs(readme): 加 logo 与徽章组,英文版补故障分流章节和四条 FAQ","k":"tip","p":["121fb9ba"],"spine":1},{"h":"9e8080a8","t":1786462500,"a":"dwgx","s":"feat(models): 同步 gpt-5.6-luna 与 Claude 5 全系,补 swe 图片明确报错 (#244)","k":"tip","p":["681bbc01"],"spine":1},{"h":"1dc3a3ed","t":1786464507,"a":"dwgx","s":"docs: 新增全局历史记账本 HISTORY-LEDGER.md —— 1205 commits/69 PR/177 issue 全景","k":"tip","p":["9e8080a8"],"spine":1},{"h":"1bb2a814","t":1786466121,"a":"dwgx","s":"docs: 记账本 v2 精细账 —— 1205 条 commit 逐条 + 问题链 + 69 PR + 177 issue 全量","k":"tip","p":["1dc3a3ed"],"spine":1},{"h":"2c90318b","t":1786466363,"a":"dwgx","s":"fix(docs): 账本引用的 sk- 夹具名触发 secret-scan —— 拆开前缀并记录教训","k":"tip","p":["1bb2a814"],"spine":1},{"h":"1bb54803","t":1786466771,"a":"dwgx","s":"docs: 全局历史账本可视化页 —— 零依赖纯 SVG 总览","k":"tip","p":["2c90318b"],"spine":1},{"h":"ef8153c8","t":1786467311,"a":"dwgx","s":"fix(docs): 可视化页三处事实错误修正","k":"tip","p":["1bb54803"],"spine":1},{"h":"2d07bbf5","t":1786469036,"a":"dwgx","s":"fix(docs): 可视化页 SVG 渲染修复 —— createElementNS 是 Document 方法不是 Element 方法","k":"tip","p":["ef8153c8"],"spine":1},{"h":"41520cd5","t":1786469740,"a":"dwgx","s":"feat(docs): 自伤时间线升级为交互版 —— hover 跟随 tooltip + 账本/Git 链接","k":"tip","p":["2d07bbf5"],"spine":1},{"h":"bb2ad5ea","t":1786469882,"a":"dwgx","s":"fix(docs): 交互时间线同样的 API 错误复发 —— 8 处 svg.createElementNS 改 document.createElementNS","k":"tip","p":["41520cd5"],"spine":1},{"h":"78d1caf5","t":1786470785,"a":"dwgx","s":"feat(docs): 全部 6 张图加交互 —— hover 跟随浮窗 + 点击锁定可点链接","k":"tip","p":["bb2ad5ea"],"spine":1},{"h":"9fce520a","t":1786472680,"a":"dwgx","s":"fix(docs): 自伤时间线节点从不挂载 —— python 替换交互段时吞掉了 svg.appendChild(g)","k":"tip","p":["78d1caf5"],"spine":1},{"h":"e81982b9","t":1786473623,"a":"dwgx","s":"feat(docs): 账本可视化页 v2 —— 全面 UI 升级(流式动画 + 仓库 Dashboard 设计 DNA)","k":"tip","p":["9fce520a"],"spine":1},{"h":"dbbe5f52","t":1786473769,"a":"dwgx","s":"fix(docs): 统计卡'14 分 49 秒'被数字滚动当成 1449 —— 非数字条目跳过滚动","k":"tip","p":["e81982b9"],"spine":1},{"h":"4e0c14aa","t":1786474530,"a":"dwgx","s":"fix(docs): 24h 图补数值文本 + 补柱子生长动画 CSS","k":"tip","p":["dbbe5f52"],"spine":1},{"h":"6289e2d1","t":1786474908,"a":"dwgx","s":"fix(docs): 全部内容不可见的根因 —— @keyframes 只写 from 没写 to,动画结束后回落 opacity 0","k":"tip","p":["4e0c14aa"],"spine":1},{"h":"60b467d4","t":1786476010,"a":"dwgx","s":"feat(docs): 顶部引导 tab + 贡献者分析 + Git 树形态","k":"tip","p":["6289e2d1"],"spine":1},{"h":"d437d358","t":1786476272,"a":"dwgx","s":"fix(docs): Git 树数据与渲染修正","k":"tip","p":["60b467d4"],"spine":1},{"h":"ce3ebeba","t":1786477458,"a":"dwgx","s":"feat(docs): 真实头像 + 背景漂浮语录 + Git 树重设计","k":"tip","p":["d437d358"],"spine":1},{"h":"20608ae1","t":1786478079,"a":"dwgx","s":"fix(docs): Git 树 lane 超画布修复 + ESC 纯净字幕模式","k":"tip","p":["ce3ebeba"],"spine":1},{"h":"52cdf503","t":1786479644,"a":"dwgx","s":"feat(docs): Git 树三模板(竖/横/环) + 容器右上角切换 + 布局修复","k":"tip","p":["20608ae1"],"spine":1},{"h":"cefedfed","t":1786481167,"a":"dwgx","s":"fix(docs): 头像系统重构 + 视觉质感提升","k":"tip","p":["52cdf503"],"spine":1},{"h":"c9924561","t":1786481232,"a":"dwgx","s":"fix(docs): 头像系统重构 + 质感落地(补上次丢失的改动)","k":"tip","p":["cefedfed"],"spine":1},{"h":"99ad5ef6","t":1786481967,"a":"dwgx","s":"fix(docs): 树数据更新到最新 + 空窗标注 + 横/环重设计 + 右侧空间利用","k":"tip","p":["c9924561"],"spine":1},{"h":"cad2892e","t":1786484160,"a":"dwgx","s":"fix(docs): Git 树空白根因 —— 重写时吞掉 render 函数定义,ReferenceError 中断整个树 IIFE","k":"tip","p":["99ad5ef6"],"spine":1},{"h":"ec1a7e7f","t":1786485453,"a":"dwgx","s":"fix(docs): 横模式改横向滚动 + 环模式贝塞尔汇入线展示关系","k":"tip","p":["cad2892e"],"spine":1},{"h":"31a83caa","t":1786485619,"a":"dwgx","s":"fix(docs): 排行榜只放 2+ PR 多次贡献者,单发过客全进长尾","k":"tip","p":["ec1a7e7f"],"spine":1},{"h":"90ebd965","t":1786486273,"a":"dwgx","s":"feat(docs): 竖模式默认收缩可展开 + 长尾 hover 总结 + 横空窗细线 + 环装饰","k":"tip","p":["31a83caa"],"spine":1},{"h":"2d194410","t":1786487181,"a":"dwgx","s":"feat(docs): 性能优化(头像 use 复用 + fragment 挂载) + 账本三入口","k":"tip","p":["90ebd965"],"spine":1},{"h":"822680db","t":1786488234,"a":"dwgx","s":"feat(docs): 五语言国际化 + 更名「工程的年轮」 + 全页面 emoji 清理","k":"tip","p":["2d194410"],"spine":1},{"h":"5407adfa","t":1786495643,"a":"dwgx","s":"fix(docs): hero logo 换回仓库品牌图 —— 原橙底深色 W 小尺寸下像 M","k":"tip","p":["822680db"],"spine":1},{"h":"a97ced3e","t":1786495789,"a":"dwgx","s":"fix(docs): W 形几何修正 —— 谷太浅(78)被读成 M,谷压到 88 接近底 90","k":"tip","p":["5407adfa"],"spine":1},{"h":"f0ce9e48","t":1786496224,"a":"dwgx","s":"feat(docs): README 与账本页嵌入 Star History 实时图表","k":"tip","p":["a97ced3e"],"spine":1},{"h":"26737c03","t":1786496769,"a":"dwgx","s":"feat(docs): 自托管 Star History —— 不再依赖 star-history 第三方服务","k":"tip","p":["f0ce9e48"],"spine":1},{"h":"0e61599b","t":1786496957,"a":"dwgx","s":"fix(docs): Star 图路径修正 —— Pages 只发布 docs/,SVG 移到 docs/assets/","k":"tip","p":["26737c03"],"spine":1},{"h":"3291f8b2","t":1786497089,"a":"github-actions[bot]","s":"chore(assets): 更新 star 历史图","k":"node","p":["0e61599b"],"spine":1},{"h":"e2b10cf9","t":1786497992,"a":"dwgx","s":"fix(docs): logo W 形方向彻底修正 —— 之前一直在画 M(SVG y 轴向下)","k":"tip","p":["3291f8b2"],"spine":1},{"h":"995a0ad8","t":1786498125,"a":"dwgx","s":"fix(docs): README 底部 Star History 重复段合并 —— 旧的纯链接段并入新块","k":"tip","p":["e2b10cf9"],"spine":1},{"h":"a2f3bc00","t":1786498357,"a":"dwgx","s":"fix(docs): 三色点配合 W 翻转重新摆放 —— 横排在 W 上方居中","k":"tip","p":["995a0ad8"],"spine":1},{"h":"fa0d8006","t":1786498521,"a":"dwgx","s":"feat(docs): homepage 切回落地页(index.html) + 落地页加「工程的年輪」入口","k":"tip","p":["a2f3bc00"],"spine":1},{"h":"c6df903e","t":1786498669,"a":"dwgx","s":"fix(docs): 三色点 L 形排版 —— 红最右上/绿在红左/蓝在绿下","k":"tip","p":["fa0d8006"],"spine":1},{"h":"f487500c","t":1786499289,"a":"dwgx","s":"fix(docs): 三点左移 + 红蓝同列对齐 —— 绿(96,20) 红(112,20) 蓝(112,36)","k":"tip","p":["c6df903e"],"spine":1},{"h":"3ec28a98","t":1786500407,"a":"dwgx","s":"feat(docs): Wave0 质感小项 —— 数字排版/节点放大/虚线提亮/滚动保留/打字机跳过","k":"tip","p":["f487500c"],"spine":1},{"h":"4d32dcb7","t":1786500938,"a":"dwgx","s":"feat(docs): Wave1 —— 8 语言 + 完整 bio + 按钮翻译修复","k":"tip","p":["3ec28a98"],"spine":1},{"h":"3cb843b0","t":1786501072,"a":"dwgx","s":"feat(docs): Wave2 —— 树时间旅行 + 过滤开关 + 缩放","k":"tip","p":["4d32dcb7"],"spine":1},{"h":"6dda9a1b","t":1786501173,"a":"dwgx","s":"feat(docs): Wave3 —— 提交列表 + 筛选 + 树联动","k":"tip","p":["3cb843b0"],"spine":1},{"h":"e3a175ad","t":1786501853,"a":"dwgx","s":"fix(docs): Wave4 —— 对抗 review 的 2 BLOCKER + 8 MAJOR 全修","k":"tip","p":["6dda9a1b"],"spine":1},{"h":"e5bcf351","t":1786503057,"a":"dwgx","s":"feat(docs): Wave5 —— 自定义 UI 控件 + 树播放 camera 动画 + 星卡移底","k":"tip","p":["e3a175ad"],"spine":1},{"h":"298665cb","t":1786504865,"a":"dwgx","s":"fix(docs): Wave6 —— 播放动画重构(增量无闪烁) + 列表收起 + 树高降 44%","k":"tip","p":["e5bcf351"],"spine":1},{"h":"7699ff30","t":1786505271,"a":"dwgx","s":"fix(docs): Wave7 —— 回退多余控件,修语言切换 UI","k":"tip","p":["298665cb"],"spine":1},{"h":"5fa32ee6","t":1786505557,"a":"dwgx","s":"fix(docs): Wave8 —— 去 camera/去 tree_sub/模式按钮自定义 UI/顶部压缩","k":"tip","p":["7699ff30"],"spine":1},{"h":"55b4a4d4","t":1786505604,"a":"dwgx","s":"fix(docs): 重建 wave7 误删的 tree-head/tree-tools CSS(模式按钮恢复自定义样式)","k":"tip","p":["5fa32ee6"],"spine":1},{"h":"f43f96a4","t":1786506272,"a":"dwgx","s":"revert(docs): 回退 Wave5-8 的自定义控件/动画改动,回到稳定版","k":"tip","p":["55b4a4d4"],"spine":1},{"h":"830ac65c","t":1786508900,"a":"dwgx","s":"fix(tool-emulation): 三个已知缺陷修复(对抗 review 两轮实锤验证)","k":"tip","p":["f43f96a4"],"spine":1},{"h":"1a4fbcce","t":1786509081,"a":"dwgx","s":"chore: 提交 assets/star-history.svg 删除(已移至 docs/assets/)","k":"tip","p":["830ac65c"],"spine":1},{"h":"756c3ac1","t":1786509996,"a":"dwgx","s":"feat(docs): 树数据更新到最新(260 锚点含 1a4fbcce)+ PR 70 单发 26","k":"node","p":["1a4fbcce"],"spine":1},{"h":"b0896cb7","t":1786510161,"a":"dwgx","s":"chore(docs): 删研究文档 + 树 HEAD 标签","k":"node","p":["756c3ac1"],"spine":1},{"h":"f1ad324f","t":1786511637,"a":"dwgx","s":"fix(tool-emulation): 工具族作战地图 P1/P3/P4(核验驱动)","k":"node","p":["b0896cb7"],"spine":1},{"h":"5a076930","t":1786650084,"a":"dwgx","s":"feat(ota): OTA 自更新版本门禁 + 失败回滚 + UI 贴合(对抗 review 2 轮)","k":"tag","p":["f1ad324f"],"spine":1},{"h":"2a999a4b","t":1786650266,"a":"dwgx","s":"docs(release): v3.9.22 release notes —— OTA 门禁+回滚 + 工具族三缺陷","k":"node","p":["5a076930"],"spine":1},{"h":"3a27b852","t":1786652984,"a":"dwgx","s":"chore(docs): 删除本地审计提示词模板","k":"node","p":["2a999a4b"],"spine":1},{"h":"722ee369","t":1786960666,"a":"dwgx","s":"fix(release): pin OTA updates to published tags","k":"node","p":["3a27b852"],"spine":1},{"h":"d2535566","t":1786964600,"a":"dwgx","s":"fix(devin-connect): finish native vision, catalog refresh, and system collapse","k":"node","p":["722ee369"],"spine":1},{"h":"f1444e2c","t":1786966708,"a":"dwgx","s":"chore(release): prepare v3.9.23","k":"tag","p":["d2535566"],"spine":1},{"h":"d208fd42","t":1787194785,"a":"dwgx","s":"fix(gateway): land identity catalog and ota follow-ups","k":"node","p":["f1444e2c"],"spine":1},{"h":"99d5c2b0","t":1787201464,"a":"dwgx","s":"fix(openai): reject logprobs, stable created, legacy completions","k":"node","p":["d208fd42"],"spine":1},{"h":"28b48512","t":1787201604,"a":"dwgx","s":"feat(connect): decode opted-in ACU as fixed64, keep default off","k":"node","p":["99d5c2b0"],"spine":1},{"h":"3ef4311d","t":1787204848,"a":"dwgx","s":"fix(connect): abort streamChat before jwt or network","k":"node","p":["28b48512"],"spine":1},{"h":"c21fc248","t":1787206952,"a":"dwgx","s":"test(ota): drop self-referential harness lock mutation","k":"node","p":["3ef4311d"],"spine":1},{"h":"eae8eb2b","t":1787207842,"a":"dwgx","s":"test(ota): keep docker cgroup identity mutation anchored","k":"node","p":["c21fc248"],"spine":1},{"h":"f674ef54","t":1787207842,"a":"dwgx","s":"chore(release): prepare v3.9.24","k":"tag","p":["eae8eb2b"],"spine":1},{"h":"e4ee9ef5","t":1787208474,"a":"dwgx","s":"test(ota): drop unanchored docker cgroup identity mutation","k":"node","p":["f674ef54"],"spine":1},{"h":"f38214c4","t":1787209180,"a":"dwgx","s":"test(ota): pin missing-probe mutation on the ss fail path","k":"node","p":["e4ee9ef5"],"spine":1},{"h":"cf5208c2","t":1787211124,"a":"dwgx","s":"docs: retarget live handoff to v3.9.24 takeover","k":"node","p":["f38214c4"],"spine":1},{"h":"98c46d6d","t":1787216628,"a":"dwgx","s":"docs: stop live handoff pinning a self-stale SHA","k":"node","p":["cf5208c2"],"spine":1},{"h":"ee169dbe","t":1787217552,"a":"dwgx","s":"docs: align CUTOVER prose with cache default and homecloud health","k":"node","p":["98c46d6d"],"spine":1},{"h":"44efc4f7","t":1787264609,"a":"dwgx","s":"fix(server): abort non-stream messages/gemini/responses","k":"node","p":["ee169dbe"],"spine":1},{"h":"2c190b9c","t":1787266880,"a":"dwgx","s":"chore(release): cut v3.9.25","k":"tag","p":["44efc4f7"],"spine":1},{"h":"2e45c992","t":1787267572,"a":"dwgx","s":"docs(env): name STRICT_MODEL 400 next to DEFAULT_MODEL","k":"node","p":["2c190b9c"],"spine":1},{"h":"3a80ad5d","t":1787285986,"a":"dwgx","s":"fix(auth): classify session tokens on POST /accounts (#257)","k":"node","p":["2e45c992"],"spine":1},{"h":"471457d2","t":1787286425,"a":"dwgx","s":"fix(auth): classify session tokens on batch-import and env bootstrap","k":"node","p":["3a80ad5d"],"spine":1},{"h":"3709aba0","t":1787286745,"a":"dwgx","s":"fix(auth): unwrap show-auth-token URLs and store auth1_ as keys","k":"node","p":["471457d2"],"spine":1},{"h":"914d5df4","t":1787286911,"a":"dwgx","s":"fix(auth): do not store auth1_ as a pool apiKey","k":"node","p":["3709aba0"],"spine":1},{"h":"ec22dbc7","t":1787288132,"a":"dwgx","s":"chore(release): cut v3.9.26","k":"tag","p":["914d5df4"],"spine":1},{"h":"83bd6bf2","t":1787355974,"a":"dwgx","s":"fix(auth): close leftover #257 fail-closed holes","k":"node","p":["ec22dbc7"],"spine":1},{"h":"c53000a1","t":1787375865,"a":"dwgx","s":"fix(auth): classify API Key paste like Auth Token","k":"node","p":["83bd6bf2"],"spine":1},{"h":"9247149d","t":1787376483,"a":"dwgx","s":"test(auth): keep API Key URL fixture under session-token scan","k":"node","p":["c53000a1"],"spine":1},{"h":"ddf44242","t":1787379369,"a":"dwgx","s":"chore(release): cut v3.9.27","k":"tag","p":["9247149d"],"spine":1},{"h":"6267e8ab","t":1787515267,"a":"dwgx","s":"fix(connect): drought-safe selectors stay reachable on paid plans (#258)","k":"node","p":["ddf44242"],"spine":1},{"h":"26a4955f","t":1787517403,"a":"dwgx","s":"chore(release): cut v3.9.28","k":"tag","p":["6267e8ab"],"spine":1},{"h":"991ac20a","t":1787635575,"a":"dwgx","s":"docs(handoff): production SHA last-measured, not current","k":"node","p":["26a4955f"],"spine":1},{"h":"09bde39b","t":1787636064,"a":"dwgx","s":"fix(dashboard): 401 must not empty models/proxy/bans; login redact-first","k":"node","p":["991ac20a"],"spine":1},{"h":"69d96670","t":1787896950,"a":"dwgx","s":"fix(dashboard): stats 401 must not wipe tables; login/otp redact-first","k":"node","p":["09bde39b"],"spine":1},{"h":"c751227c","t":1787897322,"a":"dwgx","s":"chore(release): cut v3.9.29","k":"tag","p":["69d96670"],"spine":1},{"h":"1a8e5067","t":1788500164,"a":"dwgx","s":"chore(release): cut v3.9.30","k":"tag","p":["c751227c"],"spine":1},{"h":"f49f4c10","t":1788507342,"a":"dwgx","s":"chore(release): cut v3.9.31","k":"tag","p":["1a8e5067"],"spine":1},{"h":"939872b8","t":1789309975,"a":"dwgx","s":"test: drop --test-force-exit, which silently ends runs early and still exits 0","k":"node","p":["f49f4c10"],"spine":1},{"h":"3d831b9d","t":1789292333,"a":"dwgx","s":"Merge PR #261: interleave parallel tool calls with results","k":"merge","p":["939872b8"],"spine":1},{"h":"e1df2f51","t":1789314063,"a":"dwgx","s":"chore(release): cut v3.9.32","k":"tag","p":["3d831b9d"],"spine":1},{"h":"938a57e4","t":1789320233,"a":"dwgx","s":"chore(release): cut v3.9.33","k":"tag","p":["e1df2f51"],"spine":1},{"h":"aa3db794","t":1789353512,"a":"lovedxc","s":"fix(devin-connect): merge consecutive same-role text turns before wire encoding","k":"pr","p":["938a57e4"],"spine":1},{"h":"1b1d0e5b","t":1789356780,"a":"dwgx","s":"Merge PR #265: normalize multimodal tool output + nginx 32m/900s","k":"merge","p":["aa3db794"],"spine":1},{"h":"e50fce36","t":1789356890,"a":"dwgx","s":"Merge PR #266: swe-2 catalog + gpt-5.6 Devin Local opt-in","k":"merge","p":["1b1d0e5b"],"spine":1},{"h":"81108b07","t":1789360128,"a":"dwgx","s":"Merge PR #264: Devin desktop import + 512KB state cap","k":"merge","p":["e50fce36"],"spine":1},{"h":"0c085240","t":1789360360,"a":"dwgx","s":"chore(release): cut v3.9.34","k":"tag","p":["81108b07"],"spine":1},{"h":"71982d5a","t":1789432557,"a":"dwgx","s":"test(devin-connect): pin merge guards for adjacent tool_calls and images","k":"head","p":["0c085240"],"spine":1}],"segments":[["395311f2","4ff1baff",21,1776683773,1775727718],["4ff1baff","5c8ba744",34,1776765005,1776683773],["5c8ba744","60e6f15e",4,1776768130,1776765005],["60e6f15e","6002cc6d",14,1776826592,1776768130],["6002cc6d","2721cce4",4,1776829039,1776826592],["2721cce4","d3134764",7,1776843406,1776829039],["d3134764","89512bf5",27,1776926172,1776843406],["89512bf5","c8a86c5e",28,1777002667,1776926172],["ff539c76","2ae9d158",30,1777032049,1777002690],["2ae9d158","ca770ab3",4,1777049036,1777032049],["ca770ab3","ae4067c5",4,1777104993,1777049036],["ae4067c5","e078f35b",2,1777137724,1777104993],["a71bdd1b","4d51b5a9",3,1777140721,1777137835],["4d51b5a9","b5a88569",1,1777141863,1777140721],["b5a88569","54e78ad7",1,1777150961,1777141863],["60eab58d","5e8c067d",41,1777288335,1777150993],["5e8c067d","e2c951b7",16,1777452192,1777288335],["ce9dfba8","4406bd2f",2,1777458583,1777454266],["92ccf6d8","1f055a91",2,1777461581,1777458598],["1f055a91","946a14ff",1,1777463669,1777461581],["946a14ff","6f20680f",1,1777464328,1777463669],["6f20680f","6ab39a44",1,1777475014,1777464328],["8f7a9f5f","c5e5be64",1,1777478113,1777475027],["c5e5be64","971eaca1",84,1778554007,1777478113],["d72b093b","06dc8a57",2,1778554226,1778554011],["06dc8a57","80900336",2,1779407993,1778554226],["54707dd2","f5425ff2",1,1780439623,1779408063],["f5425ff2","542bb7cd",1,1780439935,1780439623],["542bb7cd","3e133bbe",8,1780645262,1780439935],["3e133bbe","7ee509a7",76,1782670594,1780645262],["0dc3e232","7f8d4038",47,1782806381,1782671302],["7f8d4038","af0cdab9",38,1783362043,1782806381],["af0cdab9","ea324036",6,1783371145,1783362043],["ea324036","a14c1efa",44,1783544868,1783371145],["a14c1efa","1616b5eb",31,1783649876,1783544868],["1616b5eb","4fc3c399",3,1783668818,1783649876],["4fc3c399","313bb8ed",17,1783778139,1783668818],["313bb8ed","c40ae89a",28,1783839956,1783778139],["c40ae89a","fac765ac",34,1784102491,1783839956],["fac765ac","81370f55",7,1784155245,1784102491],["0b4ffbf2","04c95595",2,1784271595,1784177450],["04c95595","b8644472",1,1784275838,1784271595],["b8644472","7d3bf3d3",1,1784945238,1784275838],["7d3bf3d3","d231d44f",13,1785075593,1784945238],["d231d44f","db5c9023",4,1785076504,1785075593],["db5c9023","ac045ba4",2,1785091480,1785076504],["ac045ba4","8be3efd8",13,1785133309,1785091480],["8be3efd8","7d7d7acd",23,1785241191,1785133309],["7d7d7acd","7c398968",7,1785431374,1785241191],["320ffd5d","14745a8b",12,1785739206,1785431539],["14745a8b","438d1d62",2,1785740987,1785739206],["438d1d62","095df441",89,1785834474,1785740987],["095df441","1e2aaeaf",31,1785875674,1785834474],["1e2aaeaf","9830571c",1,1785876041,1785875674],["9830571c","45e54997",1,1785878174,1785876041],["d7d043e2","e81cde70",20,1785973542,1785878639],["714ebbb2","a7326da3",4,1785979784,1785973542],["a7326da3","0487ba2d",2,1786112337,1785979784],["51846e0e","0cfa3771",1,1786245823,1786115503],["0cfa3771","d7ac4e66",1,1786247149,1786245823],["ba4d768c","ea2cefa5",1,1786292838,1786273382],["4ee53b89","8c75d38e",3,1786304820,1786293159],["8c75d38e","08861f7d",18,1786342434,1786304820],["ed1ac352","1fbefd0c",5,1786343070,1786342448],["2e3daebb","121fb9ba",10,1786432106,1786343090],["0e61599b","e2b10cf9",1,1786497992,1786496957],["f1ad324f","5a076930",1,1786650084,1786511637],["3a27b852","722ee369",4,1786960666,1786652984],["f1444e2c","d208fd42",3,1787194785,1786966708],["2e45c992","3a80ad5d",1,1787285986,1787267572],["ddf44242","6267e8ab",1,1787515267,1787379369],["26a4955f","991ac20a",2,1787635575,1787517403],["09bde39b","69d96670",2,1787896950,1787636064],["c751227c","71982d5a",12,1789432557,1787897322]],"squash_prs":[{"h":"aa3db794","n":270,"s":"fix(devin-connect): merge consecutive same-role text turns"},{"h":"e50fce36","n":266,"s":"Merge PR #266: swe-2 catalog + gpt-5.6 Devin Local opt-in"},{"h":"1b1d0e5b","n":265,"s":"Merge PR #265: multimodal tool output + nginx 32m/900s"},{"h":"81108b07","n":264,"s":"Merge PR #264: Devin desktop import + 512KB state cap"},{"h":"3d831b9d","n":261,"s":"Merge PR #261: interleave parallel tool calls with results"},{"h":"05239105","n":252,"s":"fix(prompt): #252 的两处改动互相抵消 —— 还原全局默认值,让方言隔离真正生效"},{"h":"1fbefd0c","n":249,"s":"test(docs): 守卫第五个盲点 —— 常量间接引用,PR #249 一进来就漏掉了"},{"h":"ed1ac352","n":252,"s":"Merge pull request #252 from wjurkowlaniec/fix/swe-1-7-tool-"},{"h":"08861f7d","n":249,"s":"Merge pull request #249 from warelik/pr/reasoning-leak-trace"},{"h":"8c75d38e","n":13,"s":"feat(devin-connect): 显式 prompt 缓存 #13(默认关)—— 前置阻塞已被证伪"},{"h":"ba4d768c","n":243,"s":"docs(contributors): warelik 四条(S/A+/S/A)记入台账 —— #243 #242 #2"},{"h":"0da8e024","n":243,"s":"test(mutations): reasoning-continuity baseline 289 → 300(实测)"},{"h":"2b42fe42","n":248,"s":"Merge PR #248: feat(session-continuity): survive single-dial"},{"h":"44eeac50","n":242,"s":"test(mutations): retry-rescue baseline → 88(实测) —— #242 与 #2"},{"h":"1030b435","n":242,"s":"Merge PR #242: feat(devin-connect): session fidelity for mul"},{"h":"dc5d4878","n":243,"s":"Merge PR #243: fix(messages): reroute leading think-tagged c"},{"h":"d7ac4e66","n":247,"s":"test(mutations): 修正 reasoning-dedup spec baseline 13 → 18 ——"},{"h":"0cfa3771","n":247,"s":"Merge PR #247: feat(chat): incremental reasoning/content ded"},{"h":"69fec136","n":238,"s":"fix(devin-connect): move leading think-tag reroute to the ev"},{"h":"e980bc29","n":226,"s":"docs(session-continuity): 把 #226 的 stable-from-turn-1 注释指回 f"},{"h":"025bb734","n":241,"s":"fix(devin-connect): reasoning continuity 预算钮补上 ceiling 钳 —— "},{"h":"b0ea8b3d","n":238,"s":"fix(devin-connect): move leading think-tag reroute to the ev"},{"h":"9265e491","n":226,"s":"docs(session-continuity): 把 #226 的 stable-from-turn-1 注释指回 f"},{"h":"ca6584fd","n":241,"s":"fix(devin-connect): reasoning continuity 预算钮补上 ceiling 钳 —— "},{"h":"d7d043e2","n":241,"s":"docs(contributors): #241 记入台账(warelik,S 档)"},{"h":"45e54997","n":241,"s":"docs: 台账第十三轮 + 交接 §6(#241 已合),并修台账\"怎么读\"里两条不实的自救说明"},{"h":"9830571c","n":241,"s":"test(mutations): #241 打断了两条既有 anchor,并给新钳补 6 条突变"},{"h":"1e2aaeaf","n":241,"s":"Merge PR #241: fix(devin-connect): rescue nudge quotes the f"},{"h":"438d1d62","n":238,"s":"docs(release,credits): v3.9.7 补 #238 一节 + 台账记 warelik #238 ("},{"h":"14745a8b","n":238,"s":"Merge PR #238: fix(devin-connect): rescue thinking-only fini"},{"h":"320ffd5d","n":232,"s":"docs(contributors): 补 andya1lan #232 (A) 与 #233 (B+)"},{"h":"d7a175de","n":232,"s":"Merge PR #232: feat(models): enforce per-account upstream cl"},{"h":"7c398968","n":233,"s":"Merge PR #233: fix(ci): migrate macOS x64 release runner to "},{"h":"ac045ba4","n":224,"s":"fix(chat,auth): CAPACITY 冷却写在 connect selector 维度 — #224 同构缺"},{"h":"db5c9023","n":217,"s":"chore(credits): 补记 wangergou777 两连(#217 B+ / #230 S)"},{"h":"d231d44f","n":230,"s":"Merge PR #230: fix(chat): DEVIN_CONNECT 路径补上 sticky 绑定 — 缓存亲"},{"h":"7d3bf3d3","n":224,"s":"Merge: warelik #224-#229 + codex content-policy a7 + #220 ca"},{"h":"5cf01162","n":219,"s":"feat(devin-connect): 默认解码 cache_read_tokens + 致谢 forrinzhao("},{"h":"94356fac","n":224,"s":"chore(credits): 致谢 warelik 一轮六连(#224-#229)"},{"h":"f434ef44","n":226,"s":"Merge PR #226"},{"h":"1783545b","n":225,"s":"Merge PR #225"},{"h":"014ead1c","n":224,"s":"Merge PR #224"},{"h":"a335d516","n":227,"s":"Merge PR #227"},{"h":"581927c8","n":229,"s":"Merge PR #229"},{"h":"f433c85b","n":228,"s":"Merge PR #228"},{"h":"175b1dd5","n":219,"s":"fix(responses): treat bare {role,content} input items as mes"},{"h":"b8644472","n":217,"s":"fix: 按 #217 模式排查修 4 处同类缺陷(默认模型/developer角色/缓存键/schema数组序)"},{"h":"04c95595","n":217,"s":"Merge PR #217: fix(responses): support responses-lite additi"},{"h":"0b4ffbf2","n":216,"s":"chore(credits): 为 warelik 贡献记录补 githubId(#216/#215)"},{"h":"fac765ac","n":216,"s":"docs(credits): 致谢 warelik(#216 MCP-gate + #215 IPv6 ETIMEDOU"},{"h":"313bb8ed","n":1,"s":"fix(security): #1 dashboard 回退按可信客户端IP判本地 + #7 lockout塌缩告警"},{"h":"4fc3c399","n":206,"s":"docs(contributors): credit clarezoe for the catalog-resync f"},{"h":"7f8d4038","n":36,"s":"feat(devin-connect): recovery observability + surface rotate"},{"h":"0dc3e232","n":204,"s":"test: lock in glm-5.2 gpt_native dialect routing (#204)"},{"h":"7ee509a7","n":204,"s":"fix(tool-emulation): use gpt_native dialect for glm-5.2 (#20"},{"h":"3e133bbe","n":192,"s":"merge pr #192: align ls install sources"},{"h":"542bb7cd","n":188,"s":"chore: add @The-five-stooges to contributors (PR #188)"},{"h":"f5425ff2","n":188,"s":"fix: sticky session binding breaks on streaming path + multi"},{"h":"a6b79365","n":181,"s":"feat: Cascade reuse optimization + HTTPS proxy + configurabl"},{"h":"acac78ab","n":184,"s":"feat: add Astraflow provider support (#184 by @ucloudnb666)"},{"h":"80900336","n":182,"s":"feat: improve dashboard batch import parsing (#182 by @lauvw"},{"h":"d72b093b","n":173,"s":"refactor(dashboard): UI cleanup (#173 by @datfooldive)"},{"h":"971eaca1","n":175,"s":"fix: cross-platform language server paths (#175 by @linqiche"},{"h":"946a14ff","n":92,"s":"fix: dashboard i18n centralization (PR #92, smeinecke) + Win"},{"h":"ce9dfba8","n":90,"s":"Merge pull request #90 from smeinecke/feat/add-account-proxy"},{"h":"b933fb8a","n":88,"s":"chore: 2.0.27 — i18n guard 收尾 + PR #88 #89 合并后"},{"h":"8d2bfc84","n":88,"s":"Merge pull request #88 from smeinecke/feat/allow-private-pro"},{"h":"e2c951b7","n":89,"s":"Merge pull request #89 from smeinecke/fix/missing-i18n"},{"h":"5e8c067d","n":72,"s":"chore(credits): add sandleft for PR #72 Codex Responses tool"},{"h":"60eab58d","n":65,"s":"ci(#65): add release workflow with GHCR + GitHub Release"},{"h":"54e78ad7","n":64,"s":"fix(#64): recover from canceled cascade panel transport"},{"h":"b5a88569","n":62,"s":"chore: bump 2.0.5 + add baily-zhang PR #62 to credits"},{"h":"4d51b5a9","n":62,"s":"fix(dashboard): prevent credits syntax crash (#62)"},{"h":"a71bdd1b","n":61,"s":"feat(dashboard): add PR #61 #58 #54 #53 to credits panel; pr"},{"h":"9dc019e3","n":58,"s":"fix: resolve nginx shared memory error and missing join impo"},{"h":"e078f35b","n":61,"s":"fix: prevent Opus 4.7 context bloat (#61)"},{"h":"ae4067c5","n":54,"s":"fix: bring Claude Code tool-call flow back to Anthropic-equi"},{"h":"ca770ab3","n":51,"s":"Merge pull request #51 from aict666/fix/tool-preamble-inject"},{"h":"2ae9d158","n":50,"s":"Merge PR #50 — disable Cascade reuse for tool-emulated turns"},{"h":"ff539c76","n":43,"s":"Merge pull request #43 from smeinecke/upd/english-translatio"},{"h":"9b3ef694","n":45,"s":"Merge pull request #45 from baily-zhang/fix-cascade-reuse-of"},{"h":"c8a86c5e","n":44,"s":"Merge pull request #44 from aict666/fix/preserve-authoritati"},{"h":"89512bf5","n":36,"s":"Merge pull request #36 from baily-zhang/fix-claude-code-cont"},{"h":"9f1de4b1","n":26,"s":"Merge pull request #26 from youfak/master"},{"h":"6002cc6d","n":20,"s":"Merge pull request #20 from motto1/fix/dashboard-windsurf-lo"},{"h":"60e6f15e","n":13,"s":"fix: 补 PR #13 + Antigravity 遗漏的 3 项"},{"h":"5c8ba744","n":13,"s":"fix: merge 8 fixes from PR #13,补 XSS 转义遗漏处"},{"h":"4ff1baff","n":1,"s":"Merge pull request #1 from dd373156/fix-opus-4-7-tier-access"}],"merge_prs":{"ff539c76":43,"e2c951b7":89,"ed1ac352":252,"ce9dfba8":90,"08861f7d":249,"c8a86c5e":44,"9f1de4b1":26,"ca770ab3":51,"89512bf5":36,"8d2bfc84":88,"6002cc6d":20,"4ff1baff":1,"9b3ef694":45,"3d831b9d":261,"1b1d0e5b":265,"e50fce36":266,"81108b07":264,"aa3db794":270},"tags_milestone":[["v1.4.0","2721cce4"],["v2.0.146","af0cdab9"],["v2.0.147","ea324036"],["v2.0.95","06dc8a57"],["v2.0.97","54707dd2"],["v3.0.0","a14c1efa"],["v3.1.0","1616b5eb"],["v3.2.6-rc4","c40ae89a"],["v3.2.9","f2a3f894"],["v3.5.0","81370f55"],["v3.9.0","8be3efd8"],["v3.9.13","095df441"],["v3.9.2","7d7d7acd"],["v3.9.21","51846e0e"],["v3.9.22","5a076930"],["v3.9.23","f1444e2c"],["v3.9.24","f674ef54"],["v3.9.25","2c190b9c"],["v3.9.26","ec22dbc7"],["v3.9.27","ddf44242"],["v3.9.28","26a4955f"],["v3.9.29","c751227c"],["v3.9.30","1a8e5067"],["v3.9.31","f49f4c10"],["v3.9.32","e1df2f51"],["v3.9.33","938a57e4"],["v3.9.34","0c085240"]],"tips":{"97046344":"pr/session-compaction-survival","1a4fbcce":"chore/cleanup-star-assets","51846e0e":"chore/release-3.9.21","8e1faf1a":"claude/nostalgic-bassi-0e1b1e","2e3daebb":"docs/env-switch-coverage","1dc3a3ed":"docs/history-ledger","1bb2a814":"docs/history-ledger-v2","1bb54803":"docs/ledger-viz","681bbc01":"docs/readme-polish","822680db":"feat/i18n-rings","9e8080a8":"feat/issue-244-model-sync","fa0d8006":"feat/landing-entry","2d194410":"feat/ledger-perf-entry","97e06268":"feat/research-gaps","26737c03":"feat/self-hosted-star","f0ce9e48":"feat/star-history","52cdf503":"feat/tree-3-modes","90ebd965":"feat/tree-collapse-desc","78d1caf5":"feat/viz-all-charts-interactive","ce3ebeba":"feat/viz-avatars-quotes-tree","41520cd5":"feat/viz-interactive-timeline","60b467d4":"feat/viz-nav-contribs-tree","e81982b9":"feat/viz-v2-ui","3ec28a98":"feat/wave0-polish","4d32dcb7":"feat/wave1-i18n","3cb843b0":"feat/wave2-tree-ctl","6dda9a1b":"feat/wave3-list","e5bcf351":"feat/wave5-ui-anim","0487ba2d":"fix/audit-round3-16-items","cefedfed":"fix/avatars-and-polish","c9924561":"fix/avatars-full","f487500c":"fix/dots-align","5407adfa":"fix/hero-logo","121fb9ba":"fix/issue-template-guard-sync","6289e2d1":"fix/keyframes-to-frames","2c90318b":"fix/ledger-secret-scan","ef8153c8":"fix/ledger-viz-facts","a2f3bc00":"fix/logo-dots","c6df903e":"fix/logo-dots-layout","31a83caa":"fix/rank-multi","995a0ad8":"fix/readme-star-dedup","0e61599b":"fix/star-pages-path","bb2ad5ea":"fix/tl-api-round2","9fce520a":"fix/tl-missing-append","830ac65c":"fix/tool-emulation-3defects","55b4a4d4":"fix/tree-css-rebuild","d437d358":"fix/tree-data","20608ae1":"fix/tree-lane-esc","99ad5ef6":"fix/tree-latest-gaps-modes","cad2892e":"fix/tree-render-crash","ec1a7e7f":"fix/tree-scroll-ring","4e0c14aa":"fix/viz-hours-and-grow","2d07bbf5":"fix/viz-rendering","dbbe5f52":"fix/viz-stat-count","e2b10cf9":"fix/w-orientation","a97ced3e":"fix/w-proportion","e3a175ad":"fix/wave4-review","298665cb":"fix/wave6-anim","7699ff30":"fix/wave7-cleanup","5fa32ee6":"fix/wave8-cleanup","4ee53b89":"gap-credit-cost","3e7651bb":"pr/modelconfig-reasoning-continuity","b861cefe":"pr/reasoning-dedup-incremental","dd0f95e3":"pr/reasoning-leak-trace","36d5a851":"pr/thinktext-reroute","5d92339c":"pr249","4d8bdc3b":"pr252","273c4254":"pr252-merged","f43f96a4":"revert/wave5-8","a7326da3":"worktree-wf_5d571be5-0af-1","6dcd0a91":"worktree-wf_eec3d174-00f-1","ba4b6591":"worktree-wf_eec3d174-00f-2","27cb2306":"worktree-wf_eec3d174-00f-3","9aa22ced":"worktree-wf_eec3d174-00f-4","e42b4b93":"origin/feat/research-gaps","c751227c":"v3.9.29","1a8e5067":"v3.9.30","f49f4c10":"v3.9.31","e1df2f51":"v3.9.32","938a57e4":"v3.9.33","0c085240":"v3.9.34","71982d5a":"HEAD"},"gaps":[[1778688000,1779379200,8,"2026-05-13 → 2026-05-22"],[1780070400,1780416000,4,"2026-05-29 → 2026-06-03"],[1781366400,1781625600,3,"2026-06-13 → 2026-06-17"],[1781798400,1782144000,4,"2026-06-18 → 2026-06-23"],[1782230400,1782662400,5,"2026-06-23 → 2026-06-29"],[1784304000,1784822400,6,"2026-07-17 → 2026-07-24"]]};
const D_CONTRIBS = [{"login":"lovedxc","prs":2,"merged":1,"add":122,"commits":1,"grade":"A","title":"fix(devin-connect): merge consecutive same-role text turns before wire encoding","summary":"连续同 role 纯文本在 wire 前合并,避免 run≥3 invalid_argument。#267 repeated-#6 被回绝。"},{"login":"justhil","prs":2,"merged":2,"add":226,"commits":2,"grade":"S","title":"fix(responses): normalize multimodal content in tool call output items","summary":"工具输出图进 Connect #10;nginx 默认 1m 是真 413。#261 交错并行 tool_calls。"},{"login":"Jakevin","prs":1,"merged":1,"add":154,"commits":2,"grade":"A","title":"feat(models,devin-acp): add swe-2 + route Devin Local-only models through ACP","summary":"swe-2 进 Connect 目录;gpt-5.6 Local 显式 opt-in。FREE_TIER 仍 swe-1-6-slow。"},{"login":"Rostopher","prs":1,"merged":1,"add":11,"commits":1,"grade":"B+","title":"fix(dashboard): local import misses Devin-desktop logins","summary":"桌面端 Devin 目录 + 128KB 静默跳过抬到 512KB。"},{"login":"warelik","prs":15,"merged":12,"add":4560,"commits":7,"grade":"A","title":"feat(session-continuity): survive single-dialog history compaction — root-anchor fallback","summary":"kimi 之类的客户端会在长对话里压缩历史:0/31 保留的 pair 逐字或规范都存活不下来,客户端改写保留尾 —— 但对话的第一轮输入逐字存活。这个 PR 用 root anchor(首轮输入哈希)索引每个 state,compacti"},{"login":"baily-zhang","prs":6,"merged":5,"add":674,"commits":0,"grade":"S","title":"fix: fingerprint 只 hash 稳定轮,消除 0% 复用命中","summary":"Claude Code 每次重构 assistant content(数组 ↔ 字符串 ↔ 含 tool_use blocks)而 system prompt 又含动态项目状态,老 fingerprint 把这两者 hash 进去导致永不匹"},{"login":"smeinecke","prs":5,"merged":4,"add":2948,"commits":0,"grade":"B","title":"Fix missing/broken i18n","summary":"修掉 Dashboard 登录流里 error code 直接以裸串显示的问题 —— 像 ERR_NO_PASSWORD_SET 这类直接糊在 toast 和登录历史里,没翻成人话。这个 PR 把 toasts / result 显示 / "},{"login":"aict666","prs":5,"merged":4,"add":796,"commits":3,"grade":"S+","title":"fix(auth): 阻止 non-premium 调用把 Pro/Trial 降级为 free","summary":"updateCapability 在每次成功 chat 后无条件 inferTier() 覆写 account.tier。inferTier 只认 claude-opus-4.6 / sonnet-4.6 作为 Pro 证据,Pro 账号只"},{"login":"andya1lan","prs":5,"merged":3,"add":1354,"commits":0,"grade":"B+","title":"fix(ci): migrate macOS x64 release runner to macos-26-intel","summary":"四行改动,价值全在诊断。GitHub 已于 2025-12-04 下线 macos-13 runner 镜像,而 release.yml 的 x64 job 一直写着 runs-on: macos-13 —— v3.9.1 到 v3.9.4"},{"login":"dwgx","prs":2,"merged":2,"add":2833,"commits":1162,"grade":"GOD","title":"feat: WindsurfAPI — 零 npm 依赖反代","summary":"项目作者。从 0 到 v2.0.x 一路把 Windsurf 的 Cascade gRPC 反代成 OpenAI / Anthropic / Responses 三协议、零 npm 依赖、双皮 dashboard、多账号池 + tier 管"},{"login":"wangergou777","prs":2,"merged":2,"add":299,"commits":2,"grade":"S","title":"fix(chat): DEVIN_CONNECT 路径补上 sticky 绑定 — 缓存亲和此前完全失效","summary":"第二次出手直接到 S 级。setStickyBinding 只在两处 Cascade 成功路径被调用,DEVIN_CONNECT 路径只查不写 —— connect 部署开 STICKY_SESSION_ENABLED=1 完全空转(23 "},{"login":"abwuge","prs":2,"merged":2,"add":105,"commits":2,"grade":"B+","title":"fix: nginx shared memory zone + 缺失的 join import","summary":"新人首次贡献就解了部署死锁。docker-compose 起来后所有 windsurf 容器(windsurf-lb + 三个 API 副本)持续 Restart:nginx upstream 用了 `server windsurf-api"},{"login":"The-five-stooges","prs":2,"merged":1,"add":320,"commits":0,"grade":"S","title":"fix: sticky session binding breaks on streaming path + multi-user isolation with body.user","summary":"修复 sticky session 在流式路径的两个关键问题:① waitForAccount 在流式 retry 路径缺少 callerKey 参数,导致 sticky 绑定在流式请求中不生效;② 新增 body.user 多用户隔离机制"},{"login":"wjurkowlaniec","prs":1,"merged":1,"add":383,"commits":0,"grade":"","title":"","summary":""},{"login":"lovivi","prs":1,"merged":1,"add":10,"commits":0,"grade":"B+","title":"fix(tool-emulation): use gpt_native dialect for glm-5.2","summary":"首次贡献就解了 glm-5.2 的工具调用哑火。glm 系列默认走 glm47 那套 XML tool-call 标记,但 glm-5.2 直接无视标记、用纯文本作答,工具调用根本 emit 不出来。lovivi 在 pickToolDia"},{"login":"brandonedley","prs":1,"merged":1,"add":53,"commits":0,"grade":"A","title":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","summary":"PR #201 把 GLM 5.2 和 Kimi K2.7 补进模型 catalog,同时同步 README、英文 README、package description、模型文档生成检查和 catalog correctness 测试。模型"},{"login":"MatrixNeoKozak","prs":1,"merged":1,"add":1,"commits":0,"grade":"B","title":"fix: return 400 for malformed JSON in dashboard API","summary":"PR #195 修掉 dashboard API malformed JSON 仍按 200 返回的问题。改动很小,但错误语义重要:前端、脚本和自动化调用方现在能用 HTTP status 正确区分请求体格式错误,而不是从一个成功响应里猜 "},{"login":"ucloudnb666","prs":1,"merged":1,"add":30,"commits":1,"grade":"B+","title":"feat: add Astraflow provider support","summary":"让 WindsurfAPI 支持 Astraflow 作为第三方提供商路由。config.js 新增 ASTRAFLOW_API_KEY 配置项,models.js 注册 astraflow/* 模型前缀,30 行精准修改。"},{"login":"lauvww","prs":1,"merged":1,"add":588,"commits":1,"grade":"A","title":"feat: improve dashboard batch import parsing","summary":"重写了 Dashboard 的账号批量导入解析器 (import-parser.js):支持 JSON / CSV / 纯文本多种格式混合粘贴,自动检测分隔符,容错处理格式不一致的行。588 行新增含完整测试覆盖和中英双语 i18n。"},{"login":"Fermiz","prs":1,"merged":1,"add":323,"commits":0,"grade":"A+","title":"feat: Cascade reuse optimization + HTTPS proxy + configurable pool size","summary":"三件事一个 PR:① 单用户场景下 Cascade 复用优化 — 跳过多余的账号轮询直接命中唯一账号;② HTTPS 代理中间层 (https-proxy.js) 让反代本身也能跑 TLS;③ conversation-pool 大小可配置"},{"login":"linqichenggg","prs":1,"merged":1,"add":123,"commits":0,"grade":"A","title":"fix: align language server paths across platforms","summary":"Windows / macOS / Linux 三平台的 LS 二进制路径、数据目录、安装脚本全部对齐。之前 macOS 用 ~/Library 路径但 install-ls.sh 写的是 Linux 路径,Windows 更是直接找不到 "},{"login":"datfooldive","prs":1,"merged":1,"add":1177,"commits":0,"grade":"A","title":"refactor(dashboard): UI cleanup","summary":"Dashboard 前端大重构:1177 行新增 / 465 行删除,全部集中在 index.html。清理了冗余样式、统一了组件风格、优化了卡片布局和响应式适配。"},{"login":"youfak","prs":1,"merged":1,"add":162,"commits":2,"grade":"A+","title":"feat: Docker 全套部署 + DATA_DIR + LS 重启兜底","summary":"Dockerfile / docker-compose.yml / .dockerignore / .gitattributes 一整套,配 DATA_DIR 配置项把 accounts.json / stats.json / logs/ "},{"login":"motto1","prs":1,"merged":1,"add":459,"commits":1,"grade":"A","title":"fix(dashboard): 补上 Windsurf 官网实际使用的 Auth1 登录链路","summary":"官网可以登但 Dashboard 登录失败 —— 原因是旧 Firebase 路径已不是主链路。逆向补出 /_devin-auth/connections → /_devin-auth/password/login → WindsurfPo"},{"login":"dd373156","prs":1,"merged":1,"add":1,"commits":0,"grade":"B+","title":"fix(models): 让 pro tier 识别到云端动态合并的模型","summary":"MODEL_TIER_ACCESS.pro 引用的是 ALL_MODEL_KEYS —— 模块加载时的 Object.keys(MODELS) 快照。mergeCloudModels 启动后加进来的五个 claude-opus-4-7-* "},{"login":"forrinzhao","prs":2,"merged":0,"add":0,"commits":0,"grade":"B+","title":"fix: neutralize codex apply_patch tool description to avoid content-policy block","summary":"发现 codex 的 apply_patch 工具描述里 \"FREEFORM tool, so do not wrap the patch in JSON.\" 这句会触发 Devin content policy,飞书 codex bot "},{"login":"moduvoice","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"clarezoe","prs":1,"merged":0,"add":0,"commits":0,"grade":"S","title":"fix: retry cloud model catalog sync when first account becomes active","summary":"根因修复 + 带全套测试,一次到位。fetchAndMergeModelCatalog() 只在 initAuth() 启动时跑一次:如果那一刻池里没有活跃账号,云端 catalog 同步就被整个跳过、再也不重试 —— 后来才加进来的账号永"},{"login":"limweb","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"kevin5251984","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"UntaDotMy","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"Kloping","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"Await-d","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"suhaihui-git","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"chukangkang","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"RogueBinary","prs":1,"merged":0,"add":0,"commits":0,"grade":"","title":"","summary":""},{"login":"Yuuqq","prs":1,"merged":0,"add":0,"commits":0,"grade":"B+","title":"fix: Windows compatibility + 6 bug fixes","summary":"Windows 上跑 WindsurfAPI 的第一手反馈。#73 一次扫了 7 处 bug,处置时挑了三处方向干净的落进 commit `44ad502`,作者署名走 Co-authored-by trailer:① `src/sanit"},{"login":"sandleft","prs":1,"merged":0,"add":0,"commits":1,"grade":"S","title":"fix: add Codex Responses tool compatibility","summary":"我之前那条 #63 已经把 sandleft 标过 S 级,#72 是第二次出手,质量稳。Codex Responses API 的四种非 function tool(custom / namespace / web_search / to"},{"login":"colin1112a","prs":1,"merged":0,"add":0,"commits":0,"grade":"B+","title":"fix: 一次性审计并提交 15 个安全 / 并发 / 资源管理 bug","summary":"系统性扫了一遍仓库提了 CRITICAL/HIGH/MEDIUM 15 条修复建议:XSS 转义、shell 注入防护、10MB body 上限、auth 路由顺序、gRPC 双回调保护、LS pool 并发竞态、HTTP/2 frame "}];
/* ============ 单发贡献者总结(1 PR 的改了什么) ============ */
const D_ONE_OFF = {"JoShMiQueL":[[254,"feat(devin-connect): collapse system messages into user turns — bypass field #2 content policy"]],"wjurkowlaniec":[[252,"Fix/swe family tool dialect"]],"moduvoice":[[211,"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher"]],"clarezoe":[[206,"fix: retry cloud model catalog sync when first account becomes active"]],"lovivi":[[204,"fix(tool-emulation): use gpt_native dialect for glm-5.2"]],"brandonedley":[[201,"feat: add GLM 5.2 and Kimi K2.7 catalog entries"]],"limweb":[[199,"update"]],"kevin5251984":[[198,"feat(dashboard): default mode."]],"MatrixNeoKozak":[[195,"fix: return 400 for malformed JSON in dashboard API"]],"ucloudnb666":[[184,"feat: add Astraflow provider support"]],"lauvww":[[182,"feat: improve dashboard batch import parsing"]],"Fermiz":[[181,"feat: Cascade reuse optimization for single-user setups & HTTPS proxy"]],"linqichenggg":[[175,"fix: align language server paths across platforms"]],"datfooldive":[[173,"refactor(dashboard): UI cleanup"]],"UntaDotMy":[[171,"feat: add Codex Responses WebSocket support"]],"Kloping":[[161,"fix(dashboard): dashboard account management page width adaptive"]],"Await-d":[[144,"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login"]],"suhaihui-git":[[142,"新增响应缓存关闭开关"]],"chukangkang":[[139,"fix: add X-Devin-Auth1-Token header to PostAuth request"]],"RogueBinary":[[80,"Harden defaults and add secure startup launcher"]],"Yuuqq":[[73,"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)"]],"sandleft":[[72,"fix: 为 Codex 增加 Responses 工具兼容性"]],"youfak":[[26,"增加了 Docker 部署支持"]],"motto1":[[20,"修复 Dashboard 登录取号:支持 Auth1 登录流并新增批量导入"]],"colin1112a":[[13,"fix: 修复 15 个安全和逻辑 bug (CRITICAL/HIGH/MEDIUM)"]],"dd373156":[[1,"fix(models): make pro tier include dynamically merged cloud models"]]};
/* ============ 国际化(5 语言:zh / en / zh-TW / ja / ko) ============ */
const I18N = {"hero_title":{"zh":"WindsurfAPI · <span class=\"accent\">工程的年轮</span>","en":"WindsurfAPI · <span class=\"accent\">The Rings of Engineering</span>","zh-TW":"WindsurfAPI · <span class=\"accent\">工程的年輪</span>","ja":"WindsurfAPI · <span class=\"accent\">プロジェクトの年輪</span>","ko":"WindsurfAPI · <span class=\"accent\">프로젝트의 나이테</span>","fr":"WindsurfAPI · <span class=\"accent\">Les Anneaux de l'Ingénierie</span>","de":"WindsurfAPI · <span class=\"accent\">Die Ringe des Engineerings</span>","es":"WindsurfAPI · <span class=\"accent\">Los Anillos de la Ingeniería</span>"},"hero_sub":{"zh":"2026-04-09 → 2026-09-15 · 从单机反代脚本到 2996★ 的网关 · 五个月的全部历史","en":"2026-04-09 → 2026-09-15 · From a lone reverse-proxy script to a 2996★ gateway · five months of history in full","zh-TW":"2026-04-09 → 2026-09-15 · 從單機反代腳本到 2996★ 的閘道 · 五個月的全部歷史","ja":"2026-04-09 → 2026-09-15 · ローカルリバースプロキシスクリプトから 2996★ のゲートウェイへ · 5ヶ月の全歴史","ko":"2026-04-09 → 2026-09-15 · 단일 머신 리버스 프록시 스크립트에서 2996★ 게이트웨이까지 · 5개월의 모든 역사","fr":"2026-04-09 → 2026-09-15 · D'un script de reverse proxy isolé à une passerelle à 2996★ · cinq mois d'histoire, en intégralité","de":"2026-04-09 → 2026-09-15 · Vom einsamen Reverse-Proxy-Skript zu einer Gateway mit 2996★ · vier Monate Geschichte in voller Länge","es":"2026-04-09 → 2026-09-15 · De un script de proxy inverso solitario a una pasarela de 2996★ · cinco meses de historia al completo"},"badge_commits":{"zh":"1387 提交","en":"1387 commits","zh-TW":"1387 提交","ja":"1387 commits","ko":"1387 커밋","fr":"1387 commits","de":"1387 Commits","es":"1387 commits"},"badge_versions":{"zh":"196 版本","en":"196 versions","zh-TW":"196 版本","ja":"196 versions","ko":"191 버전","fr":"196 versions","de":"191 Versionen","es":"191 versiones"},"badge_prs":{"zh":"82 PR","en":"82 PRs","zh-TW":"82 PR","ja":"82 PRs","ko":"82 PR","fr":"82 PRs","de":"82 PRs","es":"82 PRs"},"badge_issues":{"zh":"180 issue","en":"180 issues","zh-TW":"180 issue","ja":"180 issues","ko":"180 issue","fr":"180 issues","de":"179 Issues","es":"180 issues"},"badge_selfharm":{"zh":"29 自伤","en":"29 self-inflicted wounds","zh-TW":"29 自傷","ja":"28 自爆","ko":"자업자득 28건","fr":"28 blessures auto-infligées","de":"28 Eigentore","es":"28 heridas autoinfligidas"},"nav_overview":{"zh":"总览","en":"Overview","zh-TW":"總覽","ja":"概要","ko":"개요","fr":"Vue d'ensemble","de":"Übersicht","es":"Resumen"},"nav_rhythm":{"zh":"节奏","en":"Rhythm","zh-TW":"節奏","ja":"リズム","ko":"리듬","fr":"Rythme","de":"Rhythmus","es":"Ritmo"},"nav_components":{"zh":"构成","en":"Composition","zh-TW":"構成","ja":"構成","ko":"구성","fr":"Composition","de":"Zusammensetzung","es":"Composición"},"nav_contributors":{"zh":"贡献者","en":"Contributors","zh-TW":"貢獻者","ja":"コントリビューター","ko":"기여자","fr":"Contributeurs","de":"Beitragende","es":"Contribuyentes"},"nav_gittree":{"zh":"Git 树","en":"Git Tree","zh-TW":"Git 樹","ja":"Git ツリー","ko":"Git 트리","fr":"Arbre Git","de":"Git-Baum","es":"Árbol Git"},"nav_selfharm":{"zh":"自伤","en":"Self-Inflicted","zh-TW":"自傷","ja":"自爆","ko":"자업자득","fr":"Auto-infligées","de":"Eigentore","es":"Autolesiones"},"esc_hint":{"zh":"ESC 切换纯净字幕视图","en":"Press ESC for the clean caption view","zh-TW":"ESC 切換純淨字幕視圖","ja":"ESC で字幕なしビューに切替","ko":"ESC로 순수 자막 뷰 전환","fr":"Appuyez sur ÉCHAP pour la vue sous-titres épurée","de":"ESC drücken für die Clean-Untertitel-Ansicht","es":"Pulsa ESC para la vista limpia de subtítulos"},"stat_commits":{"zh":"总提交(master)","en":"Total commits (4 months)","zh-TW":"總提交(4 個月)","ja":"総コミット数(5ヶ月)","ko":"총 커밋(5개월)","fr":"Commits au total (4 mois)","de":"Commits gesamt (4 Monate)","es":"Commits totales (4 meses)"},"stat_commits_s":{"zh":"1387 条 commit 全量 + 合并","en":"All 1387 commits + merges","zh-TW":"1387 條 commit 全量 + 合併","ja":"1387 commit 全量 + merge 分","ko":"commit 1387개 전체 + merge","fr":"Les 1387 commits complets + fusions","de":"Alle 1387 Commits + Merges","es":"Los 1387 commits completos + merges"},"stat_versions":{"zh":"版本 tag","en":"Version tags","zh-TW":"版本 tag","ja":"バージョン tag","ko":"버전 tag","fr":"Tags de version","de":"Version-Tags","es":"Tags de versión"},"stat_versions_s":{"zh":"v1.4.0 → v3.9.29","en":"v1.4.0 → v3.9.29","zh-TW":"v1.4.0 → v3.9.29","ja":"v1.4.0 → v3.9.29","ko":"v1.4.0 → v3.9.29","fr":"v1.4.0 → v3.9.29","de":"v1.4.0 → v3.9.29","es":"v1.4.0 → v3.9.29"},"stat_prs":{"zh":"PR(47 合并)","en":"PRs (47 merged)","zh-TW":"PR(47 合併)","ja":"PR(47 merge)","ko":"PR(47건 merge)","fr":"PRs (47 fusionnées)","de":"PRs (47 gemergt)","es":"PRs (47 fusionados)"},"stat_prs_s":{"zh":"34 位外部贡献者","en":"34 external contributors","zh-TW":"34 位外部貢獻者","ja":"外部コントリビューター 34 名","ko":"외부 기여자 34명","fr":"34 contributeurs externes","de":"34 externe Beitragende","es":"34 contribuyentes externos"},"stat_issues":{"zh":"issue(88% 解决)","en":"Issues (88% resolved)","zh-TW":"issue(88% 解決)","ja":"issue(88% 解決)","ko":"issue(88% 해결)","fr":"Issues (88 % résolues)","de":"Issues (88 % gelöst)","es":"Issues (88 % resueltas)"},"stat_issues_s":{"zh":"口径:COMPLETED/总数","en":"Definition: COMPLETED / total","zh-TW":"口徑:COMPLETED/總數","ja":"定義:COMPLETED / 総数","ko":"산정 기준: COMPLETED/총수","fr":"Définition : COMPLETED / total","de":"Definition: COMPLETED / Gesamtzahl","es":"Definición: COMPLETED / total"},"stat_selfharm":{"zh":"自伤/返工事件","en":"Self-inflicted wounds / rework","zh-TW":"自傷/返工事件","ja":"自爆・手戻り事件","ko":"자업자득/되돌림 사건","fr":"Blessures auto-infligées / reprise","de":"Eigentore / Nacharbeit","es":"Heridas autoinfligidas / retrabajo"},"stat_selfharm_s":{"zh":"8 起事故级","en":"8 incident-grade","zh-TW":"8 起事故級","ja":"事故級 8 件","ko":"사고급 8건","fr":"8 de niveau incident","de":"8 auf Incident-Niveau","es":"8 de nivel incidente"},"stat_contributors":{"zh":"外部贡献者","en":"External contributors","zh-TW":"外部貢獻者","ja":"外部コントリビューター","ko":"외부 기여자","fr":"Contributeurs externes","de":"Externe Beitragende","es":"Contribuyentes externos"},"stat_contributors_s":{"zh":"warelik 15 个 S×7 领跑","en":"warelik in the lead — 15 PRs, S×7","zh-TW":"warelik 15 個 S×7 領跑","ja":"warelik が S×7 15 個で首位","ko":"warelik, S×7 15건으로 선두","fr":"warelik en tête — 15 PRs, S×7","de":"warelik führt — 15 PRs, S×7","es":"warelik a la cabeza — 15 PRs, S×7"},"stat_fix":{"zh":"提交是 fix","en":"Commits that are fixes","zh-TW":"提交是 fix","ja":"fix の割合","ko":"커밋 중 fix 비율","fr":"Commits qui sont des fixes","de":"Commits, die Fixes sind","es":"Commits que son fix"},"stat_fix_s":{"zh":"525/1387","en":"525/1387","zh-TW":"525/1387","ja":"525/1387","ko":"525/1387","fr":"525/1387","de":"525/1387","es":"525/1387"},"stat_interval":{"zh":"最短发版间隔","en":"Shortest release interval","zh-TW":"最短發版間隔","ja":"最短リリース間隔","ko":"최단 릴리스 간격","fr":"Intervalle de release le plus court","de":"Kürzestes Release-Intervall","es":"Intervalo de release más corto"},"stat_interval_s":{"zh":"v3.9.16→v3.9.17","en":"v3.9.16→v3.9.17","zh-TW":"v3.9.16→v3.9.17","ja":"v3.9.16→v3.9.17","ko":"v3.9.16→v3.9.17","fr":"v3.9.16→v3.9.17","de":"v3.9.16→v3.9.17","es":"v3.9.16→v3.9.17"},"stat_peak":{"zh":"单日峰值","en":"Peak day","zh-TW":"單日峰值","ja":"1日あたりのピーク","ko":"일일 최고치","fr":"Pic journalier","de":"Tagesrekord","es":"Pico diario"},"stat_peak_s":{"zh":"08-04 · 一天五版","en":"08-04 · five releases in one day","zh-TW":"08-04 · 一天五版","ja":"08-04 · 1日5リリース","ko":"08-04 · 하루 5버전","fr":"08-04 · cinq releases en un jour","de":"08-04 · fünf Releases an einem Tag","es":"08-04 · cinco releases en un día"},"months_title":{"zh":"月度提交节奏","en":"Monthly commit rhythm","zh-TW":"月度提交節奏","ja":"月次コミットリズム","ko":"월간 커밋 리듬","fr":"Rythme mensuel des commits","de":"Monatlicher Commit-Rhythmus","es":"Ritmo mensual de commits"},"months_sub":{"zh":"4 月成型冲刺(339)· 5 月停滞深水(98)· 6 月换轨(141)· 7-8 月高速期(328/405)—— 悬停柱子看详情,点击锁定可点链接","en":"April: the shaping sprint (339) · May: the slow season (98) · June: the pivot (141) · Jul-Aug: the fast lane (328/405) — hover a bar for details, click to pin and make links clickable","zh-TW":"4 月成型衝刺(339)· 5 月停滯深水(98)· 6 月換軌(141)· 7-8 月高速期(328/405)—— 懸停柱子看詳情,點擊鎖定可點連結","ja":"4月は成形スプリント(339)· 5月は停滞の深み(98)· 6月は軌道変換(141)· 7-8月は高速期(328/405)—— バーにホバーで詳細、クリックで固定&リンク可能","ko":"4월 형성 스프린트(339) · 5월 정체의 늪(98) · 6월 궤도 변경(141) · 7-8월 고속기(328/405) —— 막대에 호버하면 상세, 클릭 고정 후 링크 가능","fr":"Avril : le sprint fondateur (339) · Mai : la morte-saison (98) · Juin : le virage (141) · Juil-août : la voie rapide (328/405) — survolez une barre pour les détails, cliquez pour épingler et rendre les liens cliquables","de":"April: der Formations-Sprint (339) · Mai: die Flaute (98) · Juni: der Kurswechsel (141) · Juli-Aug: die Überholspur (328/405) — Balken hovern für Details, klicken zum Anpinnen; Links werden klickbar","es":"Abril: el sprint fundacional (339) · Mayo: la temporada muerta (98) · Junio: el giro (141) · Jul-ago: el carril rápido (328/405) — pasa el ratón por una barra para ver detalles, haz clic para fijarla y activar los enlaces"},"weeks_title":{"zh":"每周脉搏","en":"Weekly pulse","zh-TW":"每週脈搏","ja":"週間の鼓動","ko":"주간 맥박","fr":"Pouls hebdomadaire","de":"Wöchentlicher Puls","es":"Pulso semanal"},"weeks_sub":{"zh":"W17(04-21 前后)提交洪峰 233 条(当周 issue 报告 50 个)· W20-W21(5 月中旬)空窗 · W32(08-04)爆发 244 条","en":"W17 (around 04-21) saw a 233-commit flood (50 issues reported that week) · W20-W21 (mid-May) went quiet · W32 (08-04) exploded with 244","zh-TW":"W17(04-21 前後)提交洪峰 233 條(當週 issue 報告 50 個)· W20-W21(5 月中旬)空窗 · W32(08-04)爆發 244 條","ja":"W17(04-21 前後)にコミット殺到 233 件(当週の issue 報告 50 件)· W20-W21(5月中旬)は空白 · W32(08-04)に爆発的な 244 件","ko":"W17(04-21 무렵) 커밋 홍수 233개(그 주 issue 리포트 50개) · W20-W21(5월 중순) 공백 · W32(08-04) 폭발 244개","fr":"W17 (vers 04-21) : raz-de-marée de 233 commits (50 issues signalées cette semaine-là) · W20-W21 (mi-mai) : zone morte · W32 (08-04) : explosion à 244","de":"W17 (um den 04-21) meldete eine 233-Commit-Flut (50 Issues in dieser Woche) · W20-W21 (Mitte Mai) war Flaute · W32 (08-04) explodierte mit 244","es":"W17 (alrededor del 04-21): inundación de 233 commits (50 issues reportadas esa semana) · W20-W21 (mediados de mayo): ventana vacía · W32 (08-04): explosión con 244"},"kinds_title":{"zh":"提交类型构成","en":"Commit type breakdown","zh-TW":"提交類型構成","ja":"コミット種別の構成","ko":"커밋 유형 구성","fr":"Répartition des types de commits","de":"Commit-Typen im Überblick","es":"Desglose por tipo de commit"},"kinds_sub":{"zh":"fix 占 40% —— 这个项目修 bug 的时间比写功能多","en":"fix accounts for 40% — this project spends more time fixing bugs than writing features","zh-TW":"fix 佔 40% —— 這個專案修 bug 的時間比寫功能多","ja":"fix が 40% —— このプロジェクトは機能を書くよりバグ直しの時間が多い","ko":"fix가 40% —— 이 프로젝트는 기능을 만들 시간보다 버그를 고치는 시간이 더 많다","fr":"fix représente 40 % — ce projet passe plus de temps à corriger des bugs qu'à écrire des fonctionnalités","de":"fix macht 40 % aus — dieses Projekt verbringt mehr Zeit mit Bugfixes als mit Features","es":"fix representa el 40 % — este proyecto pasa más tiempo corrigiendo bugs que escribiendo features"},"days_title":{"zh":"单日峰值 Top 10","en":"Top 10 peak days","zh-TW":"單日峰值 Top 10","ja":"1日ピーク Top 10","ko":"일일 최고치 Top 10","fr":"Top 10 des pics journaliers","de":"Top 10 der Tagesrekorde","es":"Top 10 de picos diarios"},"days_sub":{"zh":"08-04 单日 84 条(v3.9.10→v3.9.14 五连发)· 全史最短发版间隔 08-05 v3.9.16→v3.9.17 仅 14 分 49 秒","en":"08-04: 84 commits in one day (v3.9.10→v3.9.14, five straight releases) · the shortest release gap ever: v3.9.16→v3.9.17 on 08-05, just 14m 49s","zh-TW":"08-04 單日 84 條(v3.9.10→v3.9.14 五連發)· 史上最短發版間隔 08-05 v3.9.16→v3.9.17 僅 14 分 49 秒","ja":"08-04 は1日 84 件(v3.9.10→v3.9.14 の5連投)· 史上最短リリース間隔 08-05 v3.9.16→v3.9.17 わずか 14分49秒","ko":"08-04 하루 84개(v3.9.10→v3.9.14 5연발) · 사상 최단 릴리스 간격 08-05 v3.9.16→v3.9.17 단 14분 49초","fr":"08-04 : 84 commits en un jour (v3.9.10→v3.9.14, cinq releases d'affilée) · l'intervalle de release le plus court de l'histoire : v3.9.16→v3.9.17 le 08-05, 14 min 49 s seulement","de":"08-04: 84 Commits an einem Tag (v3.9.10→v3.9.14, fünf Releases in Folge) · das kürzeste Release-Intervall überhaupt: v3.9.16→v3.9.17 am 08-05, gerade mal 14 Min 49 Sek","es":"08-04: 84 commits en un día (v3.9.10→v3.9.14, cinco releases seguidas) · el intervalo de release más corto de la historia: v3.9.16→v3.9.17 el 08-05, solo 14 min 49 s"},"hours_title":{"zh":"一天 24 小时","en":"The 24-hour day","zh-TW":"一天 24 小時","ja":"一日 24 時間","ko":"하루 24시간","fr":"Les 24 heures de la journée","de":"Der 24-Stunden-Tag","es":"Las 24 horas del día"},"hours_sub":{"zh":"凌晨 2 点还在提交 —— 该项目没有作息表","en":"Committing at 2 AM — this project has no sleep schedule","zh-TW":"凌晨 2 點還在提交 —— 這個專案沒有作息表","ja":"深夜2時でも commit —— このプロジェクトに就寝時間はない","ko":"새벽 2시에도 커밋 중 —— 이 프로젝트에는 정해진 생활 리듬이 없다","fr":"Des commits à 2 h du matin — ce projet n'a pas d'horaires de sommeil","de":"Um 2 Uhr nachts wird noch committet — dieses Projekt kennt keinen Schlafrhythmus","es":"Commits a las 2 de la madrugada — este proyecto no tiene horario de sueño"},"tags_title":{"zh":"版本节奏","en":"Release rhythm","zh-TW":"版本節奏","ja":"バージョンのリズム","ko":"버전 리듬","fr":"Rythme des versions","de":"Release-Rhythmus","es":"Ritmo de releases"},"tags_sub":{"zh":"191 个 tag:4-5 月 52/52 个(每日一版),8 月 22 个(v3.9.8→v3.9.29)","en":"191 tags: 52/52 in Apr-May (one release a day), 22 in Aug (v3.9.8→v3.9.29)","zh-TW":"191 個 tag:4-5 月 52/52 個(每日一版),8 月 22 個(v3.9.8→v3.9.29)","ja":"191 tag:4-5月は 52/52 個(毎日1版)、8月は 22 個(v3.9.8→v3.9.29)","ko":"tag 191개: 4-5월 52/52개(하루 1버전), 8월 22개(v3.9.8→v3.9.29)","fr":"191 tags : 52/52 en avril-mai (une release par jour), 22 en août (v3.9.8→v3.9.29)","de":"191 Tags: 52/52 im April-Mai (ein Release pro Tag), 22 im August (v3.9.8→v3.9.29)","es":"191 tags: 52/52 en abril-mayo (una release al día), 22 en agosto (v3.9.8→v3.9.29)"},"phases_title":{"zh":"四个阶段","en":"Four phases","zh-TW":"四個階段","ja":"4つのフェーズ","ko":"네 개의 단계","fr":"Quatre phases","de":"Vier Phasen","es":"Cuatro fases"},"phases_sub":{"zh":"从脚本到网关的演化轨迹","en":"The arc from script to gateway","zh-TW":"從腳本到閘道的演化軌跡","ja":"スクリプトからゲートウェイへの進化の軌跡","ko":"스크립트에서 게이트웨이로 진화한 궤적","fr":"La trajectoire du script à la passerelle","de":"Der Bogen vom Skript zur Gateway","es":"El arco del script a la pasarela"},"phase1_t":{"zh":"成型期","en":"Taking Shape","zh-TW":"成型期","ja":"成形期","ko":"형성기","fr":"Prise de forme","de":"Formation","es":"Tomando forma"},"phase1_d":{"zh":"单机脚本→完整网关 · issue 洪峰 82 个 · 98% 解决率 · 单日最高 53 条","en":"Lone script → full gateway · 82-issue flood · 98% resolution rate · 53 commits on the peak day","zh-TW":"單機腳本→完整閘道 · issue 洪峰 82 個 · 98% 解決率 · 單日最高 53 條","ja":"ローカルスクリプト→完全なゲートウェイ · issue 殺到 82 個 · 98% 解決率 · 1日最大 53 件","ko":"단독 스크립트→완전한 게이트웨이 · issue 폭풍 82개 · 98% 해결률 · 하루 최대 53개","fr":"Script solitaire → passerelle complète · raz-de-marée de 82 issues · 98 % résolues · 53 commits au pic","de":"Einsames Skript → vollwertige Gateway · 82er-Issue-Flut · 98 % Lösungquote · 53 Commits am Rekordtag","es":"Script solitario → pasarela completa · inundación de 82 issues · 98 % resueltas · 53 commits en el día pico"},"phase2_t":{"zh":"深水区","en":"The Deep End","zh-TW":"深水區","ja":"深みの時期","ko":"정체의 늪","fr":"Le grand bain","de":"Das tiefe Wasser","es":"El fondo del charco"},"phase2_d":{"zh":"工具兼容/上下文/粘性会话 · 中旬 8 天空窗(离线研究)· #115 六天四稿","en":"Tool compatibility / context / sticky sessions · an 8-day mid-month gap (offline research) · #115: four drafts in six days","zh-TW":"工具相容/上下文/黏性會話 · 中旬 8 天空窗(離線研究)· #115 六天四稿","ja":"ツール互換/コンテキスト/sticky セッション · 中旬は 8 日間の空白(オフライン研究)· #115 は6日4稿","ko":"도구 호환성/컨텍스트/스티키 세션 · 중순 8일 공백(오프라인 리서치) · #115 6일 4초안","fr":"Compatibilité d'outils / contexte / sessions sticky · une fenêtre de 8 jours à la mi-mois (recherche hors ligne) · #115 : quatre brouillons en six jours","de":"Tool-Kompatibilität / Kontext / sticky Sessions · eine 8-tägige Lücke Mitte des Monats (Offline-Recherche) · #115: vier Entwürfe in sechs Tagen","es":"Compatibilidad de herramientas / contexto / sesiones sticky · un hueco de 8 días a mediados de mes (investigación offline) · #115: cuatro borradores en seis días"},"phase3_t":{"zh":"换轨期","en":"The Pivot","zh-TW":"換軌期","ja":"軌道変換期","ko":"궤도 변경기","fr":"Le virage","de":"Der Kurswechsel","es":"El giro"},"phase3_d":{"zh":"Cascade bridge 爆发 → devin-connect 桥 24 小时诞生 · 探针烧账户事故","en":"Cascade bridge takes off → devin-connect bridge born in 24 hours · the probe that burned accounts","zh-TW":"Cascade bridge 爆發 → devin-connect 橋 24 小時誕生 · 探針燒帳戶事故","ja":"Cascade bridge が爆誕 → devin-connect ブリッジは 24 時間で誕生 · プローブがアカウントを食い潰す事故","ko":"Cascade bridge 폭발적 증가 → devin-connect 브릿지 24시간 만에 탄생 · 프로브가 계정을 태워먹은 사고","fr":"Le pont Cascade décolle → le pont devin-connect né en 24 heures · la sonde qui a brûlé des comptes","de":"Cascade-Bridge hebt ab → devin-connect-Bridge in 24 Stunden geboren · die Sonde, die Konten verbrannt hat","es":"El puente Cascade despega → el puente devin-connect nace en 24 horas · la sonda que quemó cuentas"},"phase4_t":{"zh":"高速期","en":"The Fast Lane","zh-TW":"高速期","ja":"高速期","ko":"고속기","fr":"La voie rapide","de":"Die Überholspur","es":"El carril rápido"},"phase4_d":{"zh":"v3.0-v3.9 密集发布 · 对抗 review 文化 · OTA 跟 tag · 模型同步","en":"v3.0-v3.9 back-to-back releases · an adversarial-review culture · OTA follows the tag · model sync","zh-TW":"v3.0-v3.9 密集發佈 · 對抗 review 文化 · OTA 跟 tag · 模型同步","ja":"v3.0-v3.9 の密集リリース · 対抗 review 文化 · OTA は tag に追従 · モデル同期","ko":"v3.0-v3.9 집중 릴리스 · 대립형 review 문화 · OTA는 tag를 따름 · 모델 동기화","fr":"v3.0-v3.9 en rafale · une culture du review contradictoire · OTA suit le tag · la synchro des modèles","de":"v3.0-v3.9 in Serie · Kultur des gegnerischen Reviews · OTA folgt dem Tag · Modell-Sync","es":"v3.0-v3.9 en ráfaga · cultura de review adversarial · OTA sigue el tag · sync de modelos"},"contribs_title":{"zh":"贡献者分析","en":"Contributor analysis","zh-TW":"貢獻者分析","ja":"コントリビューター分析","ko":"기여자 분석","fr":"Analyse des contributeurs","de":"Beitragenden-Analyse","es":"Análisis de contribuyentes"},"contribs_sub":{"zh":"34 位外部贡献者 · 8 位多次贡献者(2+ PR)· 26 位单发过客 —— 悬停排行看详情,点击锁定","en":"34 external contributors · 8 repeat contributors (2+ PRs) · 26 one-and-done passers-by — hover a row for details, click to pin","zh-TW":"34 位外部貢獻者 · 8 位多次貢獻者(2+ PR)· 26 位單發過客 —— 懸停排行看詳情,點擊鎖定","ja":"外部コントリビューター 34 名 · 複数回コントリビューター 8 名(2+ PR)· 単発 26 名 —— ランキングにホバーで詳細、クリックで固定","ko":"외부 기여자 34명 · 복수 기여자 8명(2+ PR) · 단발성 기여자 26명 —— 순위표에 호버하면 상세, 클릭으로 고정","fr":"34 contributeurs externes · 8 contributeurs réguliers (2+ PRs) · 26 passants one-shot — survolez une ligne pour les détails, cliquez pour épingler","de":"34 externe Beitragende · 8 Wiederholungstäter (2+ PRs) · 26 Einmal-Vorbeigänger — Zeile hovern für Details, klicken zum Anpinnen","es":"34 contribuyentes externos · 8 contribuyentes repetidos (2+ PRs) · 26 transeúntes de una sola vez — pasa el ratón por una fila para ver detalles, haz clic para fijarla"},"c_stat_ext":{"zh":"外部贡献者","en":"External contributors","zh-TW":"外部貢獻者","ja":"外部コントリビューター","ko":"외부 기여자","fr":"Contributeurs externes","de":"Externe Beitragende","es":"Contribuyentes externos"},"c_stat_ext_s":{"zh":"共 34 人","en":"34 in total","zh-TW":"共 34 人","ja":"計 34 名","ko":"총 34명","fr":"34 au total","de":"34 insgesamt","es":"34 en total"},"c_stat_prs":{"zh":"PR 总数","en":"Total PRs","zh-TW":"PR 總數","ja":"PR 総数","ko":"PR 총수","fr":"PRs au total","de":"PRs gesamt","es":"PRs totales"},"c_stat_prs_s":{"zh":"47 个合并","en":"47 merged","zh-TW":"47 個合併","ja":"47 件 merge","ko":"47건 merge","fr":"47 fusionnées","de":"47 gemergt","es":"47 fusionados"},"c_stat_lines":{"zh":"合并行数","en":"Merged lines","zh-TW":"合併行數","ja":"merge 行数","ko":"merge된 줄 수","fr":"Lignes fusionnées","de":"Gemergte Zeilen","es":"Líneas fusionadas"},"c_stat_lines_s":{"zh":"外部 14,366 行","en":"14,366 lines from externals","zh-TW":"外部 14,366 行","ja":"外部 14,366 行","ko":"외부 14,366줄","fr":"14 366 lignes externes","de":"14.366 Zeilen von Externen","es":"14 366 líneas externas"},"c_stat_single":{"zh":"单发过客","en":"One-and-done contributors","zh-TW":"單發過客","ja":"単発コントリビューター","ko":"단발성 기여자","fr":"Contributeurs one-shot","de":"Einmal-Beitragende","es":"Contribuyentes de una sola vez"},"c_stat_single_s":{"zh":"每人只交 1 个 PR","en":"One PR each","zh-TW":"每人只交 1 個 PR","ja":"1人1 PR だけ","ko":"1인당 PR 1개뿐","fr":"Une PR chacun","de":"Je eine PR","es":"Una PR cada uno"},"share_label":{"zh":"代码归属(合并 PR 行数占比)","en":"Code ownership (share of merged-PR lines)","zh-TW":"程式碼歸屬(合併 PR 行數佔比)","ja":"コード帰属(merge した PR の行数比率)","ko":"코드 귀속(merge PR 줄 수 비중)","fr":"Propriété du code (part des lignes des PRs fusionnées)","de":"Code-Eigentum (Anteil an gemergten PR-Zeilen)","es":"Propiedad del código (proporción de líneas de PRs fusionadas)"},"lt_summary":{"zh":"位单发过客(每人 1 个 PR,修完就走)","en":"one-and-done contributors (one PR each — fix, then gone)","zh-TW":"位單發過客(每人 1 個 PR,修完就走)","ja":"名の単発コントリビューター(1人1 PR、直したら去る)","ko":"명의 단발성 기여자(1인당 PR 1개, 고치면 바로 떠남)","fr":"contributeurs one-shot (une PR chacun — le fix, puis disparus)","de":"Einmal-Beitragende (je eine PR — fix, dann weg)","es":"contribuyentes de una sola vez (una PR cada uno — el fix, y luego se fueron)"},"grade_hint":{"zh":"评级:","en":"Rating: ","zh-TW":"評級:","ja":"評価:","ko":"등급: ","fr":"Note : ","de":"Bewertung: ","es":"Calificación: "},"tree_title":{"zh":"Git 树形态","en":"Git tree shape","zh-TW":"Git 樹形態","ja":"Git ツリーの形状","ko":"Git 트리 형태","fr":"Forme de l'arbre Git","de":"Form des Git-Baums","es":"Forma del árbol Git"},"tree_sub":{"zh":"1387 提交 · 57 个合并 · 主干 1155 节点 —— 悬停节点/线段看详情,点击锁定","en":"1387 commits · 57 merges · 1155 nodes on the trunk — hover a node or segment for details, click to pin","zh-TW":"1387 提交 · 57 個合併 · 主幹 1155 節點 —— 懸停節點/線段看詳情,點擊鎖定","ja":"1387 commit · 57 merge · メインライン 1155 ノード —— ノード・線分にホバーで詳細、クリックで固定","ko":"커밋 1387개 · merge 57개 · 본류 1155 노드 —— 노드/선분에 호버하면 상세, 클릭으로 고정","fr":"1387 commits · 57 fusions · 1155 nœuds sur le tronc — survolez un nœud ou un segment pour les détails, cliquez pour épingler","de":"1387 Commits · 57 Merges · 1155 Knoten am Stamm — Knoten oder Segment hovern für Details, klicken zum Anpinnen","es":"1387 commits · 57 merges · 1155 nodos en el tronco — pasa el ratón por un nodo o segmento para ver detalles, haz clic para fijarlo"},"tree_v":{"zh":"竖","en":"Vertical","zh-TW":"直式","ja":"縦","ko":"세로","fr":"Vertical","de":"Vertikal","es":"Vertical"},"tree_h":{"zh":"横","en":"Horizontal","zh-TW":"橫式","ja":"横","ko":"가로","fr":"Horizontal","de":"Horizontal","es":"Horizontal"},"tree_ring":{"zh":"环","en":"Ring","zh-TW":"環式","ja":"環","ko":"링","fr":"Anneau","de":"Ring","es":"Anillo"},"tree_collapse":{"zh":"展开","en":"Expand","zh-TW":"展開","ja":"展開","ko":"펼치기","fr":"Déployer","de":"Aufklappen","es":"Expandir"},"tree_expand":{"zh":"收缩","en":"Collapse","zh-TW":"收縮","ja":"収縮","ko":"접기","fr":"Réduire","de":"Zusammenklappen","es":"Contraer"},"tree_seg_desc":{"zh":"主干上两锚点之间的普通提交(已折叠)。","en":"Ordinary commits between two anchors on the trunk (collapsed).","zh-TW":"主幹上兩錨點之間的普通提交(已摺疊)。","ja":"メインライン上の2つのアンカー間にある通常の commit(折りたたみ済み)。","ko":"본류에서 두 앵커 사이의 일반 커밋(접힘).","fr":"Commits ordinaires entre deux ancres du tronc (repliés).","de":"Gewöhnliche Commits zwischen zwei Ankern am Stamm (eingeklappt).","es":"Commits ordinarios entre dos anclas del tronco (plegados)."},"tree_tag":{"zh":"版本里程碑。","en":"A version milestone.","zh-TW":"版本里程碑。","ja":"バージョンのマイルストーン。","ko":"버전 마일스톤.","fr":"Un jalon de version.","de":"Ein Versionsmeilenstein.","es":"Un hito de versión."},"tree_month_anchor":{"zh":"锚点","en":"Anchor","zh-TW":"錨點","ja":"アンカー","ko":"앵커","fr":"Ancre","de":"Anker","es":"Ancla"},"tree_month_pr":{"zh":"PR","en":"PR","zh-TW":"PR","ja":"PR","ko":"PR","fr":"PR","de":"PR","es":"PR"},"month_desc_04":{"zh":"成型冲刺:issue 洪峰 82 个,98% 解决率","en":"Shaping sprint: 82-issue flood, 98% resolved","zh-TW":"成型衝刺:issue 洪峰 82 個,98% 解決率","ja":"成形スプリント:issue 殺到 82 個、98% 解決率","ko":"형성 스프린트:issue 폭풍 82개,98% 해결률","fr":"Sprint fondateur : raz-de-marée de 82 issues, 98 % résolues","de":"Formations-Sprint: 82er-Issue-Flut, 98 % gelöst","es":"Sprint fundacional: inundación de 82 issues, 98 % resueltas"},"month_desc_05":{"zh":"停滞深水:#115 六天四稿,NLU 七连 hotfix","en":"The slow season: #115 four drafts in six days, NLU's seven straight hotfixes","zh-TW":"停滯深水:#115 六天四稿,NLU 七連 hotfix","ja":"停滞の深み:#115 は6日4稿、NLU 7連 hotfix","ko":"정체의 늪:#115 6일 4초안,NLU hotfix 7연발","fr":"La morte-saison : #115 quatre brouillons en six jours, les sept hotfixes NLU d'affilée","de":"Die Flaute: #115 vier Entwürfe in sechs Tagen, sieben NLU-hotfixes in Folge","es":"La temporada muerta: #115 cuatro borradores en seis días, siete hotfixes seguidos de NLU"},"month_desc_06":{"zh":"换轨:devin-connect 桥 24 小时诞生","en":"The pivot: devin-connect bridge born in 24 hours","zh-TW":"換軌:devin-connect 橋 24 小時誕生","ja":"軌道変換:devin-connect ブリッジは 24 時間で誕生","ko":"궤도 변경:devin-connect 브릿지 24시간 탄생","fr":"Le virage : le pont devin-connect né en 24 heures","de":"Der Kurswechsel: devin-connect-Bridge in 24 Stunden geboren","es":"El giro: el puente devin-connect nace en 24 horas"},"month_desc_07":{"zh":"高速:v3.0-v3.9 密集发布,对抗 review 风暴","en":"Fast lane: v3.0-v3.9 back-to-back releases, the adversarial-review storm","zh-TW":"高速:v3.0-v3.9 密集發佈,對抗 review 風暴","ja":"高速:v3.0-v3.9 の密集リリース、対抗 review の嵐","ko":"고속:v3.0-v3.9 집중 릴리스,대립형 review 폭풍","fr":"Voie rapide : v3.0-v3.9 en rafale, la tempête du review contradictoire","de":"Überholspur: v3.0-v3.9 in Serie, der Sturm des adversarischen Reviews","es":"Carril rápido: v3.0-v3.9 a ráfagas, la tormenta del review adversarial"},"month_desc_08":{"zh":"高速:会话保真三线,OTA 跟 tag,发到 v3.9.29","en":"Fast lane: session fidelity on three fronts, OTA follows the tag, through v3.9.29","zh-TW":"高速:會話保真三線,OTA 跟 tag,發到 v3.9.29","ja":"高速:セッション忠実度の3路線、OTA は tag に追従、v3.9.29 まで","ko":"고속:세션 보존 3개 라인,OTA는 tag를 따름,v3.9.29까지","fr":"Voie rapide : fidélité des sessions sur trois fronts, OTA suit le tag, jusqu'à v3.9.29","de":"Überholspur: Session-Treue auf drei Fronten, OTA folgt dem Tag, bis v3.9.29","es":"Carril rápido: fidelidad de sesión en tres frentes, OTA sigue el tag, hasta v3.9.29"},"tl_title":{"zh":"自伤与返工(28 起,按时间)","en":"Self-Inflicted Wounds (28, chronological)","zh-TW":"自傷與返工(28 起,按時間)","ja":"自爆と手戻りの記録(28 件、時系列)","ko":"자업자득과 되돌림(28건, 시간순)","fr":"Blessures auto-infligées (28, par ordre chronologique)","de":"Eigentore (28, chronologisch)","es":"Heridas autoinfligidas (28, cronológicas)"},"tl_sub":{"zh":"红 = 事故级(烧钱/安全/生产挂)· 橙 = 合并后自毁/返工 · 悬停节点看详情(AI 总结 + 账本/Git 链接),点击锁定浮窗","en":"Red = incident-grade (money burned / security / prod down) · Orange = post-merge self-owns & rework · hover a node for details (AI summary + ledger/Git links), click to pin the popover","zh-TW":"紅 = 事故級(燒錢/安全/生產掛)· 橙 = 合併後自毀/返工 · 懸停節點看詳情(AI 總結 + 帳本/Git 連結),點擊鎖定浮窗","ja":"赤 = 事故級(金が飛ぶ/セキュリティ/本番障害)· 橙 = merge 後の自爆・手戻り · ノードにホバーで詳細(AI 要約 + 帳簿/Git リンク)、クリックでフロートを固定","ko":"빨강 = 사고급(돈 태움/보안/프로덕션 마비) · 주황 = merge 후 자폭/되돌림(셀프 뺨 때리기) · 노드에 호버하면 상세(AI 요약 + 장부/Git 링크), 클릭으로 팝업 고정","fr":"Rouge = niveau incident (argent brûlé / sécurité / prod en panne) · Orange = auto-sabotage et reprise après merge · survolez un nœud pour les détails (résumé IA + liens registre/Git), cliquez pour épingler la fenêtre","de":"Rot = Incident-Niveau (Geld verbrannt / Sicherheit / Produktion down) · Orange = Selbsttore & Nacharbeit nach dem Merge · Knoten hovern für Details (KI-Zusammenfassung + Ledger/Git-Links), klicken zum Anpinnen des Popovers","es":"Rojo = nivel incidente (dinero quemado / seguridad / prod caída) · Naranja = autogoles y retrabajo tras el merge · pasa el ratón por un nodo para ver detalles (resumen de IA + enlaces al ledger/Git), haz clic para fijar la ventana"},"tl_lv_h":{"zh":"事故级(烧钱/安全/生产挂)","en":"Incident-grade (money / security / production)","zh-TW":"事故級(燒錢/安全/生產掛)","ja":"事故級(金が飛ぶ/セキュリティ/本番障害)","ko":"사고급(돈 태움/보안/프로덕션 마비)","fr":"Niveau incident (argent / sécurité / production)","de":"Incident-Niveau (Geld / Sicherheit / Produktion)","es":"Nivel incidente (dinero / seguridad / producción)"},"tl_lv_m":{"zh":"合并后自毁/返工自打脸","en":"Post-merge self-owns & rework","zh-TW":"合併後自毀/返工自打臉","ja":"merge 後の自爆・手戻り(自ら墓穴を掘る)","ko":"merge 후 자폭/되돌림, 셀프 뺨 때리기","fr":"Auto-sabotage et reprise après merge","de":"Selbsttore & Nacharbeit nach dem Merge","es":"Autogoles y retrabajo tras el merge"},"foot_1":{"zh":"数据源:git log 全量 1387 条 + 22 个并行采集 agent · 逐条账见 docs/HISTORY-LEDGER-2026-*.md · 总账","en":"Sources: the full 1387-entry git log + 22 parallel harvest agents · itemized entries in docs/HISTORY-LEDGER-2026-*.md · the master ledger","zh-TW":"資料源:git log 全量 1387 條 + 22 個並行採集 agent · 逐條帳目見 docs/HISTORY-LEDGER-2026-*.md · 總帳","ja":"データ源:git log 全量 1387 件 + 並列で走らせた取得 agent 22 個 · 一件ごとの帳簿は docs/HISTORY-LEDGER-2026-*.md · 総帳簿","ko":"데이터 소스: git log 전체 1387개 + 병렬 수집 agent 22개 · 건별 장부는 docs/HISTORY-LEDGER-2026-*.md · 총 장부","fr":"Sources : le git log complet de 1387 entrées + 22 agents de collecte en parallèle · entrées détaillées dans docs/HISTORY-LEDGER-2026-*.md · le registre maître","de":"Quellen: das vollständige git log mit 1387 Einträgen + 22 parallele Harvest-Agents · Einzelposten in docs/HISTORY-LEDGER-2026-*.md · das Hauptledger","es":"Fuentes: el git log completo de 1387 entradas + 22 agents de recolección en paralelo · entradas detalladas en docs/HISTORY-LEDGER-2026-*.md · el ledger maestro"},"foot_2":{"zh":"零依赖纯原生 · 设计 DNA 取自仓库自带 Dashboard · 每个 hash 都可 git show 验证","en":"Zero dependencies, all native · design DNA lifted from the repo's own Dashboard · every hash verifiable with git show","zh-TW":"零依賴純原生 · 設計 DNA 取自倉庫自帶 Dashboard · 每個 hash 都可 git show 驗證","ja":"零依存・純ネイティブ · デザイン DNA はリポジトリ同梱の Dashboard 由来 · どの hash も git show で検証可能","ko":"제로 의존성 순수 네이티브 · 디자인 DNA는 레포 내장 Dashboard에서 유래 · 모든 hash는 git show로 검증 가능","fr":"Zéro dépendance, tout natif · l'ADN du design emprunté au Dashboard du dépôt · chaque hash vérifiable avec git show","de":"Null Abhängigkeiten, alles nativ · Design-DNA aus dem eigenen Dashboard des Repos · jeder Hash mit git show verifizierbar","es":"Cero dependencias, todo nativo · el ADN del diseño tomado del Dashboard del propio repo · cada hash verificable con git show"},"sh_0_t":{"zh":"PR #1 撞车+致谢反转","en":"PR #1: collision + credit reversal","zh-TW":"PR #1 撞車+致謝反轉","ja":"PR #1 衝突 + 謝辞の逆転","ko":"PR #1 충돌+크레딧 번복","fr":"PR #1 : collision + retournement des remerciements","de":"PR #1: Kollision + Dankes-Umkehr","es":"PR #1: colisión + reversión de créditos"},"sh_0_desc":{"zh":"同一 commit 走两遍 PR 流程(merge 第二父就是作者自己的 commit);随后 credits 移除又 revert,对外部贡献的致谢决策反复横跳。","en":"The same commit ran through the PR flow twice (the merge's second parent was the author's own commit); credits were then removed and reverted — the thank-you to an external contributor flip-flopped.","zh-TW":"同一個 commit 走兩遍 PR 流程(merge 第二父就是作者自己的 commit);隨後 credits 移除又 revert,對外部貢獻的致謝決策反覆橫跳。","ja":"同じ commit が PR フローを2回走る(merge の第二親が作者自身の commit);その後 credits を削除→revert し、外部貢献への謝辞の判断が二転三転。","ko":"같은 commit이 PR 프로세스를 두 번 통과(merge의 두 번째 부모가 바로 저자 자신의 commit); 이후 credits 제거와 revert가 이어지며 외부 기여 감사 결정이 왔다 갔다 반복.","fr":"Le même commit a passé deux fois par le flux PR (le second parent du merge était le propre commit de l'auteur) ; les crédits ont ensuite été retirés puis revertés — la gratitude envers le contributeur externe a fait du yo-yo.","de":"Derselbe Commit durchlief den PR-Flow zweimal (der zweite Elternteil des Merges war der eigene Commit des Autors); danach wurden die Credits entfernt und wieder revertet — der Dank an den externen Beitragenden kippte hin und her.","es":"El mismo commit pasó dos veces por el flujo de PR (el segundo padre del merge era el propio commit del autor); luego los créditos se quitaron y se revirtieron — el agradecimiento al contribuyente externo fue un yoyó."},"sh_1_t":{"zh":"kimi/qwen 目录三连撤","en":"kimi/qwen catalog: triple retraction","zh-TW":"kimi/qwen 目錄三連撤","ja":"kimi/qwen ディレクトリ3連続撤収","ko":"kimi/qwen 카탈로그 3연속 철회","fr":"kimi/qwen : triple rétractation au catalogue","de":"kimi/qwen: dreifache Rücknahme im Katalog","es":"kimi/qwen: triple retirada en el catálogo"},"sh_1_desc":{"zh":"上游目录没核实就上架。kimi-k2 六分钟内三连改(enum→string→回退);qwen-3-coder 82 秒内三次提交全撤回。","en":"Catalog entries shipped without verifying upstream. kimi-k2 changed three times in six minutes (enum→string→reverted); all three qwen-3-coder commits were retracted within 82 seconds.","zh-TW":"上游目錄沒核實就上架。kimi-k2 六分鐘內三連改(enum→string→回退);qwen-3-coder 82 秒內三次提交全撤回。","ja":"上流ディレクトリを確認せずに公開。kimi-k2 は6分で3連続変更(enum→string→戻し);qwen-3-coder は82秒で3回の commit をすべて撤回。","ko":"상류 카탈로그를 검증 없이 올렸다. kimi-k2는 6분 만에 3연속 변경(enum→string→롤백); qwen-3-coder는 82초 안에 커밋 3개 전부 철회.","fr":"Des entrées au catalogue publiées sans vérifier l'upstream. kimi-k2 a changé trois fois en six minutes (enum→string→revert) ; les trois commits qwen-3-coder ont été retirés en 82 secondes.","de":"Katalogeinträge ausgeliefert, ohne den Upstream zu prüfen. kimi-k2 wurde in sechs Minuten dreimal geändert (enum→string→revert); alle drei qwen-3-coder-Commits wurden innerhalb von 82 Sekunden zurückgezogen.","es":"Entradas de catálogo publicadas sin verificar el upstream. kimi-k2 cambió tres veces en seis minutos (enum→string→revert); los tres commits de qwen-3-coder se retiraron en 82 segundos."},"sh_2_t":{"zh":"auth bypass 自己引入","en":"Self-inflicted auth bypass","zh-TW":"auth bypass 自己引入","ja":"auth bypass を自ら導入","ko":"자기가 유발한 auth bypass","fr":"Bypass d'auth auto-infligé","de":"Selbst eingebauter Auth-Bypass","es":"Bypass de auth autoinfligido"},"sh_2_desc":{"zh":"为前端免密访问加的「空 password header 放行」成为严重 Dashboard auth bypass(同日恢复原逻辑,fail-closed 深化)。","en":"The 'empty password header passes' exemption, added for passwordless frontend access, became a severe Dashboard auth bypass (original logic restored the same day; fail-closed deepened).","zh-TW":"為前端免密訪問加的「空 password header 放行」成為嚴重 Dashboard auth bypass(同日恢復原邏輯,fail-closed 深化)。","ja":"フロントエンドのパスワードレスアクセスのために足した「空 password header を許可」が、Dashboard の重大 auth bypass に(当日中に元のロジックへ復旧、fail-closed を徹底)。","ko":"프론트엔드 무비밀번호 접근을 위해 추가한 「빈 password header 통과 허용」이 심각한 Dashboard auth bypass가 되었다(당일 원래 로직 복구, fail-closed 강화).","fr":"L'exemption « le header password vide passe », ajoutée pour un accès front sans mot de passe, est devenue un sérieux bypass d'auth du Dashboard (logique d'origine restaurée le jour même ; fail-closed renforcé).","de":"Die Ausnahme „leerer Password-Header passiert“, eingebaut für passwortlosen Frontend-Zugriff, wurde zu einem schweren Dashboard-Auth-Bypass (die ursprüngliche Logik wurde am selben Tag wiederhergestellt; fail-closed vertieft).","es":"La excepción 'el header de password vacío pasa', añadida para el acceso del frontend sin contraseña, se convirtió en un serio bypass de auth del Dashboard (la lógica original se restauró el mismo día; fail-closed reforzado)."},"sh_3_t":{"zh":"三连 revert 踩中'不能改'","en":"Three reverts stomp the do-not-touch list","zh-TW":"三連 revert 踩中'不能改'","ja":"三連 revert で「変更禁止」を踏む","ko":"revert 3연속, '건드리면 안 됨' 직격","fr":"Trois reverts piétinent la liste « ne pas toucher »","de":"Drei Reverts trampeln auf der Liste „Nicht anfassen“","es":"Tres reverts pisotean la lista 'no tocar'"},"sh_3_desc":{"zh":"gRPC 压缩头 / planner READ_ONLY / 版本号——三个「聪明改动」全踩中 dfb979a 当天自己写的「不能改」清单,全部导致 LS proto wire-type 解析错误。","en":"gRPC compression header / planner READ_ONLY / version number — three 'clever changes' that all landed on the 'must not change' list the author wrote the same day as dfb979a, each breaking LS proto wire-type parsing.","zh-TW":"gRPC 壓縮頭 / planner READ_ONLY / 版本號——三個「聰明改動」全踩中 dfb979a 當天自己寫的「不能改」清單,全部導致 LS proto wire-type 解析錯誤。","ja":"gRPC 圧縮ヘッダ / planner READ_ONLY / バージョン番号——3つの「賢い変更」がすべて dfb979a 当日に自分で書いた「変更禁止」リストに衝突し、いずれも LS proto の wire-type 解析エラーを引き起こした。","ko":"gRPC 압축 헤더 / planner READ_ONLY / 버전 번호 — 세 가지 '똑똑한 개선'이 전부 dfb979a 당일 자신이 써둔 '건드리면 안 되는 것' 목록을 직격, 전부 LS proto wire-type 파싱 오류 유발.","fr":"Header de compression gRPC / planner READ_ONLY / numéro de version — trois « changements intelligents » qui ont tous atterri sur la liste « à ne pas modifier » écrite par l'auteur le jour même de dfb979a, chacun cassant le parsing wire-type du proto LS.","de":"gRPC-Kompressionsheader / planner READ_ONLY / Versionsnummer — drei „clevere Änderungen“, die alle auf der Liste „darf nicht geändert werden“ landeten, die der Autor am selben Tag wie dfb979a geschrieben hatte, und jede brach das LS-proto-wire-type-Parsing.","es":"Header de compresión gRPC / planner READ_ONLY / número de versión — tres 'cambios listos' que aterrizaron todos en la lista 'no cambiar' que el autor escribió el mismo día que dfb979a, y cada uno rompió el parsing wire-type del proto LS."},"sh_4_t":{"zh":"GRPC_PROTOCOL 生产全挂","en":"GRPC_PROTOCOL: full prod outage","zh-TW":"GRPC_PROTOCOL 生產全掛","ja":"GRPC_PROTOCOL で本番全滅","ko":"GRPC_PROTOCOL 프로덕션 전면 마비","fr":"GRPC_PROTOCOL : production entièrement en panne","de":"GRPC_PROTOCOL: komplette Produktionspanne","es":"GRPC_PROTOCOL: caída total de producción"},"sh_4_desc":{"zh":"v2.0.19 把默认 legacy→Connect,v2.0.21 生产全挂(cascade_id 空串)回退。作者写了 postmortem:单测只验 const 取值,没起 fake LS 端到端验 Connect framing。","en":"v2.0.19 flipped the default from legacy→Connect; v2.0.21 took production fully down (empty cascade_id) and was reverted. The author wrote a postmortem: unit tests only checked constant values; no fake LS was spun up to verify Connect framing end-to-end.","zh-TW":"v2.0.19 把預設 legacy→Connect,v2.0.21 生產全掛(cascade_id 空字串)回退。作者寫了 postmortem:單測只驗 const 取值,沒起 fake LS 端到端驗 Connect framing。","ja":"v2.0.19 でデフォルトを legacy→Connect に変更、v2.0.21 で本番全滅(cascade_id が空文字)→ ロールバック。作者は postmortem を執筆:単体テストは const の値だけ検証し、fake LS を立てて Connect framing を端から端まで検証していなかった。","ko":"v2.0.19에서 기본값을 legacy→Connect로 변경, v2.0.21에서 프로덕션 전면 마비(cascade_id 빈 문자열) 후 롤백. 저자가 postmortem 작성: 단위 테스트는 const 값만 검증했을 뿐 fake LS를 띄워 Connect framing을 엔드투엔드로 검증하지 않았다.","fr":"v2.0.19 a basculé la valeur par défaut de legacy→Connect ; v2.0.21 a mis la production entièrement hors service (cascade_id vide) et a été reverté. L'auteur a écrit un postmortem : les tests unitaires ne vérifiaient que des valeurs de constantes, et aucun faux LS n'a été lancé pour valider le framing Connect de bout en bout.","de":"v2.0.19 stellte den Default von legacy→Connect um; v2.0.21 legte die Produktion komplett lahm (leere cascade_id) und wurde revertet. Der Autor schrieb ein Postmortem: Unit-Tests prüften nur Konstantenwerte; kein Fake-LS wurde gestartet, um das Connect-Framing end-to-end zu verifizieren.","es":"v2.0.19 cambió el default de legacy→Connect; v2.0.21 tumbó la producción entera (cascade_id vacío) y se revirtió. El autor escribió un postmortem: los tests unitarios solo comprobaban valores de constantes; no se levantó ningún LS falso para verificar el framing Connect de extremo a extremo."},"sh_5_t":{"zh":"#115 根因误判六天四稿","en":"#115: misread root cause, four drafts in six days","zh-TW":"#115 根因誤判六天四稿","ja":"#115 誤診からの6日4稿","ko":"#115 원인 오판, 6일 4초안","fr":"#115 : cause racine mal identifiée, quatre brouillons en six jours","de":"#115: Ursache fehldiagnostiziert, vier Entwürfe in sechs Tagen","es":"#115: causa raíz mal diagnosticada, cuatro borradores en seis días"},"sh_5_desc":{"zh":"自称 root cause 是 gpt_native 方言 → 当天修的是自己引入的 body is not defined → 三稿「真修」→ 5-03 承认翻方向 → 换 NLU 层 → 6-05 以带遥测+测试+canary 的形态终章。","en":"Declared the root cause was the gpt_native dialect → what got fixed that day was the self-introduced 'body is not defined' → three drafts of 'real fixes' → admitted on 5-03 the direction was wrong → switched the NLU layer → wrapped up on 6-05 with telemetry + tests + canary in place.","zh-TW":"自稱 root cause 是 gpt_native 方言 → 當天修的是自己引入的 body is not defined → 三稿「真修」→ 5-03 承認翻方向 → 換 NLU 層 → 6-05 以帶遙測+測試+canary 的型態終章。","ja":"root cause は gpt_native 方言と自称 → その日に直したのは自分が持ち込んだ body is not defined → 3稿の「本当の修正」→ 5-03 に方向違いを認める → NLU 層へ切替 → 6-05 にテレメトリー + テスト + canary 付きの形で完結。","ko":"root cause가 gpt_native 방언이라고 자평 → 당일 고친 건 자기가 유발한 body is not defined → 3초안 '진짜 수정' → 5-03에 방향 오판 인정 → NLU 레이어로 교체 → 6-05에 텔레메트리+테스트+canary를 갖춘 형태로 종결.","fr":"A déclaré que la cause racine était le dialecte gpt_native → ce qui a été corrigé ce jour-là, c'est le « body is not defined » auto-introduit → trois brouillons de « vraies corrections » → a admis le 5-03 que la direction était fausse → a changé la couche NLU → a bouclé le 6-05 avec télémétrie + tests + canary en place.","de":"Behauptete, die Ursache sei der gpt_native-Dialekt → was an dem Tag gefixt wurde, war das selbst eingeführte „body is not defined“ → drei Entwürfe „echter Fixes“ → am 5-03 zugegeben, die Richtung sei falsch → die NLU-Ebene gewechselt → am 6-05 mit Telemetrie + Tests + canary abgeschlossen.","es":"Declaró que la causa raíz era el dialecto gpt_native → lo que se arregló ese día fue el 'body is not defined' auto-introducido → tres borradores de 'arreglos reales' → admitió el 5-03 que la dirección era errónea → cambió la capa NLU → cerró el 6-05 con telemetría + tests + canary en su lugar."},"sh_6_t":{"zh":"NLU 七连 hotfix","en":"NLU: seven hotfixes in a row","zh-TW":"NLU 七連 hotfix","ja":"NLU 7連 hotfix","ko":"NLU hotfix 7연발","fr":"NLU : sept hotfixes d'affilée","de":"NLU: sieben hotfixes in Folge","es":"NLU: siete hotfixes seguidos"},"sh_6_desc":{"zh":"NLU 协议转换层引入后同一层一周 8 个补丁(allThinking 通道漏、Claude 工具卡死回归、误抠 placeholder、入口扩宽…),5-07 仍在打补丁。","en":"After the NLU protocol translation layer landed, the same layer took 8 patches in a week (allThinking channel leak, Claude tool-hang regression, misplaced placeholder stripping, widened entry points…), and patches were still flying on 5-07.","zh-TW":"NLU 協定轉換層引入後同一層一週 8 個補丁(allThinking 通道漏、Claude 工具卡死迴歸、誤摳 placeholder、入口擴寬…),5-07 仍在打補丁。","ja":"NLU プロトコル変換層の導入後、同じ層に1週間で 8 個のパッチ(allThinking の経路漏れ、Claude ツール固まりの回帰、placeholder 誤抽出、入口の拡大…)、5-07 時点でもまだパッチを当てていた。","ko":"NLU 프로토콜 변환 레이어 도입 후 같은 레이어에 일주일간 패치 8개(allThinking 채널 누락, Claude 도구 멈춤 회귀, placeholder 오제거, 진입점 확장...), 5-07에도 여전히 패치 중.","fr":"Après l'arrivée de la couche de traduction de protocole NLU, cette même couche a pris 8 correctifs en une semaine (fuite du canal allThinking, régression du blocage des outils Claude, stripping intempestif de placeholder, points d'entrée élargis…), et les correctifs pleuvaient encore le 5-07.","de":"Nachdem die NLU-Protokollübersetzungsebene kam, brauchte dieselbe Ebene in einer Woche 8 Patches (allThinking-Kanal-Leck, Claude-Tool-Hänger-Regression, fälschliches Placeholder-Entfernen, verbreiterte Einstiegspunkte…), und am 5-07 flogen immer noch Patches.","es":"Tras la llegada de la capa de traducción de protocolo NLU, esa misma capa recibió 8 parches en una semana (fuga del canal allThinking, regresión del bloqueo de herramientas de Claude, stripping erróneo de placeholders, puntos de entrada ensanchados…), y el 5-07 aún llovían parches."},"sh_7_t":{"zh":"#129 wnfilm 回归三连","en":"#129: the wnfilm triple regression","zh-TW":"#129 wnfilm 迴歸三連","ja":"#129 wnfilm 3連続の回帰","ko":"#129 wnfilm 회귀 3연속","fr":"#129 : la triple régression wnfilm","de":"#129: die wnfilm-Dreifach-Regression","es":"#129: la triple regresión de wnfilm"},"sh_7_desc":{"zh":"2.0.85 上 fallback 直接打挂 wnfilm 用户 → 86 紧急默认 OFF → 87 真修(fingerprint 锁 modelKey 双挂)后才重开。","en":"The fallback in 2.0.85 broke wnfilm users outright → 86 flipped the default to OFF in a hurry → 87 applied the real fix (fingerprint locks modelKey on both ends) before re-enabling.","zh-TW":"2.0.85 上 fallback 直接打掛 wnfilm 用戶 → 86 緊急預設 OFF → 87 真修(fingerprint 鎖 modelKey 雙掛)後才重開。","ja":"2.0.85 の fallback が wnfilm ユーザーを直撃 → 86 で緊急にデフォルト OFF → 87 で本修正(fingerprint で modelKey を二重に縛る)してからようやく再開。","ko":"2.0.85의 fallback이 wnfilm 사용자를 직격으로 마비 → 86에서 긴급 기본 OFF → 87에서 진짜 수정(fingerprint로 modelKey 이중 마운트를 잠금) 후에야 재개.","fr":"Le fallback de 2.0.85 a cassé net les utilisateurs wnfilm → 86 a mis la valeur par défaut sur OFF en urgence → 87 a appliqué le vrai fix (le fingerprint verrouille modelKey des deux côtés) avant de réactiver.","de":"Der Fallback in 2.0.85 brach wnfilm-Nutzer komplett → 86 stellte den Default schnell auf OFF → 87 brachte den echten Fix (Fingerprint verriegelt modelKey auf beiden Seiten), bevor wieder aktiviert wurde.","es":"El fallback de 2.0.85 rompió de lleno a los usuarios de wnfilm → 86 puso el default en OFF con urgencia → 87 aplicó el fix real (el fingerprint bloquea modelKey en ambos lados) antes de reactivar."},"sh_8_t":{"zh":"sticky 自查风暴","en":"sticky: the self-audit storm","zh-TW":"sticky 自查風暴","ja":"sticky 自爆査定の嵐","ko":"sticky 자가 점검 폭풍","fr":"sticky : la tempête d'auto-audit","de":"sticky: der Selbst-Audit-Sturm","es":"sticky: la tormenta de auto-auditoría"},"sh_8_desc":{"zh":"5-12 引入 #162,5-25 一天 11 条 debug+修复(双重打标、body.user 优先级、流式漏传 callerKey、dashboard 401)——自己实现的功能自己发现 bug 自己修。","en":"Introduced #162 on 5-12; on 5-25, 11 debug-and-fix commits in a single day (double tagging, body.user precedence, callerKey dropped on streaming, dashboard 401) — we built it, we broke it, we fixed it.","zh-TW":"5-12 引入 #162,5-25 一天 11 條 debug+修復(雙重打標、body.user 優先級、流式漏傳 callerKey、dashboard 401)——自己實現的功能自己發現 bug 自己修。","ja":"5-12 に #162 を導入、5-25 に1日 11 件の debug + 修正(二重タグ付け、body.user の優先度、ストリーミングでの callerKey 渡し漏れ、dashboard 401)——自分で実装した機能の bug を自分で見つけて自分で直す。","ko":"5-12에 #162 도입, 5-25에 하루 debug+수정 11건(이중 마킹, body.user 우선순위, 스트리밍 callerKey 누락, dashboard 401) — 직접 만든 기능의 버그를 직접 발견해 직접 고쳤다.","fr":"#162 introduit le 5-12 ; le 5-25, 11 commits debug-et-fix en une seule journée (double étiquetage, priorité de body.user, callerKey perdu en streaming, 401 du dashboard) — on l'a construit, on l'a cassé, on l'a réparé.","de":"#162 am 5-12 eingeführt; am 5-25 dann 11 Debug-und-Fix-Commits an einem einzigen Tag (Doppel-Tagging, body.user-Priorität, callerKey im Streaming verloren, Dashboard-401) — wir haben's gebaut, wir haben's kaputt gemacht, wir haben's gefixt.","es":"Introdujo #162 el 5-12; el 5-25, 11 commits de debug-y-fix en un solo día (doble etiquetado, prioridad de body.user, callerKey perdido en streaming, 401 del dashboard) — lo construimos, lo rompimos, lo arreglamos."},"sh_9_t":{"zh":"假上游冷却","en":"The fake upstream cooldown","zh-TW":"假上游冷卻","ja":"偽の上流クーリング","ko":"가짜 upstream 쿨다운","fr":"Le faux cooldown upstream","de":"Das Fake-Upstream-Cooldown","es":"El falso cooldown del upstream"},"sh_9_desc":{"zh":"native bridge 尾巴:上片引入的「假上游冷却」换成真实冷却。","en":"A native bridge tail-end: the 'fake upstream cooldown' shipped in the previous episode, replaced with a real one.","zh-TW":"native bridge 尾巴:上篇引入的「假上游冷卻」換成真實冷卻。","ja":"native bridge の尻尾:前編で導入した「偽の上流クーリング」を本物のクーリングに差し替え。","ko":"native bridge 마무리 작업: 앞선 커밋에서 도입한 「가짜 upstream 쿨다운」을 실제 쿨다운으로 교체.","fr":"Queue du pont natif : le « faux cooldown upstream » livré dans l'épisode précédent, remplacé par un vrai.","de":"Zum Schluss der nativen Bridge: das in der vorherigen Folge ausgelieferte „Fake-Upstream-Cooldown“ wurde durch ein echtes ersetzt.","es":"Cola del puente nativo: el 'falso cooldown del upstream' publicado en el episodio anterior, reemplazado por uno real."},"sh_10_t":{"zh":"探针烧账户(最重事故)","en":"The probe that burned accounts (the big one)","zh-TW":"探針燒帳戶(最重事故)","ja":"プローブがアカウントを食い潰す(最悪の事故)","ko":"프로브 계정 태움(최중대 사고)","fr":"La sonde qui a brûlé des comptes (la grosse)","de":"Die Sonde, die Konten verbrannte (die ganz Große)","es":"La sonda que quemó cuentas (la grande)"},"sh_10_desc":{"zh":"强制探针 Step 2/3 对每个模型发真实请求,约 12 个模型扫完把活着的免费账户 free→expired——线上事故。修法:计费 canary 改 opt-in,普通探测变零额度。","en":"The forced probe sent real requests to every model at Steps 2/3; after sweeping ~12 models, live free accounts flipped free→expired — a production incident. The fix: billing canary became opt-in, and normal probes cost zero quota.","zh-TW":"強制探針 Step 2/3 對每個模型發真實請求,約 12 個模型掃完把活著的免費帳戶 free→expired——線上事故。修法:計費 canary 改 opt-in,普通探測變零額度。","ja":"強制プローブの Step 2/3 が全モデルに本物のリクエストを送り、約 12 モデル分スキャンした時点で生きていた無料アカウントが free→expired——本番事故。対策:課金 canary を opt-in に変更し、通常プローブはゼロ枠に。","ko":"강제 프로브 Step 2/3가 모든 모델에 실제 요청을 보내, 모델 약 12개를 훑은 뒤 살아있던 무료 계정들을 free→expired로 만들어버림 — 실제 서비스 사고. 해결: 결제 canary를 opt-in으로 전환, 일반 탐지는 제로 할당량으로.","fr":"La sonde forcée envoyait de vraies requêtes à chaque modèle aux étapes 2/3 ; après avoir balayé ~12 modèles, des comptes gratuits vivants sont passés de free→expired — un incident de production. Le fix : le canary de facturation est devenu opt-in, et les sondes normales ne consomment plus aucun quota.","de":"Die erzwungene Sonde schickte in Schritt 2/3 echte Requests an jedes Modell; nach ~12 Modellen kippten lebende Gratiskonten von free→expired — ein Produktionsvorfall. Der Fix: Billing-canary wurde opt-in, und normale Sonden kosten null Quota.","es":"La sonda forzada enviaba requests reales a cada modelo en los pasos 2/3; tras barrer ~12 modelos, cuentas gratuitas vivas pasaron de free→expired — un incidente de producción. El fix: el canary de facturación pasó a ser opt-in, y las sondas normales cuestan cero quota."},"sh_11_t":{"zh":"cutover 双 P0","en":"Cutover: two P0s in one day","zh-TW":"cutover 雙 P0","ja":"cutover で P0 が2連発","ko":"cutover P0 2건","fr":"Cutover : deux P0 le même jour","de":"Cutover: zwei P0 an einem Tag","es":"Cutover: dos P0 en un día"},"sh_11_desc":{"zh":"桥上线当天:P0-1 re-login 风暴无节流(force:true 绕过 60s 冷却);P0-2 池耗尽静默降级到无记账的 env token,零 RPM 记账把单账户锤向封禁。","en":"On bridge launch day: P0-1, an unthrottled re-login storm (force:true bypassed the 60s cooldown); P0-2, pool exhaustion silently fell back to an un-metered env token, and zero-RPM metering hammered a single account toward a ban.","zh-TW":"橋上線當天:P0-1 re-login 風暴無節流(force:true 繞過 60s 冷卻);P0-2 池耗盡靜默降級到無記帳的 env token,零 RPM 記帳把單一帳戶錘向封禁。","ja":"ブリッジ公開当日:P0-1 re-login ストームにスロットリングなし(force:true が 60s クーリングを回避);P0-2 プール枯渇で記帳なしの env token へ静かに降格し、RPM ゼロのまま記帳されて単一アカウントが封禁まで叩かれる。","ko":"브릿지 출시 당일: P0-1 re-login 폭풍에 스로틀링 없음(force:true가 60초 쿨다운 우회); P0-2 풀 소진 시 accounting 없는 env token으로 무음 다운그레이드, 제로 RPM 계상으로 단일 계정이 정지로 내몰림.","fr":"Le jour du lancement du pont : P0-1, une tempête de re-login sans throttling (force:true contournait le cooldown de 60 s) ; P0-2, l'épuisement du pool est retombé silencieusement sur un env token non mesuré, et le comptage à zéro RPM a martelé un compte unique jusqu'à son ban.","de":"Am Launch-Tag der Bridge: P0-1, ein ungedrosselter Re-Login-Sturm (force:true umging das 60-s-Cooldown); P0-2, Pool-Erschöpfung fiel still auf ein nicht abgerechnetes env token zurück, und Null-RPM-Abrechnung hämmerte ein einzelnes Konto Richtung Ban.","es":"El día del lanzamiento del puente: P0-1, una tormenta de re-login sin throttling (force:true eludía el cooldown de 60 s); P0-2, el agotamiento del pool cayó silenciosamente en un env token sin medir, y la contabilidad a cero RPM martilleó una sola cuenta hacia el ban."},"sh_12_t":{"zh":"alias 漂移:要 GPT 拿 SWE","en":"Alias drift: asked for GPT, got SWE","zh-TW":"alias 漂移:要 GPT 拿 SWE","ja":"alias ドリフト:GPT 頼みで SWE が来る","ko":"alias 드리프트: GPT를 원했는데 SWE","fr":"Dérive d'alias : on demande du GPT, on reçoit du SWE","de":"Alias-Drift: GPT bestellt, SWE bekommen","es":"Deriva de alias: pediste GPT, recibiste SWE"},"sh_12_desc":{"zh":"catalog alias 未全量解析——客户端按广告发 gpt-5.5/gemini-3.0-flash,selector 未命中静默降级到免费 swe-1-6-slow。要 GPT 拿到 SWE。快照 fixture 同日入库防漂移。","en":"Catalog aliases weren't fully resolved — clients sent gpt-5.5/gemini-3.0-flash as advertised, the selector missed and silently downgraded to the free swe-1-6-slow. Asked for GPT, got SWE. Snapshot fixtures were committed the same day to prevent drift.","zh-TW":"catalog alias 未全量解析——用戶端按廣告發 gpt-5.5/gemini-3.0-flash,selector 未命中靜默降級到免費 swe-1-6-slow。要 GPT 拿到 SWE。快照 fixture 同日入庫防漂移。","ja":"catalog alias が未フル解析——クライアントが宣伝通り gpt-5.5/gemini-3.0-flash を送るも、selector 不発で無料の swe-1-6-slow へ静かに降格。GPT 頼みで SWE が来る。同日、スナップショット fixture を登録してドリフト防止。","ko":"catalog alias가 전부 해석되지 않음 — 클라이언트가 광고된 대로 gpt-5.5/gemini-3.0-flash를 요청, selector 미매치 시 무음 다운그레이드로 무료 swe-1-6-slow로 전달. GPT를 원했는데 SWE를 받았다. 스냅샷 fixture를 당일 추가해 드리프트 방지.","fr":"Les alias du catalogue n'étaient pas tous résolus — les clients envoyaient gpt-5.5/gemini-3.0-flash comme annoncé, le selector manquait sa cible et dégradait silencieusement vers le gratuit swe-1-6-slow. On demande du GPT, on reçoit du SWE. Des snapshots ont été commités le jour même pour prévenir la dérive.","de":"Katalog-Aliasse wurden nicht vollständig aufgelöst — Clients schickten gpt-5.5/gemini-3.0-flash wie beworben, der Selector verfehlte und stufte still auf das kostenlose swe-1-6-slow ab. GPT bestellt, SWE bekommen. Snapshot-Fixtures wurden am selben Tag committet, um Drift zu verhindern.","es":"Los alias del catálogo no se resolvían del todo — los clientes enviaban gpt-5.5/gemini-3.0-flash como se anunciaba, el selector fallaba y degradaba silenciosamente al gratuito swe-1-6-slow. Pediste GPT, recibiste SWE. Los fixtures de snapshot se commitearon el mismo día para evitar la deriva."},"sh_13_t":{"zh":"jimp 违反零依赖铁律","en":"jimp breaks the zero-dependency iron rule","zh-TW":"jimp 違反零依賴鐵律","ja":"jimp が零依存の鉄則を破る","ko":"jimp, 제로 의존성 철칙 위반","fr":"jimp brise la règle de fer du zéro dépendance","de":"jimp bricht die eiserne Null-Abhängigkeit-Regel","es":"jimp rompe la regla de hierro de cero dependencias"},"sh_13_desc":{"zh":"项目核心卖点是零 npm 运行时依赖,引入 jimp 当日 CI 红,次日 vendor 化回收(jpeg-js/png.js,package-lock -796 行)。","en":"The project's core selling point is zero npm runtime dependencies; the day jimp landed, CI went red, and the next day it was vendored back out (jpeg-js/png.js, package-lock -796 lines).","zh-TW":"專案核心賣點是零 npm 執行期依賴,引入 jimp 當日 CI 紅,次日 vendor 化回收(jpeg-js/png.js,package-lock -796 行)。","ja":"プロジェクトの核となる売りが npm ランタイム零依存なのに、jimp を導入した当日に CI が赤、翌日に vendor 化して回収(jpeg-js/png.js、package-lock -796 行)。","ko":"프로젝트의 핵심 장점은 제로 npm 런타임 의존성인데 jimp를 도입하자 당일 CI 레드, 다음 날 vendor화로 수습(jpeg-js/png.js, package-lock -796줄).","fr":"Le cœur de l'argument du projet est zéro dépendance npm au runtime ; le jour où jimp est arrivé, la CI est passée au rouge, et le lendemain il a été revendorisé (jpeg-js/png.js, package-lock -796 lignes).","de":"Das Kern-Verkaufsargument des Projekts ist null npm-Laufzeitabhängigkeiten; am Tag, als jimp kam, wurde die CI rot, und am nächsten Tag wurde es wieder vendorisiert (jpeg-js/png.js, package-lock -796 Zeilen).","es":"El argumento central del proyecto es cero dependencias npm en runtime; el día que llegó jimp, la CI se puso roja, y al día siguiente se revendorizó (jpeg-js/png.js, package-lock -796 líneas)."},"sh_14_t":{"zh":"host/key 泄漏进公开仓","en":"Hosts & keys leaked into the public repo","zh-TW":"host/key 洩漏進公開倉","ja":"host/key が公開リポジトリに漏れる","ko":"host/key 공개 레포 유출","fr":"Hosts et clés fuient dans le dépôt public","de":"Hosts & Keys in das öffentliche Repo geleakt","es":"Hosts y claves filtrados al repo público"},"sh_14_desc":{"zh":"历史遗留:README/4 份 release notes/2 份 RE 文档/3 处测试烙有真实上游 host/key。redact 13 文件 + gitignore 屏蔽 + 删 1401 行内部笔记(内部资料反复混入公开仓)。","en":"A legacy issue: README, 4 release notes, 2 RE docs, and 3 test sites had real upstream host/key burned in. Redacted 13 files + gitignore shielding + deleted 1401 lines of internal notes (internal material kept leaking into the public repo).","zh-TW":"歷史遺留:README/4 份 release notes/2 份 RE 文件/3 處測試烙有真實上游 host/key。redact 13 個檔案 + gitignore 屏蔽 + 刪 1401 行內部筆記(內部資料反覆混入公開倉)。","ja":"歴史的遺物:README / release notes 4 件 / RE ドキュメント 2 件 / テスト 3 箇所に本物の上流 host/key が焼き付いていた。redact 13 ファイル + gitignore で遮断 + 内部メモ 1401 行を削除(内部資料が繰り返し公開リポジトリに混入)。","ko":"역사적 잔재: README/release notes 4개/RE 문서 2개/테스트 3곳에 실제 상류 host/key가 박혀 있었다. redact 13개 파일 + gitignore 차단 + 내부 메모 1401줄 삭제(내부 자료가 공개 레포에 반복적으로 섞여 들어감).","fr":"Un reliquat historique : le README, 4 release notes, 2 docs RE et 3 sites de test portaient de vrais host/clé upstream gravés. 13 fichiers redactés + blindage gitignore + 1401 lignes de notes internes supprimées (le matériel interne n'arrêtait pas de fuir dans le dépôt public).","de":"Eine Altlast: README, 4 Release-Notes, 2 RE-Dokumente und 3 Test-Stellen hatten echte Upstream-Hosts/Keys eingebrannt. 13 Dateien redacted + gitignore-Abschirmung + 1401 Zeilen interne Notizen gelöscht (internes Material leckte ständig ins öffentliche Repo).","es":"Una herencia histórica: el README, 4 release notes, 2 docs de RE y 3 sitios de test tenían host/clave reales del upstream incrustados. 13 archivos redactados + blindaje con gitignore + 1401 líneas de notas internas borradas (el material interno no paraba de filtrarse al repo público)."},"sh_15_t":{"zh":"v3.0.0 页面 ban 自己","en":"v3.0.0: the page bans itself","zh-TW":"v3.0.0 頁面 ban 自己","ja":"v3.0.0 のページが自分を ban","ko":"v3.0.0, 페이지가 자신을 ban","fr":"v3.0.0 : la page se bannit elle-même","de":"v3.0.0: die Seite bannt sich selbst","es":"v3.0.0: la página se banea a sí misma"},"sh_15_desc":{"zh":"Dashboard 打开即发 ~12 个空密码 401,每个都计入失败——页面一加载自己的 IP 就被 ban。v3.0.0 发布当天暴露、v3.0.2 收口。","en":"Opening the Dashboard fired ~12 empty-password 401s, each counted as a failure — the page got its own IP banned on load. Exposed the day v3.0.0 shipped, patched up in v3.0.2.","zh-TW":"Dashboard 開啟即發 ~12 個空密碼 401,每個都計入失敗——頁面一載入自己的 IP 就被 ban。v3.0.0 發佈當天暴露、v3.0.2 收口。","ja":"Dashboard を開いた瞬間に空パスワードの 401 を約 12 回発火し、それぞれ失敗にカウント——ページを読み込んだだけで自分の IP が ban される。v3.0.0 公開当日に露呈、v3.0.2 で塞ぎ。","ko":"Dashboard를 열자마자 빈 비밀번호 401이 ~12개 발생, 각각 실패로 집계 — 페이지 로드 즉시 자기 IP가 ban됨. v3.0.0 출시 당일 노출, v3.0.2에서 봉합.","fr":"Ouvrir le Dashboard déclenchait ~12 erreurs 401 à mot de passe vide, chacune comptée comme un échec — la page faisait bannir sa propre IP au chargement. Exposé le jour de la sortie de v3.0.0, colmaté dans v3.0.2.","de":"Das Öffnen des Dashboards feuerte ~12 Leerpasswort-401s ab, jede als Fehlversuch gezählt — die Seite ließ ihre eigene IP beim Laden bannen. Am Tag der v3.0.0-Veröffentlichung aufgedeckt, in v3.0.2 gestopft.","es":"Abrir el Dashboard disparaba ~12 errores 401 de password vacío, cada uno contado como fallo — la página hacía que su propia IP fuera baneada al cargar. Expuesto el día del lanzamiento de v3.0.0, parcheado en v3.0.2."},"sh_16_t":{"zh":"gpt-5.6-sol 烧账号","en":"gpt-5.6-sol burns an account","zh-TW":"gpt-5.6-sol 燒帳戶","ja":"gpt-5.6-sol がアカウントを食い潰す","ko":"gpt-5.6-sol 계정 태움","fr":"gpt-5.6-sol brûle un compte","de":"gpt-5.6-sol verbrennt ein Konto","es":"gpt-5.6-sol quema una cuenta"},"sh_16_desc":{"zh":"live-catalog 把 family alias 也折进 selectors → 原样透传 → UPSTREAM_INTERNAL 烧 homecloud 单账号(真号坐实)。根治:只折 canonical + fail closed,2547 测试绿。","en":"live-catalog folded family aliases into the selectors → passed through as-is → UPSTREAM_INTERNAL burned a single homecloud account (confirmed on a real account). Root fix: fold canonical only + fail closed; 2547 tests green.","zh-TW":"live-catalog 把 family alias 也折進 selectors → 原樣透傳 → UPSTREAM_INTERNAL 燒 homecloud 單一帳戶(真號坐實)。根治:只折 canonical + fail closed,2547 測試綠。","ja":"live-catalog が family alias まで selectors に折り込み → そのまま素通し → UPSTREAM_INTERNAL で homecloud の単一アカウントを食い潰す(実アカウントで確定)。根治:canonical のみ折り込み + fail closed、2547 テストがグリーン。","ko":"live-catalog가 family alias까지 selectors에 포함 → 그대로 투과 → UPSTREAM_INTERNAL이 homecloud 단일 계정 태움(실제 계정으로 피해 확인). 근본 해결: canonical만 포함 + fail closed, 테스트 2547개 그린.","fr":"live-catalog a plié les alias de famille dans les selectors → transmis tels quels → UPSTREAM_INTERNAL a brûlé un seul compte homecloud (confirmé sur un vrai compte). Fix racine : plier uniquement le canonique + fail closed ; 2547 tests au vert.","de":"live-catalog faltete Family-Aliasse in die Selectors → unverändert durchgereicht → UPSTREAM_INTERNAL verbrannte ein einzelnes homecloud-Konto (auf echtem Konto bestätigt). Grundfix: nur canonical falten + fail closed; 2547 Tests grün.","es":"live-catalog plegó los alias de familia en los selectors → se pasaron tal cual → UPSTREAM_INTERNAL quemó una única cuenta de homecloud (confirmado en una cuenta real). Fix de raíz: plegar solo lo canónico + fail closed; 2547 tests en verde."},"sh_17_t":{"zh":"Windows 热更新致命 bug","en":"Windows hot-reload: a fatal bug","zh-TW":"Windows 熱更新致命 bug","ja":"Windows ホットアップデートの致命 bug","ko":"Windows 핫업데이트 치명적 버그","fr":"Hot-reload Windows : un bug fatal","de":"Windows-Hot-Reload: ein fataler Bug","es":"Hot-reload en Windows: un bug fatal"},"sh_17_desc":{"zh":"v3.2.2 上线的 Windows 脚本套件热更新退出码致命(stop.ps1 杀不掉 supervisor 副 bug),v3.2.6 当天 6 个 ps1 改 114 行收掉。","en":"The hot-reload shipped in v3.2.2 had a fatal exit-code bug in the Windows script suite (stop.ps1 couldn't kill the supervisor — the side bug); v3.2.6 wrapped it up the same day: 114 lines across 6 ps1 files.","zh-TW":"v3.2.2 上線的 Windows 腳本套件熱更新退出碼致命(stop.ps1 殺不掉 supervisor 副 bug),v3.2.6 當天 6 個 ps1 改 114 行收掉。","ja":"v3.2.2 で公開した Windows スクリプト群のホットアップデートが退出コードで致命的な bug(stop.ps1 が supervisor を殺せない副次的な bug)、v3.2.6 当日に ps1 6 ファイル・114 行の修正で収拾。","ko":"v3.2.2에서 도입한 Windows 스크립트 제품군의 핫업데이트가 종료 코드 치명적 오류(stop.ps1이 supervisor를 못 죽이는 부수 버그), v3.2.6 당일 ps1 6개를 114줄 수정해 정리.","fr":"Le hot-reload livré dans v3.2.2 avait un bug fatal de code de sortie dans la suite de scripts Windows (stop.ps1 ne pouvait pas tuer le supervisor — le bug secondaire) ; v3.2.6 a bouclé le jour même : 114 lignes réparties sur 6 fichiers ps1.","de":"Der in v3.2.2 ausgelieferte Hot-Reload hatte einen fatalen Exit-Code-Bug im Windows-Skriptpaket (stop.ps1 konnte den Supervisor nicht töten — der Nebeneffekt-Bug); v3.2.6 räumte es am selben Tag ab: 114 Zeilen über 6 ps1-Dateien.","es":"El hot-reload publicado en v3.2.2 tenía un bug fatal de código de salida en el conjunto de scripts de Windows (stop.ps1 no podía matar al supervisor — el bug secundario); v3.2.6 lo cerró el mismo día: 114 líneas en 6 archivos ps1."},"sh_18_t":{"zh":"v3.9.0 当天被打穿","en":"v3.9.0: shredded on day one","zh-TW":"v3.9.0 當天被打穿","ja":"v3.9.0 当日に打ち抜かれる","ko":"v3.9.0 당일 방어 뚫림","fr":"v3.9.0 : déchiqueté le jour même","de":"v3.9.0: am Tag eins zerlegt","es":"v3.9.0: destrozado el primer día"},"sh_18_desc":{"zh":"对抗复核第二轮:1 blocker + 4 major 全部是 30485c3 上一批修复自己引入的(response-store 重写区 167 行 diff)。当天自伤-自愈闭环。","en":"Adversarial review, round two: 1 blocker + 4 majors, every one self-introduced by the previous batch of fixes in 30485c3 (167-line diff in the response-store rewrite). Self-inflicted → self-healed, closed the same day.","zh-TW":"對抗複核第二輪:1 blocker + 4 major 全部是 30485c3 上一批修復自己引入的(response-store 重寫區 167 行 diff)。當天自傷-自癒閉環。","ja":"対抗レビュー第2ラウンド:blocker 1 + major 4 が全て、直前の 30485c3 の修正群が自分で持ち込んだもの(response-store 書き換え部 167 行 diff)。当日に自爆→自癒のループを完結。","ko":"대립형 재검토 2차: blocker 1건 + major 4건 전부 30485c3의 직전 수정 묶음이 스스로 유발(response-store 재작성 구간 diff 167줄). 당일 자업자득-자가 치유로 마무리.","fr":"Review contradictoire, deuxième round : 1 blocker + 4 majeurs, tous auto-introduits par le lot de fixes précédent dans 30485c3 (diff de 167 lignes dans la réécriture du response-store). Auto-infligé → auto-réparé, bouclé le jour même.","de":"Adversarisches Review, Runde zwei: 1 blocker + 4 major, jedes selbst eingeführt durch das vorherige Fix-Paket in 30485c3 (167-Zeilen-Diff in der response-store-Umschreibung). Selbst verursacht → selbst geheilt, am selben Tag geschlossen.","es":"Review adversarial, segunda ronda: 1 blocker + 4 major, todos auto-introducidos por el lote de fixes anterior en 30485c3 (diff de 167 líneas en la reescritura del response-store). Autoinfligido → autocurado, cerrado el mismo día."},"sh_19_t":{"zh":"v3.9.2 只修了 1/3","en":"v3.9.2: only 1/3 fixed","zh-TW":"v3.9.2 只修了 1/3","ja":"v3.9.2 は 1/3 しか直していない","ko":"v3.9.2, 1/3만 수정","fr":"v3.9.2 : seulement 1/3 corrigé","de":"v3.9.2: nur 1/3 gefixt","es":"v3.9.2: solo 1/3 arreglado"},"sh_19_desc":{"zh":"合成终止帧漏洞只修了 responses 路由,messages/gemini 两条路由两天后补全——v3.9.2 发布说明自己承认「只修了 1/3」。","en":"The synthetic end-frame hole was patched on the responses route only; the messages/gemini routes were completed two days later — the v3.9.2 release notes themselves admitted 'only 1/3 fixed'.","zh-TW":"合成終止幀漏洞只修了 responses 路由,messages/gemini 兩條路由兩天後補全——v3.9.2 發佈說明自己承認「只修了 1/3」。","ja":"合成終端フレームの穴は responses ルートしか直されておらず、messages/gemini の2ルートは2日後に補完——v3.9.2 のリリースノートが自ら「1/3 しか直していない」と認めた。","ko":"합성 종료 프레임 취약점을 responses 라우트만 고치고, messages/gemini 두 라우트는 이틀 뒤에 보완 — v3.9.2 릴리스 노트가 스스로 '1/3만 고쳤다'고 인정.","fr":"Le trou de frame de fin synthétique n'a été corrigé que sur la route responses ; les routes messages/gemini ont été complétées deux jours plus tard — les release notes de v3.9.2 admettent elles-mêmes « seulement 1/3 corrigé ».","de":"Das synthetische End-Frame-Loch wurde nur auf der responses-Route gepatcht; die messages/gemini-Routen wurden zwei Tage später vervollständigt — die Release-Notes von v3.9.2 geben selbst zu: „nur 1/3 gefixt“.","es":"El agujero del frame de fin sintético solo se parcheó en la ruta responses; las rutas messages/gemini se completaron dos días después — las release notes de v3.9.2 admiten ellas mismas 'solo 1/3 arreglado'."},"sh_20_t":{"zh":"检索端点 8 小时不可用","en":"Retrieval endpoint down for 8 hours","zh-TW":"檢索端點 8 小時不可用","ja":"取得エンドポイント 8 時間ダウン","ko":"조회 엔드포인트 8시간 먹통","fr":"Endpoint de récupération indisponible 8 heures","de":"Retrieval-Endpoint 8 Stunden down","es":"Endpoint de recuperación caído 8 horas"},"sh_20_desc":{"zh":"GET/DELETE /v1/responses/{id} 自 v3.9.1 发布起对所有客户端不可用,8 小时后才修(v3.9.4)。测试曾把 bug 固化成契约。","en":"GET/DELETE /v1/responses/{id} was unavailable to every client from the v3.9.1 release, fixed only 8 hours later (v3.9.4). A test had even hardened the bug into a contract.","zh-TW":"GET/DELETE /v1/responses/{id} 自 v3.9.1 發佈起對所有用戶端不可用,8 小時後才修(v3.9.4)。測試曾把 bug 固化成契約。","ja":"GET/DELETE /v1/responses/{id} が v3.9.1 公開以降すべてのクライアントで使えず、8 時間後にようやく修正(v3.9.4)。テストが bug を契約として固定化していた。","ko":"GET/DELETE /v1/responses/{id}가 v3.9.1 릴리스부터 모든 클라이언트에 사용 불가, 8시간 뒤에야 수정(v3.9.4). 테스트가 버그를 계약으로 굳혀버렸던 사례.","fr":"GET/DELETE /v1/responses/{id} était indisponible pour tous les clients depuis la release v3.9.1, corrigé seulement 8 heures plus tard (v3.9.4). Un test avait même figé le bug en contrat.","de":"GET/DELETE /v1/responses/{id} war seit der v3.9.1-Veröffentlichung für alle Clients nicht verfügbar, erst 8 Stunden später gefixt (v3.9.4). Ein Test hatte den Bug sogar zu einem Vertrag verhärtet.","es":"GET/DELETE /v1/responses/{id} estuvo indisponible para todos los clientes desde la release v3.9.1, arreglado solo 8 horas después (v3.9.4). Un test incluso había endurecido el bug convertido en contrato."},"sh_21_t":{"zh":"8 条假测试清理","en":"8 fake tests cleaned out","zh-TW":"8 條假測試清理","ja":"フェイクテスト 8 件の掃除","ko":"가짜 테스트 8건 정리","fr":"8 faux tests nettoyés","de":"8 Fake-Tests ausgemistet","es":"8 tests falsos limpiados"},"sh_21_desc":{"zh":"「修 5 条自己写的假测试——源码 grep 冒充行为守卫 + 条件断言 + 恒真断言」。7-27 起累计清理 8 条自产假测试。","en":"'Fixed 5 fake tests we wrote ourselves — source greps posing as behavior guards, conditional assertions, always-true assertions.' A total of 8 self-produced fake tests cleaned out since 7-27.","zh-TW":"「修 5 條自己寫的假測試——原始碼 grep 冒充行為守衛 + 條件斷言 + 恆真斷言」。7-27 起累計清理 8 條自產假測試。","ja":"「自分で書いたフェイクテスト 5 件を直す——ソースコードの grep を挙動ガードに偽装 + 条件付きアサーション + 恒真アサーション」。7-27 以降、自前のフェイクテスト計 8 件を掃除。","ko":"「자기가 쓴 가짜 테스트 5개 고치기 — 소스 grep으로 행동 가드를 위장 + 조건부 단언 + 항상 참 단언」. 7-27부터 자체 생산 가짜 테스트 8건 정리.","fr":"« Corrigé 5 faux tests qu'on avait écrits nous-mêmes — des grep de sources déguisés en gardes de comportement, des assertions conditionnelles, des assertions toujours vraies. » Au total, 8 faux tests autoproduits nettoyés depuis le 7-27.","de":"„5 selbst geschriebene Fake-Tests gefixt — Source-Greps als Verhaltens-Guards getarnt, bedingte Assertions, immer-wahre Assertions.“ Insgesamt seit dem 7-27 8 selbst produzierte Fake-Tests ausgemistet.","es":"'Arreglados 5 tests falsos que escribimos nosotros mismos — greps de código fuente disfrazados de guards de comportamiento, aserciones condicionales, aserciones siempre verdaderas.' En total, 8 tests falsos auto-producidos limpiados desde el 7-27."},"sh_22_t":{"zh":"sticky 修复当天 revert","en":"sticky fix reverted the same day","zh-TW":"sticky 修復當天 revert","ja":"sticky の修正が当日 revert","ko":"sticky 수정 당일 revert","fr":"Le fix sticky reverté le jour même","de":"sticky-Fix noch am selben Tag revertet","es":"El fix de sticky revertido el mismo día"},"sh_22_desc":{"zh":"60 秒 RPM 抖动永久迁走会话的修复当天就被 revert——实测「保留绑定会散射,比清绑定更贵」,结论反转(v3.9.13 notes 写明)。","en":"The fix that permanently moved sessions on 60-second RPM jitter was reverted the same day — measurements showed 'keeping the binding scatters worse than clearing it', a flipped conclusion (spelled out in the v3.9.13 notes).","zh-TW":"60 秒 RPM 抖動永久遷走會話的修復當天就被 revert——實測「保留綁定會散射,比清綁定更貴」,結論反轉(v3.9.13 notes 寫明)。","ja":"60 秒の RPM 揺らぎでセッションを永久に移す修正が当日 revert——実測で「バインディングを残すと散乱し、消すより高くつく」となり、結論が反転(v3.9.13 notes に明記)。","ko":"60초 RPM 변동으로 세션을 영구 이전하던 수정이 당일 revert됨 — 실측 결과 '바인딩을 유지하면 산란이 발생해 정리보다 더 비싸다', 결론이 뒤집힘(v3.9.13 노트에 명시).","fr":"Le fix qui déplaçait définitivement les sessions sur les à-coups RPM de 60 secondes a été reverté le jour même — les mesures montraient que « garder le binding disperse plus que le nettoyer », une conclusion inversée (écrite noir sur blanc dans les notes v3.9.13).","de":"Der Fix, der Sessions bei 60-Sekunden-RPM-Jitter dauerhaft umzog, wurde am selben Tag revertet — Messungen zeigten: „Bindung behalten streut schlimmer als sie zu löschen“, eine umgedrehte Schlussfolgerung (in den v3.9.13-Notizen ausdrücklich festgehalten).","es":"El fix que movía permanentemente las sesiones con los jitters de RPM de 60 segundos se revirtió el mismo día — las mediciones mostraron que 'mantener el binding dispersa peor que limpiarlo', una conclusión invertida (escrita en las notes de v3.9.13)."},"sh_23_t":{"zh":"守卫三连崩","en":"The flag guards fail three times","zh-TW":"守衛三連崩","ja":"ガード3連続崩壊","ko":"가드 3연속 붕괴","fr":"Les gardes de flags échouent trois fois","de":"Die Flag-Guards scheitern dreimal","es":"Los guards de flags fallan tres veces"},"sh_23_desc":{"zh":"默认开开关发现机制三连漏写法(某赋值形式→??→=== '0'),每次修都挖出更多未登记开关(累计 8 个,含凭证开关对守卫隐形)。","en":"The default-on flag-discovery mechanism missed three syntaxes in a row (one assignment form → ?? → === '0'); every fix surfaced more unregistered flags (8 total, incl. credential flags invisible to the guards).","zh-TW":"預設開關發現機制三連漏寫法(某賦值形式→??→=== '0'),每次修都挖出更多未登記開關(累計 8 個,含憑證開關對守衛隱形)。","ja":"デフォルトオン機能の発見機構が3連続で書き漏れ(ある代入形式→??→=== '0')、直すたびに未登録スイッチがさらに掘り出される(累計 8 個、credential スイッチがガードに見えていなかったものも含む)。","ko":"기본 활성 스위치 발견 메커니즘이 표기 방식 3연속 놓침(어떤 할당 형식→??→=== '0'), 고칠 때마다 미등록 스위치가 더 발굴됨(누적 8개, 자격 증명 스위치가 가드에 보이지 않던 것 포함).","fr":"Le mécanisme de détection des flags actifs par défaut a raté trois syntaxes d'affilée (une forme d'affectation → ?? → === '0') ; chaque fix a fait remonter plus de flags non enregistrés (8 au total, dont des flags de credentials invisibles pour les gardes).","de":"Der Default-On-Flag-Erkennungsmechanismus verpasste drei Syntaxen in Folge (eine Zuweisungsform → ?? → === '0'); jeder Fix förderte mehr nicht registrierte Flags zutage (8 insgesamt, darunter Credential-Flags, die für die Guards unsichtbar sind).","es":"El mecanismo de descubrimiento de flags activos por defecto falló tres sintaxis seguidas (una forma de asignación → ?? → === '0'); cada fix sacó a la luz más flags no registrados (8 en total, incluidos flags de credenciales invisibles para los guards)."},"sh_24_t":{"zh":"SWE kimi 方言过度推广","en":"SWE: kimi dialect over-generalized","zh-TW":"SWE kimi 方言過度推廣","ja":"kimi 方言の SWE への過剰適用","ko":"SWE kimi 방언 과잉 적용","fr":"SWE : dialecte kimi sur-généralisé","de":"SWE: kimi-Dialekt überverallgemeinert","es":"SWE: dialecto kimi sobre-generalizado"},"sh_24_desc":{"zh":"SWE 全家路由到 kimi_k2 方言 → 两天后排除 swe lightning(6cc2886)——过度推广的修正。","en":"The whole SWE family was routed to the kimi_k2 dialect → swe lightning was excluded two days later (6cc2886) — an over-generalization, corrected.","zh-TW":"SWE 全家路由到 kimi_k2 方言 → 兩天後排除 swe lightning(6cc2886)——過度推廣的修正。","ja":"SWE ファミリーを kimi_k2 方言へ全ルーティング → 2日後に swe lightning を除外(6cc2886)——過剰適用の修正。","ko":"SWE 전 제품군을 kimi_k2 방언으로 라우팅 → 이틀 뒤 swe lightning 제외(6cc2886) — 과잉 적용의 수정.","fr":"Toute la famille SWE a été routée vers le dialecte kimi_k2 → swe lightning a été exclu deux jours plus tard (6cc2886) — une sur-généralisation, corrigée.","de":"Die ganze SWE-Familie wurde auf den kimi_k2-Dialekt geroutet → swe lightning wurde zwei Tage später ausgeschlossen (6cc2886) — eine Überverallgemeinerung, korrigiert.","es":"Toda la familia SWE se enrutó al dialecto kimi_k2 → swe lightning se excluyó dos días después (6cc2886) — una sobre-generalización, corregida."},"sh_25_t":{"zh":"#252 两处互相抵消","en":"#252: two changes cancel each other out","zh-TW":"#252 兩處互相抵消","ja":"#252 の2箇所が互いに打ち消し合う","ko":"#252 두 곳이 서로 상쇄","fr":"#252 : deux changements s'annulent","de":"#252: zwei Änderungen heben sich gegenseitig auf","es":"#252: dos cambios que se anulan entre sí"},"sh_25_desc":{"zh":"抑制逻辑靠检测 <tool_call> 标记触发,同一 PR 把默认值里的标记删了——抑制变永久 no-op,合并时守卫与审查都没发现,合并当天发现并修。","en":"The suppression logic triggers on <tool_call> markers — and the same PR deleted the marker from the default value, turning suppression into a permanent no-op. Neither the guards nor review caught it at merge time; found and fixed the day of the merge.","zh-TW":"抑制邏輯靠偵測 <tool_call> 標記觸發,同一個 PR 把預設值裡的標記刪了——抑制變永久 no-op,合併時守衛與審查都沒發現,合併當天發現並修。","ja":"抑制ロジックは <tool_call> マーカー検出で発火するのに、同じ PR がデフォルト値からマーカーを削除——抑制が恒久 no-op 化し、merge 時にガードもレビューも気づかず、merge 当日に発見・修正。","ko":"억제 로직이 <tool_call> 마커 감지로 작동하는데, 같은 PR에서 기본값의 마커를 삭제 — 억제가 영구 no-op이 됨. merge 시점에 가드도 리뷰도 발견 못 했고, merge 당일 발견해 수정.","fr":"La logique de suppression se déclenche sur les marqueurs <tool_call> — et la même PR a supprimé le marqueur de la valeur par défaut, transformant la suppression en no-op permanent. Ni les gardes ni le review ne l'ont vu au moment du merge ; trouvé et corrigé le jour du merge.","de":"Die Unterdrückungslogik feuert auf <tool_call>-Marker — und dieselbe PR löschte den Marker aus dem Default-Wert, was die Unterdrückung in ein permanentes no-op verwandelte. Weder Guards noch Review bemerkten es beim Merge; am Mergertag gefunden und gefixt.","es":"La lógica de supresión se dispara con los marcadores <tool_call> — y el mismo PR eliminó el marcador del valor por defecto, convirtiendo la supresión en un no-op permanente. Ni los guards ni el review lo vieron al fusionar; encontrado y arreglado el día del merge."},"sh_26_t":{"zh":"'86/86 收齐'被推翻","en":"'86/86 accounted for' — overturned","zh-TW":"'86/86 收齊'被推翻","ja":"「86/86 達成」が覆される","ko":"'86/86 전부 확보' 뒤집힘","fr":"« 86/86 comptabilisés » — renversé","de":"»86/86 vollständig« — umgestoßen","es":"'86/86 contabilizados' — derrocado"},"sh_26_desc":{"zh":"「35/35」「86/86 收齐」声明被 4ada929 一次补 47 个开关推翻(含全部 5 个 wire 坐标);6202c34 修正则后又补 2 个,数字修正链 156→157→158。","en":"The '35/35' and '86/86 accounted for' claims were overturned when 4ada929 added 47 flags in one shot (including all 5 wire coordinates); after 6202c34's correction, 2 more surfaced — the correction chain: 156→157→158.","zh-TW":"「35/35」「86/86 收齊」聲明被 4ada929 一次補 47 個開關推翻(含全部 5 個 wire 座標);6202c34 修正則後又補 2 個,數字修正鏈 156→157→158。","ja":"「35/35」「86/86 達成」の宣言が 4ada929 のスイッチ 47 個一括補充で覆される(wire 座標全 5 個含む);6202c34 の修正後にさらに 2 個補充され、数字の修正チェーン 156→157→158。","ko":"'35/35' '86/86 전부 확보' 선언이 4ada929의 스위치 47개 일괄 보충으로 무너짐(wire 좌표 5개 전부 포함); 6202c34 수정 이후 2개 추가, 숫자 수정 체인 156→157→158.","fr":"Les affirmations « 35/35 » et « 86/86 comptabilisés » ont été renversées quand 4ada929 a ajouté 47 flags d'un coup (dont les 5 coordonnées wire) ; après la correction de 6202c34, 2 de plus ont surgi — la chaîne de corrections : 156→157→158.","de":"Die Behauptungen »35/35« und »86/86 vollständig« wurden umgestoßen, als 4ada929 mit einem Schlag 47 Flags ergänzte (darunter alle 5 wire-Koordinaten); nach der Korrektur von 6202c34 kamen 2 weitere zum Vorschein — die Korrekturkette: 156→157→158.","es":"Las afirmaciones de '35/35' y '86/86 contabilizados' se derrocaron cuando 4ada929 añadió 47 flags de una vez (incluidas las 5 coordenadas wire); tras la corrección de 6202c34, salieron 2 más — la cadena de correcciones: 156→157→158."},"sh_27_t":{"zh":"账本自己踩 sk- 坑","en":"The ledger trips the sk- trap itself","zh-TW":"帳本自己踩 sk- 坑","ja":"帳簿が自ら sk- の罠に嵌まる","ko":"장부가 스스로 sk- 함정","fr":"Le registre se prend lui-même au piège sk-","de":"Das Ledger tappt selbst in die sk--Falle","es":"El ledger cae él mismo en la trampa sk-"},"sh_27_desc":{"zh":"账本描述 f3e0094(sk- 前缀误报修复)时原文引用了夹具 key 形态,触发 secret-scan 导致 CI 红——「审计工具与被审对象共享盲点」的第三个实例。","en":"When the ledger described f3e0094 (the sk- false-positive fix), it quoted a fixture key shape verbatim, tripping secret-scan and turning CI red — the third instance of 'the auditor sharing the audited's blind spot'.","zh-TW":"帳本描述 f3e0094(sk- 前綴誤報修復)時原文引用了夾具 key 型態,觸發 secret-scan 導致 CI 紅——「稽核工具與被稽核對象共享盲點」的第三個實例。","ja":"帳簿が f3e0094(sk- 接頭辞の誤検知修正)を説明する際、原文がフィクスチャ key の形を引用し、secret-scan を発火させ CI が赤——「監査ツールと被監査対象が盲点を共有する」3例目。","ko":"장부가 f3e0094(sk- 접두사 오탐 수정)를 설명하며 원문에 fixture key 형태를 인용, secret-scan이 발동해 CI 레드 — '감사 도구와 피감사 대상이 같은 사각지대를 공유'하는 세 번째 사례.","fr":"En décrivant f3e0094 (le fix du faux positif sk-), le registre a cité textuellement une forme de clé de fixture, déclenchant secret-scan et faisant passer la CI au rouge — le troisième cas de « l'auditeur partageant l'angle mort de l'audité ».","de":"Als das Ledger f3e0094 beschrieb (den sk--Fehlalarm-Fix), zitierte es wörtlich eine Fixture-Key-Form, löste secret-scan aus und machte die CI rot — das dritte Beispiel für „der Auditor teilt den blinden Fleck des Audit-Objekts“.","es":"Al describir f3e0094 (el fix del falso positivo sk-), el ledger citó textualmente una forma de clave de fixture, disparando secret-scan y poniendo la CI roja — el tercer caso de 'el auditor comparte el punto ciego del auditado'."},"bio_warelik":{"zh":"kimi 之类的客户端会在长对话里压缩历史:0/31 保留的 pair 逐字或规范都存活不下来,客户端改写保留尾 —— 但对话的第一轮输入逐字存活。这个 PR 用 root anchor(首轮输入哈希)索引每个 state,compaction 之后 pair 证据全丢时,通过 root 重新关联。三条防线:root-index the fork(分叉共享开端的 root,避免压缩后被并回原会话的 hijack)、模糊规则(多个活跃候选无 pair 证据 → 不分配给任何,生成新 id)、TTL 驱逐(过期 state 不通过压缩复活)。guard 只在「没有任何入站哈希命中索引」时走 root(seen 为空)—— 前缀 pair 仍命中的分叉对话不得被 root 重关联。新增 spec session-continuity-compaction-survival.json 3 条突变全 CAUGHT。未到 S 是因为「survive client history compaction」只在单对话成立,而 PR 描述说成通用能力;另外与 #242 的 findExistingState 抽取有结构性交互(git 三方合并看不见,合并时实测 15 条测试失败后修复),说明拆函数抽局部这类重构跨 PR 叠加时风险在文本层之下。","en":"Clients like kimi compress history in long conversations: the 0/31 kept pairs survive neither verbatim nor normalized, the client's rewrite keeps the tail — but the conversation's very first-round input survives verbatim. This PR indexes every state with a root anchor (hash of the first-round input), and when all pair evidence is gone after compaction, re-associates through the root. Three defense lines: root-index the fork (forks share the root of their common start, avoiding a hijack where a compressed fork gets merged back into the original session), fuzzy rules (multiple active candidates with no pair evidence → assign to none, generate a new id), and TTL eviction (expired states don't resurrect through compaction). The guard only goes root when \"no inbound hash hits the index\" (seen is empty) — fork conversations whose prefix pairs still hit must NOT be root re-associated. New spec session-continuity-compaction-survival.json, all 3 mutations CAUGHT. Not S because \"survive client history compaction\" only holds within a single conversation while the PR description sells it as a generic capability; there's also a structural interaction with #242's findExistingState extraction (invisible to git three-way merge — 15 tests failed after merging, then fixed), a reminder that refactors like function extraction stack risk below the text layer across PRs.","zh-TW":"kimi 之類的客戶端會在長對話裡壓縮歷史:0/31 保留的 pair 逐字或規範都存活不下來,客戶端改寫保留尾 —— 但對話的第一輪輸入逐字存活。這個 PR 用 root anchor(首輪輸入雜湊)索引每個 state,compaction 之後 pair 證據全丟時,透過 root 重新關聯。三道防線:root-index the fork(分叉共享開端的 root,避免壓縮後被併回原會話的 hijack)、模糊規則(多個活躍候選無 pair 證據 → 不分配給任何,生成新 id)、TTL 驅逐(過期 state 不透過壓縮復活)。guard 只在「沒有任何入站雜湊命中索引」時走 root(seen 為空)—— 前綴 pair 仍命中的分叉對話不得被 root 重關聯。新增 spec session-continuity-compaction-survival.json 3 條突變全 CAUGHT。未到 S 是因為「survive client history compaction」只在單對話成立,而 PR 描述說成通用能力;另外與 #242 的 findExistingState 抽取有結構性互動(git 三方合併看不見,合併時實測 15 條測試失敗後修復),說明拆函數抽局部這類重構跨 PR 疊加時風險在文字層之下。","ja":"kimi のようなクライアントは長い会話で履歴を圧縮する:0/31 で保持された pair は逐語でも正規化でも生き残れず、クライアントの書き換えは末尾を残す —— しかし会話の最初のターン入力は逐語で生き残る。この PR は root anchor(最初のターン入力のハッシュ)で各 state をインデックスし、compaction 後に pair エビデンスがすべて消えた場合に root 経由で再関連付けする。3 つの防御線:root-index the fork(分岐は共通の開始 root を共有し、圧縮後に元のセッションへ併合されてしまう hijack を回避)、ファジー規則(pair エビデンスのない複数のアクティブ候補 → どれにも割り当てず新 id を生成)、TTL 駆逐(期限切れ state は圧縮経由で復活しない)。ガードは「入ってくるハッシュが何もインデックスにヒットしない」場合(seen が空)だけ root を使う —— プレフィックス pair がまだヒットする分岐会話は root で再関連付けしてはならない。新規 spec session-continuity-compaction-survival.json、3 つのミューテーションすべて CAUGHT。S に届かなかった理由は、「survive client history compaction」が単一会話内でのみ成立するのに、PR 説明は汎用能力のように書かれていたこと。さらに #242 の findExistingState 抽出と構造的に相互作用があり(git 三方マージでは見えず、マージ後にテスト 15 本が失敗して修正)、関数抽出のような局所リファクタリングが PR をまたいで積み重なると、リスクがテキスト層の下に潜むことを示している。","ko":"kimi 같은 클라이언트는 긴 대화에서 히스토리를 압축한다: 0/31로 유지된 pair는 그대로든 정규화든 살아남지 못하고, 클라이언트의 재작성은 꼬리를 유지한다 — 하지만 대화의 첫 번째 턴 입력은 그대로 살아남는다. 이 PR은 root anchor(첫 턴 입력 해시)로 각 state를 인덱스하고, compaction 후 pair 증거가 전부 사라졌을 때 root를 통해 재연결한다. 세 겹 방어선: root-index the fork(포크는 공통 시작점의 root를 공유해, 압축 후 원래 세션으로 병합돼버리는 hijack을 피함), 퍼지 규칙(쌍 증거가 없는 활성 후보가 여럿이면 아무에게도 할당하지 않고 새 id 생성), TTL 퇴거(만료된 state는 압축을 통해 부활하지 않음). 가드는 '들어오는 해시가 인덱스에 아무것도 맞지 않을 때'(seen이 빈 경우)에만 root를 쓰고, 프리픽스 pair가 여전히 맞는 포크 대화는 root로 재연결하면 안 된다. 새 spec session-continuity-compaction-survival.json, 변이 3개 전부 CAUGHT. S가 아닌 이유는 'survive client history compaction'이 단일 대화 안에서만 성립하는데 PR 설명은 일반 능력인 것처럼 썼기 때문. 또 #242의 findExistingState 추출과 구조적으로 상호작용하며(git 3-way 병합으로는 안 보이고, 병합 후 테스트 15개가 실패해 수정됨), 함수 추출 같은 국소 리팩터링이 PR을 넘어 쌓일 때 리스크가 텍스트 층 아래에 있다는 걸 보여준다.","fr":"Des clients comme kimi compressent l'historique dans les longues conversations : 0/31 paires conservées ne survivent pas — ni verbatim ni normalisées — et le client réécrit la fin survivante. Seule l'entrée du tout premier tour survit verbatim. Ce PR indexe chaque état avec un root anchor (hash du premier tour), compacti","de":"Clients wie kimi komprimieren in langen Gesprächen den Verlauf: 0/31 erhaltene Paare überleben — weder wortwörtlich noch normalisiert — und der Client schreibt das überlebende Ende um. Nur die allererste Turn-Eingabe überlebt wortwörtlich. Diese PR indexiert jeden Zustand mit einem Root-Anchor (Hash des ersten Turns), compacti","es":"Clientes como kimi comprimen el historial en conversaciones largas: 0/31 pares conservados no sobreviven — ni verbatim ni normalizados — y el cliente reescribe la cola superviviente. Solo la entrada del primer turn sobrevive verbatim. Este PR indexa cada estado con un root anchor (hash del primer turn), compacti"},"bio_baily-zhang":{"zh":"Claude Code 每次重构 assistant content(数组 ↔ 字符串 ↔ 含 tool_use blocks)而 system prompt 又含动态项目状态,老 fingerprint 把这两者 hash 进去导致永不匹配。改成只 hash user + tool_result 两类稳定轮,system prompt 通过 CASCADE_REUSE_HASH_SYSTEM=1 opt-in。外加可配置历史 budget(1m 模型更大默认)+ LS_DATA_DIR override。#24 \"胡言乱语\"爆发后的第一块拼图,和 PR #45 合起来算这条线上最彻底的修复。","en":"Claude Code keeps restructuring assistant content (array ↔ string ↔ tool_use blocks) and the system prompt carries dynamic project state, so the old fingerprint hashed both and never matched. Now only the stable user + tool_result rounds are hashed; the system prompt is handled via the CASCADE_REUSE_HASH_SYSTEM=1 opt-in. Plus a configurable history budget (larger default for 1m models) and an LS_DATA_DIR override. The first piece of the puzzle after the #24 \"nonsense\" outbreak — together with PR #45 it's the most thorough fix on this line.","zh-TW":"Claude Code 每次重構 assistant content(陣列 ↔ 字串 ↔ 含 tool_use blocks)而 system prompt 又含動態專案狀態,舊 fingerprint 把這兩者 hash 進去導致永不匹配。改成只 hash user + tool_result 兩類穩定輪,system prompt 透過 CASCADE_REUSE_HASH_SYSTEM=1 opt-in。外加可設定歷史 budget(1m 模型更大預設)+ LS_DATA_DIR override。#24「胡言亂語」爆發後的第一塊拼圖,和 PR #45 合起來算這條線上最徹底的修復。","ja":"Claude Code は assistant content を頻繁に再構築する(配列 ↔ 文字列 ↔ tool_use ブロック)うえ、system prompt には動的なプロジェクト状態が入るため、両方をハッシュしていた従来の fingerprint は永遠に一致しなかった。修正後は user と tool_result の安定した二種類のターンだけをハッシュし、system prompt は CASCADE_REUSE_HASH_SYSTEM=1 の opt-in で扱う。さらに履歴 budget の設定化(1m モデルはデフォルトを拡大)+ LS_DATA_DIR の override。これは #24 の「支離滅裂」大発生の後の最初のピースで、PR #45 と合わせればこのラインで最も徹底的な修正になる。","ko":"Claude Code가 assistant content를 자주 재구성하고(배열 ↔ 문자열 ↔ tool_use 블록) system prompt에 동적 프로젝트 상태가 들어가므로, 둘 다 해시하던 기존 fingerprint는 영원히 일치하지 않았다. 이제 user와 tool_result 두 가지 안정적인 턴만 해시하고, system prompt는 CASCADE_REUSE_HASH_SYSTEM=1 opt-in으로 처리한다. 여기에 설정 가능한 히스토리 budget(1m 모델은 기본값 확대) + LS_DATA_DIR override까지. #24 '횡설수설' 대발발 후 첫 번째 퍼즐 조각이며, PR #45와 합치면 이 라인에서 가장 철저한 수정이다.","fr":"Claude Code remodèle le contenu assistant à chaque tour (array ↔ string ↔ blocs tool_use) pendant que le system prompt porte l'état dynamique du projet ; l'ancien fingerprint hashait les deux, donc il ne matche ja","de":"Claude Code formt den Assistant-Inhalt in jedem Turn um (array ↔ string ↔ tool_use-Blöcke), während der System-Prompt dynamischen Projektstatus trägt; der alte Fingerprint hasht beide mit ein, also matchte er nie, ma","es":"Claude Code remodela el contenido del assistant en cada turn (array ↔ string ↔ bloques tool_use) mientras el system prompt lleva el estado dinámico del proyecto; el viejo fingerprint los hasheaba ambos, así que nunca mat"},"bio_smeinecke":{"zh":"修掉 Dashboard 登录流里 error code 直接以裸串显示的问题 —— 像 ERR_NO_PASSWORD_SET 这类直接糊在 toast 和登录历史里,没翻成人话。这个 PR 把 toasts / result 显示 / login history 三处的错误码全接上 i18n,登录历史表显示翻译后消息 + tooltip 保留原始 code。同时统一错误码命名向已有的 ERR_PROXY_PRIVATE_IP 看齐:ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP、ERR_PRIVATE_HOST → ERR_PROXY_PRIVATE_HOST,net-safety.js 和 ssrf.test.js 同步改名,补上缺失的 ERR_PROXY_PRIVATE_HOST 翻译键到 en.json / zh-CN.json。顺手把语言切换按钮的 tooltip 改成动态显示目标语言(Switch to English / 切换到中文)。","en":"Fixed the Dashboard login flow displaying error codes as raw strings — ERR_NO_PASSWORD_SET and friends plastered verbatim into toasts and login history instead of human-readable messages. This PR wires the error codes in all three spots — toasts / result display / login history — through i18n; the login history table shows the translated message with a tooltip retaining the original code. It also aligns error code naming with the existing ERR_PROXY_PRIVATE_IP convention: ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP, ERR_PRIVATE_HOST → ERR_PROXY_PRIVATE_HOST, with net-safety.js and ssrf.test.js renamed in sync and the missing ERR_PROXY_PRIVATE_HOST translation keys added to en.json / zh-CN.json. Along the way the language switch button tooltip now dynamically shows the target language (Switch to English / 切换到中文).","zh-TW":"修掉 Dashboard 登入流裡 error code 直接以裸字串顯示的問題 —— 像 ERR_NO_PASSWORD_SET 這類直接糊在 toast 和登入歷史裡,沒翻成人話。這個 PR 把 toasts / result 顯示 / login history 三處的錯誤碼全接上 i18n,登入歷史表顯示翻譯後訊息 + tooltip 保留原始 code。同時統一錯誤碼命名向已有的 ERR_PROXY_PRIVATE_IP 看齊:ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP、ERR_PRIVATE_HOST → ERR_PROXY_PRIVATE_HOST,net-safety.js 和 ssrf.test.js 同步改名,補上缺失的 ERR_PROXY_PRIVATE_HOST 翻譯鍵到 en.json / zh-CN.json。順手把語言切換按鈕的 tooltip 改成動態顯示目標語言(Switch to English / 切換到中文)。","ja":"Dashboard のログインフローでエラーコードが生文字列のまま表示される問題を修正 —— ERR_NO_PASSWORD_SET のようなコードが toast やログイン履歴にそのまま貼り付けられ、人間の言葉に翻訳されていなかった。この PR は toast / result 表示 / login history の 3 箇所のエラーコードをすべて i18n に接続し、ログイン履歴表には翻訳済みメッセージを表示しつつ tooltip で元のコードを残す。同時に既存の ERR_PROXY_PRIVATE_IP に合わせてエラーコード命名を統一:ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP、ERR_PRIVATE_HOST → ERR_PROXY_PRIVATE_HOST。net-safety.js と ssrf.test.js も同期して改名し、欠けていた ERR_PROXY_PRIVATE_HOST の翻訳キーを en.json / zh-CN.json に追加。ついでに言語切替ボタンの tooltip を対象言語を動的表示する形(Switch to English / 切换到中文)に変更。","ko":"Dashboard 로그인 흐름에서 error code가 원시 문자열로 표시되던 문제를 수정 — ERR_NO_PASSWORD_SET 같은 코드가 toast와 로그인 기록에 그대로 박혀 사람 말로 번역되지 않았다. 이 PR은 toast / result 표시 / login history 세 곳의 에러 코드를 전부 i18n에 연결하고, 로그인 기록 테이블에는 번역된 메시지를 보여주되 tooltip에 원본 코드를 남긴다. 동시에 기존 ERR_PROXY_PRIVATE_IP에 맞춰 에러 코드 명명을 통일한다: ERR_PRIVATE_IP → ERR_PROXY_PRIVATE_IP, ERR_PRIVATE_HOST → ERR_PROXY_PRIVATE_HOST, net-safety.js와 ssrf.test.js도 함께 개명하고, 누락된 ERR_PROXY_PRIVATE_HOST 번역 키를 en.json / zh-CN.json에 추가한다. 덤으로 언어 전환 버튼 tooltip이 대상 언어를 동적으로 표시하도록 바꿨다(Switch to English / 切换到中文).","fr":"Corrigé l'affichage du flux de login du Dashboard qui montrait des codes d'erreur bruts — ERR_NO_PASSWORD_SET et compagnie collés tels quels dans les toasts et l'historique de login, jamais traduits en langage humain. Ce PR retravaille l'affichage des toasts / des résultats / ","de":"Den Dashboard-Login-Flow gefixt, der rohe Fehlercodes anzeigte — ERR_NO_PASSWORD_SET und Co. wurden direkt in Toasts und Login-Verlauf geklatscht, nie in Menschensprache übersetzt. Diese PR überarbeitet Toasts / Result-Anzeige / ","es":"Arreglado el flujo de login del Dashboard que mostraba códigos de error crudos — ERR_NO_PASSWORD_SET y similares pegados directamente en los toasts y el historial de login, nunca traducidos a lenguaje humano. Este PR rehace los toasts / la visualización de resultados / "},"bio_aict666":{"zh":"updateCapability 在每次成功 chat 后无条件 inferTier() 覆写 account.tier。inferTier 只认 claude-opus-4.6 / sonnet-4.6 作为 Pro 证据,Pro 账号只要调用 gemini-2.5-flash 或 gpt-4o-mini 一次就被悄悄降级到 free,后续请求按 free 限制,Dashboard 显示 FREE。修法是当 GetUserStatus 已经给过权威 tier(userStatusLastFetched > 0 或 tierManual=true)时跳过 inferTier 回退。probeAccount 原本有手动 restore 工作绕过,chat handler 路径没有,每个真实 API 调用都在偷偷降级账号。","en":"`updateCapability` unconditionally calls `inferTier()` after every successful chat and overwrites account.tier. `inferTier` only recognizes claude-opus-4.6 / sonnet-4.6 as Pro evidence, so a Pro account gets silently downgraded to free the moment it calls gemini-2.5-flash or gpt-4o-mini once — subsequent requests run under free limits and the Dashboard shows FREE. The fix: skip the inferTier fallback when GetUserStatus has already supplied an authoritative tier (userStatusLastFetched > 0 or tierManual=true). probeAccount had a manual restore workaround; the chat handler path didn't — every real API call was silently downgrading accounts.","zh-TW":"updateCapability 在每次成功 chat 後無條件 inferTier() 覆寫 account.tier。inferTier 只認 claude-opus-4.6 / sonnet-4.6 作為 Pro 證據,Pro 帳號只要呼叫 gemini-2.5-flash 或 gpt-4o-mini 一次就被悄悄降級到 free,後續請求按 free 限制,Dashboard 顯示 FREE。修法是當 GetUserStatus 已經給過權威 tier(userStatusLastFetched > 0 或 tierManual=true)時跳過 inferTier 回退。probeAccount 原本有手動 restore 工作繞過,chat handler 路徑沒有,每個真實 API 呼叫都在偷偷降級帳號。","ja":"`updateCapability` が成功した chat のたびに無条件で `inferTier()` を呼び、account.tier を上書きする。inferTier は claude-opus-4.6 / sonnet-4.6 だけを Pro の根拠として認めるため、Pro アカウントは gemini-2.5-flash か gpt-4o-mini を一度でも呼ぶと無言で free に格下げされ、以降のリクエストは free の制限を受け、Dashboard にも FREE と表示される。修正は、GetUserStatus がすでに権威ある tier を返している場合(userStatusLastFetched > 0 または tierManual=true)は inferTier へのフォールバックをスキップするというもの。probeAccount には手動 restore の回避策があったが、chat handler の経路にはなく、実際の API 呼び出しのたびにアカウントがこっそり格下げされていた。","ko":"`updateCapability`가 성공한 chat마다 무조건 `inferTier()`를 호출해 account.tier를 덮어쓴다. inferTier는 claude-opus-4.6 / sonnet-4.6만 Pro 증거로 인정하므로, Pro 계정은 gemini-2.5-flash나 gpt-4o-mini를 한 번만 호출해도 조용히 free로 강등되고 이후 요청은 free 제한을 받으며 Dashboard에는 FREE로 표시된다. 수정 방법은 GetUserStatus가 이미 권위 있는 tier를 제공한 경우(userStatusLastFetched > 0 또는 tierManual=true) inferTier 폴백을 건너뛰는 것. probeAccount에는 수동 restore 우회 작업이 있었지만 chat handler 경로에는 없어서, 실제 API 호출마다 계정이 은밀히 강등되고 있었다.","fr":"updateCapability écrase inconditionnellement account.tier avec inferTier() après chaque chat réussi. inferTier ne reconnaît que claude-opus-4.6 / sonnet-4.6 comme preuve de Pro, donc les comptes Pro seulement","de":"updateCapability überschreibt nach jedem erfolgreichen Chat bedingungslos account.tier mit inferTier(). inferTier erkennt nur claude-opus-4.6 / sonnet-4.6 als Pro-Nachweis, also bekommen Pro-Konten nur","es":"updateCapability sobrescribe incondicionalmente account.tier con inferTier() tras cada chat exitoso. inferTier solo reconoce claude-opus-4.6 / sonnet-4.6 como prueba de Pro, así que las cuentas Pro solo"},"bio_andya1lan":{"zh":"四行改动,价值全在诊断。GitHub 已于 2025-12-04 下线 macos-13 runner 镜像,而 release.yml 的 x64 job 一直写着 runs-on: macos-13 —— v3.9.1 到 v3.9.4 的 x64 job 终态**全部是 cancelled 而非排队**,也就是连续四个版本都没有 macOS x64 二进制发出去。而我们这边一直把 GitHub UI 上的 queued 按 workflow 注释里\"x64 可能排几小时\"的说法解释,没去看终态,四次都判断成\"非阻塞、不用管\"。他附了 GitHub changelog 与 actions/runner-images README 两条官方依据,标签选择(macos-26-intel)也与官方当前 x64 标签一致,并保留了 x64 不进 release needs 的非阻塞语义。这条的分量不在代码量,而在于它是外部贡献者替维护方发现的自身发布链缺口 —— 与 lovivi #204 那种\"十行精准方言修复\"同档但更靠上,因为它纠正的是一个已经影响了四个已发布版本的事实。","en":"Four lines of change, and the entire value is in the diagnosis. GitHub retired the macos-13 runner image on 2025-12-04, but the x64 job in release.yml had been writing runs-on: macos-13 all along — the terminal state of the x64 job from v3.9.1 to v3.9.4 is **all cancelled, not queued**, meaning four consecutive releases shipped no macOS x64 binary. On our side we kept interpreting the queued status in the GitHub UI through the workflow comment that \"x64 may queue for hours\", never checked the terminal state, and judged it \"non-blocking, nothing to do\" four times. He attached two official references — the GitHub changelog and the actions/runner-images README — his label choice (macos-26-intel) matches the current official x64 label, and he preserved the non-blocking semantics of x64 not being in release needs. The weight here isn't the code volume; it's an external contributor catching a gap in the maintainer's own release pipeline — same tier as lovivi's #204 \"ten-line precise dialect fix\" but ranking above it, because it corrects a fact that had already affected four released versions.","zh-TW":"四行改動,價值全在診斷。GitHub 已於 2025-12-04 下線 macos-13 runner 映像,而 release.yml 的 x64 job 一直寫著 runs-on: macos-13 —— v3.9.1 到 v3.9.4 的 x64 job 終態**全部是 cancelled 而非 queued**,也就是連續四個版本都沒有 macOS x64 二進位發出去。而我們這邊一直把 GitHub UI 上的 queued 按 workflow 註解裡「x64 可能排幾小時」的說法解釋,沒去看終態,四次都判斷成「非阻塞、不用管」。他附了 GitHub changelog 與 actions/runner-images README 兩條官方依據,標籤選擇(macos-26-intel)也與官方當前 x64 標籤一致,並保留了 x64 不進 release needs 的非阻塞語義。這條的分量不在程式碼量,而在於它是外部貢獻者替維護方發現的自身發佈鏈缺口 —— 與 lovivi #204 那種「十行精準方言修復」同檔但更靠上,因為它糾正的是一個已經影響了四個已發佈版本的事實。","ja":"4 行の変更、価値はすべて診断にある。GitHub は 2025-12-04 に macos-13 runner イメージを廃止したのに、release.yml の x64 job はずっと runs-on: macos-13 を書き続けていた —— v3.9.1 から v3.9.4 の x64 job の終状態は**すべて cancelled であって queued ではない**、つまり 4 連続バージョンで macOS x64 バイナリが 1 つもリリースされていない。こちらは GitHub UI の queued 表示を workflow コメントの「x64 は数時間待つことがある」という説明で解釈し続け、終状態を確認せず、4 回とも「非ブロッキング、対処不要」と判断した。彼は GitHub changelog と actions/runner-images README の 2 つの公式根拠を添付し、ラベル選択(macos-26-intel)も公式の現行 x64 ラベルと一致、x64 が release needs に含まれない非ブロッキングの意味論も維持した。この件の重みはコード量ではなく、外部コントリビューターがメンテナー自身のリリースパイプラインの欠陥を見つけたことにある —— lovivi の #204「10 行の精密な方言修正」と同格だが、それより上位だ。なぜなら、すでに 4 つのリリース版に影響した事実を正したから。","ko":"네 줄짜리 변경, 가치는 전부 진단에 있다. GitHub가 2025-12-04에 macos-13 runner 이미지를 폐지했는데 release.yml의 x64 job은 계속 runs-on: macos-13을 쓰고 있었다 — v3.9.1부터 v3.9.4까지 x64 job의 최종 상태가 **전부 cancelled지 queued가 아니다**, 즉 네 버전 연속으로 macOS x64 바이너리가 하나도 릴리스되지 않았다. 우리 쪽은 GitHub UI의 queued 표시를 workflow 주석의 'x64는 몇 시간 대기할 수 있음'이라는 설명으로 해석하고 최종 상태를 확인하지 않아 네 번 모두 '비차단, 조치 불필요'로 판단했다. 그는 GitHub changelog와 actions/runner-images README라는 공식 근거 두 개를 첨부했고, 라벨 선택(macos-26-intel)도 공식의 현재 x64 라벨과 일치하며, x64가 release needs에 포함되지 않는 비차단 의미론도 유지했다. 이 건의 무게는 코드량이 아니라 외부 기여자가 유지보수자 자신의 릴리스 파이프라인 결함을 찾아냈다는 데 있다 — lovivi의 #204 '10줄 정밀 방언 수정'과 동급이지만 더 위다. 이미 릴리스된 네 버전에 영향을 준 사실을 바로잡았기 때문이다.","fr":"Quatre lignes changées, et toute la valeur est dans le diagnostic. GitHub a retiré l'image de runner macos-13 le 2025-12-04, alors que le job x64 de release.yml disait encore runs-on: macos-13 — de v3.9.1 à v3.9.4","de":"Vier Zeilen geändert, und der ganze Wert steckt in der Diagnose. GitHub hat das macos-13-Runner-Image am 2025-12-04 eingestellt, während der x64-Job in release.yml immer noch runs-on: macos-13 sagte — v3.9.1 bis v3.9.4","es":"Cuatro líneas cambiadas, y todo el valor está en el diagnóstico. GitHub retiró la imagen del runner macos-13 el 2025-12-04, mientras el job x64 de release.yml seguía diciendo runs-on: macos-13 — de v3.9.1 a v3.9.4"},"bio_dwgx":{"zh":"项目作者。从 0 到 v2.0.x 一路把 Windsurf 的 Cascade gRPC 反代成 OpenAI / Anthropic / Responses 三协议、零 npm 依赖、双皮 dashboard、多账号池 + tier 管理 + 工具协议翻译 + 路径 sanitize / identity neutralize / cascade reuse / drought mode / 后台改密码 / brute-force lockout / 封号检测 + GHCR 自动 release。所有架构决策、安全审计 follow-up、上游迁移跟进、issue triage、PR review 都在这里。","en":"Project author. Took Windsurf's Cascade gRPC from zero to v2.0.x, reverse-proxying it into the OpenAI / Anthropic / Responses triple protocol — zero npm dependencies, dual-skin dashboard, multi-account pool + tier management + tool protocol translation + path sanitize / identity neutralize / cascade reuse / drought mode / backend password change / brute-force lockout / account ban detection + GHCR auto release. Every architecture decision, security audit follow-up, upstream migration tracking, issue triage, and PR review lives here.","zh-TW":"專案作者。從 0 到 v2.0.x 一路把 Windsurf 的 Cascade gRPC 反代成 OpenAI / Anthropic / Responses 三協定、零 npm 依賴、雙皮 dashboard、多帳號池 + tier 管理 + 工具協定翻譯 + 路徑 sanitize / identity neutralize / cascade reuse / drought mode / 後台改密碼 / brute-force lockout / 封號偵測 + GHCR 自動 release。所有架構決策、安全稽核 follow-up、上游遷移追蹤、issue triage、PR review 都在這裡。","ja":"プロジェクト作者。0 から v2.0.x まで Windsurf の Cascade gRPC を OpenAI / Anthropic / Responses の三プロトコルにリバースプロキシしてきた —— npm 依存ゼロ、デュアルスキンの Dashboard、複数アカウントプール + tier 管理 + ツールプロトコル翻訳 + パス sanitize / identity neutralize / cascade reuse / drought mode / バックエンドでのパスワード変更 / brute-force lockout / アカウント凍結検出 + GHCR 自動リリース。すべてのアーキテクチャ判断、セキュリティ監査の follow-up、上流移行の追跡、issue triage、PR review はここにある。","ko":"프로젝트 작성자. 0부터 v2.0.x까지 Windsurf의 Cascade gRPC를 OpenAI / Anthropic / Responses 3개 프로토콜로 리버스 프록시해왔다 — npm 의존성 제로, 듀얼 스킨 dashboard, 멀티 계정 풀 + tier 관리 + 툴 프로토콜 번역 + 경로 sanitize / identity neutralize / cascade reuse / drought mode / 백엔드 비밀번호 변경 / brute-force lockout / 계정 차단 감지 + GHCR 자동 릴리스. 모든 아키텍처 결정, 보안 감사 follow-up, 업스트림 마이그레이션 추적, issue triage, PR review가 모두 여기에 있다.","fr":"Auteur du projet. De 0 à v2.0.x, transformant le gRPC Cascade de Windsurf en reverse proxy parlant trois protocoles — OpenAI / Anthropic / Responses — avec zéro dépendance npm, un dashboard à double peau, un pool multi-comptes + gestion des tiers","de":"Projektautor. Von 0 bis v2.0.x: Windsurfs Cascade-gRPC in einen Reverse-Proxy mit drei Protokollen verwandelt — OpenAI / Anthropic / Responses — mit null npm-Abhängigkeiten, Dual-Skin-Dashboard, Multi-Account-Pool + Tier-Verwaltung","es":"Autor del proyecto. De 0 a v2.0.x, convirtiendo el gRPC de Cascade de Windsurf en un proxy inverso que habla tres protocolos — OpenAI / Anthropic / Responses — con cero dependencias npm, dashboard de doble skin, pool multi-cuenta + gestión de tiers"},"bio_wangergou777":{"zh":"第二次出手直接到 S 级。setStickyBinding 只在两处 Cascade 成功路径被调用,DEVIN_CONNECT 路径只查不写 —— connect 部署开 STICKY_SESSION_ENABLED=1 完全空转(23 次请求 23 CHECK/23 MISS/0 SET),随后 getApiKey 候选排序以 lastUsed 升序收尾,把同一会话每轮主动派给不同账号。而上游 prompt cache 按账号隔离、写入约为读取 10 倍单价:换号 = 每轮重付整个累积上下文。用付费账号做了三账号交叉实测(tag4 write / tag5 read)钉死机制,修复后同一长对话 write 恒为本轮增量、read 随上下文线性攒到 17 万 token。最见功力的是 modelKey=null 键域细节:connect 取号恒传 null,绑定必须同域写入,否则查询静默不命中退化回空转 —— 他不但踩对了还为这个失败模式加了回归守卫。与 #226 pair-chain 互补成完整闭环:session_id 稳定 + 账号稳定,缓存才真正续得上。合并时 master 侧同步补了 sticky 快路径 excludeKeys、per-user-scope 门禁与 connect-metrics 观测(2f8bce0/f2c938e/3419854)。","en":"Second contribution, straight to S-grade. setStickyBinding is only called on two Cascade success paths; the DEVIN_CONNECT path only reads, never writes — so a connect deployment with STICKY_SESSION_ENABLED=1 spins completely idle (23 requests: 23 CHECK / 23 MISS / 0 SET), and then getApiKey's candidate sort ends with lastUsed ascending, actively handing each round of the same session to a different account. Upstream prompt cache is isolated per account, and writes cost ~10x a read: switching accounts means re-paying the entire accumulated context every round. A paid-account three-account cross-test (tag4 write / tag5 read) pinned the mechanism; after the fix, the same long conversation's write stays at the current round's increment while read linearly grows to 170k tokens of context. The most impressive part is the modelKey=null key-domain detail: connect always passes null when fetching an account, so the binding must be written in the same domain or the query silently misses and degenerates back to idle — he not only got it right but added a regression guard for this failure mode. Together with #226's pair-chain this closes the loop: session_id stable + account stable, and the cache actually keeps continuity. On the merge side, master synchronously added the sticky fast-path excludeKeys, the per-user-scope gate, and connect-metrics observation (2f8bce0/f2c938e/3419854).","zh-TW":"第二次出手直接到 S 級。setStickyBinding 只在兩處 Cascade 成功路徑被呼叫,DEVIN_CONNECT 路徑只查不寫 —— connect 部署開 STICKY_SESSION_ENABLED=1 完全空轉(23 次請求 23 CHECK/23 MISS/0 SET),隨後 getApiKey 候選排序以 lastUsed 升序收尾,把同一會話每輪主動派給不同帳號。而上游 prompt cache 按帳號隔離、寫入約為讀取 10 倍單價:換號 = 每輪重付整個累積上下文。用付費帳號做了三帳號交叉實測(tag4 write / tag5 read)釘死機制,修復後同一長對話 write 恆為本輪增量、read 隨上下文線性攢到 17 萬 token。最見功力的是 modelKey=null 鍵域細節:connect 取號恆傳 null,綁定必須同域寫入,否則查詢靜默不命中退化回空轉 —— 他不但踩對了還為這個失敗模式加了回歸守衛。與 #226 pair-chain 互補成完整閉環:session_id 穩定 + 帳號穩定,快取才真正續得上。合併時 master 側同步補了 sticky 快路徑 excludeKeys、per-user-scope 門禁與 connect-metrics 觀測(2f8bce0/f2c938e/3419854)。","ja":"二度目の参加でいきなり S 級。setStickyBinding は Cascade の成功経路 2 箇所でしか呼ばれず、DEVIN_CONNECT 経路は参照のみで書き込みがない —— connect デプロイで STICKY_SESSION_ENABLED=1 を立てても完全に空回り(23 リクエストで 23 CHECK / 23 MISS / 0 SET)、さらに getApiKey の候補ソートは lastUsed 昇順で締めるため、同じセッションの各ラウンドが積極的に別アカウントに割り振られる。上流の prompt cache はアカウント単位で分離され、書き込みは読み取りの約 10 倍の単価:アカウントを変えるたびに累積コンテキスト全体を毎ラウンド払い直すことになる。有料アカウントで 3 アカウントの交差実測(tag4 write / tag5 read)を行いメカニズムを確定、修正後は同じ長い会話で write が常に今ラウンドの差分増分、read はコンテキストに合わせて線形に積み上がり 17 万トークンに到達。最も冴えているのは modelKey=null のキードメイン詳細:connect は取号時に常に null を渡すため、バインディングも同じドメインで書かないとクエリが静かにミスして空回りに退化する —— 彼は正しく踏んだだけでなく、この失敗モードに回帰ガードまで追加した。#226 の pair-chain と相補して完全なループを形成:session_id 安定 + アカウント安定で、キャッシュが本当に継続する。マージ時に master 側も同期して sticky ファストパスの excludeKeys、per-user-scope ゲート、connect-metrics 観測(2f8bce0/f2c938e/3419854)を追加。","ko":"두 번째 참여로 바로 S급. setStickyBinding은 Cascade 성공 경로 두 곳에서만 호출되고, DEVIN_CONNECT 경로는 조회만 하고 쓰지 않는다 — connect 배포에서 STICKY_SESSION_ENABLED=1을 켜도 완전히 헛돈다(요청 23개에 23 CHECK / 23 MISS / 0 SET), 그런데 getApiKey 후보 정렬이 lastUsed 오름차순으로 끝나면서 같은 세션의 매 라운드를 서로 다른 계정에 능동적으로 배정한다. 업스트림 prompt cache는 계정 단위로 분리되어 있고 쓰기는 읽기의 약 10배 단가다: 계정을 바꾸면 매 라운드 누적 컨텍스트 전체를 다시 지불하는 셈. 유료 계정으로 세 계정 교차 실측(tag4 write / tag5 read)을 해 메커니즘을 못박았고, 수정 후 같은 긴 대화에서 write는 항상 이번 라운드 증분, read는 컨텍스트에 따라 선형으로 쌓여 17만 토큰에 도달한다. 가장 빛나는 부분은 modelKey=null 키 도메인 디테일: connect는 계정을 가져올 때 항상 null을 넘기므로 바인딩도 같은 도메인에 써야 하고, 그렇지 않으면 쿼리가 조용히 미스해 헛돌던 상태로 퇴화한다 — 그는 정확히 밟았을 뿐 아니라 이 실패 모드에 회귀 가드까지 추가했다. #226의 pair-chain과 상호 보완해 완전한 루프를 이룬다: session_id 안정 + 계정 안정이 돼야 캐시가 진짜로 이어진다. 병합 시 master 쪽도 동기로 sticky 패스트패스 excludeKeys, per-user-scope 게이트, connect-metrics 관측(2f8bce0/f2c938e/3419854)을 추가했다.","fr":"Deuxième PR, direct S-grade. setStickyBinding n'est appelé que sur deux chemins de succès Cascade ; le chemin DEVIN_CONNECT lit mais n'écrit jamais — donc les déploiements connect avec STICKY_SESSION_ENABLED=1 tournaient complètement à vide (23 ","de":"Zweiter PR, direkt S-Grade. setStickyBinding wird nur auf zwei Cascade-Erfolgspfaden aufgerufen; der DEVIN_CONNECT-Pfad liest nur, schreibt aber nie — Connect-Deployments mit STICKY_SESSION_ENABLED=1 liefen also völlig leer (23 ","es":"Segundo PR, directo a S-grade. setStickyBinding solo se invoca en dos rutas de éxito de Cascade; la ruta DEVIN_CONNECT lee pero nunca escribe — así que los despliegues connect con STICKY_SESSION_ENABLED=1 corrieron totalmente en vacío (23 "},"bio_abwuge":{"zh":"新人首次贡献就解了部署死锁。docker-compose 起来后所有 windsurf 容器(windsurf-lb + 三个 API 副本)持续 Restart:nginx upstream 用了 `server windsurf-api:3003 resolve` 动态解析但没配 `zone`,nginx 直接拒绝启动;Node 这边 src/config.js:38 实际调用 `join()` 但顶部 import 漏写,replica-data-dir 路径 hit 时 ReferenceError 进程崩。两处单行修,docker compose 立刻能起。PR 描述里那句\"~~这么简单的问题,难道没人用 docker 吗?~~\" 本人勿删。","en":"A first-time contributor who solved a deployment deadlock. After docker-compose up, every windsurf container (windsurf-lb + three API replicas) keeps Restarting: the nginx upstream uses `server windsurf-api:3003 resolve` for dynamic resolution but no `zone` was configured, so nginx refuses to start; on the Node side, src/config.js:38 actually calls `join()` but the import at the top is missing, so the replica-data-dir path crashes the process with a ReferenceError. Two one-line fixes and docker compose comes up immediately. Don't delete that PR description line: \"~~A problem this simple — does nobody actually use docker?~~\"","zh-TW":"新人首次貢獻就解了部署死鎖。docker-compose 起來後所有 windsurf 容器(windsurf-lb + 三個 API 副本)持續 Restart:nginx upstream 用了 `server windsurf-api:3003 resolve` 動態解析但沒配 `zone`,nginx 直接拒絕啟動;Node 這邊 src/config.js:38 實際呼叫 `join()` 但頂部 import 漏寫,replica-data-dir 路徑 hit 時 ReferenceError 程序崩潰。兩處單行修,docker compose 立刻能起。PR 描述裡那句「~~這麼簡單的問題,難道沒人用 docker 嗎?~~」本人勿刪。","ja":"新人の初コントリビューションでデプロイのデッドロックを解決。docker-compose 起動後、すべての windsurf コンテナ(windsurf-lb + API レプリカ 3 基)が Restart を繰り返す:nginx upstream が `server windsurf-api:3003 resolve` の動的解決を使っているのに `zone` が設定されておらず、nginx が起動を拒否。Node 側は src/config.js:38 が実際には `join()` を呼んでいるのに先頭の import が漏れており、replica-data-dir パスに到達すると ReferenceError でプロセスがクラッシュ。2 箇所の 1 行修正で docker compose がすぐ起動するようになった。PR 説明の「~~这么简单的问题,难道没人用 docker 吗?~~」という一文は削らないこと。","ko":"신인이 첫 기여로 배포 데드락을 풀었다. docker-compose를 올리면 모든 windsurf 컨테이너(windsurf-lb + API 레플리카 3개)가 계속 Restart한다: nginx upstream이 `server windsurf-api:3003 resolve` 동적 해석을 쓰는데 `zone`이 설정돼 있지 않아 nginx가 시작을 거부한다. Node 쪽은 src/config.js:38이 실제로 `join()`을 호출하는데 상단 import가 빠져 있어 replica-data-dir 경로에 닿으면 ReferenceError로 프로세스가 죽는다. 한 줄짜리 수정 두 곳으로 docker compose가 바로 뜬다. PR 설명의 「~~这么简单的问题,难道没人用 docker 吗?~~」 문장은 지우지 말 것.","fr":"Un contributeur débutant a débloqué un interblocage de déploiement. Après le docker-compose, tous les conteneurs windsurf (windsurf-lb + trois réplicas API) continuaient de redémarrer : l'upstream nginx utilisait `server windsurf-api","de":"Ein Neuling hat einen Deployment-Deadlock geknackt. Nach dem docker-compose starteten alle windsurf-Container (windsurf-lb + drei API-Replicas) immer wieder neu: der nginx-upstream nutzte `server windsurf-api","es":"Un contribuyente novato rompió un deadlock de despliegue. Tras levantar docker-compose, todos los contenedores de windsurf (windsurf-lb + tres réplicas de API) no paraban de reiniciarse: el upstream de nginx usaba `server windsurf-api"},"bio_The-five-stooges":{"zh":"修复 sticky session 在流式路径的两个关键问题:① waitForAccount 在流式 retry 路径缺少 callerKey 参数,导致 sticky 绑定在流式请求中不生效;② 新增 body.user 多用户隔离机制,允许不同用户在共享 API key 下保持独立的 session 绑定。配套添加了 stickyNoFallback 开关(绑定账号失败时不回退到其他账号)、stickyBindByUserOnly 开关(忽略模型维度按 user 绑定)、以及 Dashboard 配置面板。320 行新增,涉及 sticky-session / auth / chat / messages / runtime-config / dashboard 六个子系统。","en":"Fixes two key sticky session problems in the streaming path: ① waitForAccount was missing the callerKey parameter in the streaming retry path, so sticky binding never took effect on streaming requests; ② a new body.user multi-user isolation mechanism lets different users hold independent session bindings under a shared API key. Also ships the stickyNoFallback switch (no fallback to other accounts when binding fails), the stickyBindByUserOnly switch (bind by user, ignoring the model dimension), and a Dashboard config panel. 320 lines added across six subsystems: sticky-session / auth / chat / messages / runtime-config / dashboard.","zh-TW":"修復 sticky session 在串流路徑的兩個關鍵問題:① waitForAccount 在串流 retry 路徑缺少 callerKey 參數,導致 sticky 綁定在串流請求中不生效;② 新增 body.user 多使用者隔離機制,允許不同使用者在共享 API key 下保持獨立的 session 綁定。配套新增了 stickyNoFallback 開關(綁定帳號失敗時不回退到其他帳號)、stickyBindByUserOnly 開關(忽略模型維度按 user 綁定)、以及 Dashboard 設定面板。320 行新增,涉及 sticky-session / auth / chat / messages / runtime-config / dashboard 六個子系統。","ja":"ストリーミング経路での sticky session の 2 つの重要問題を修正:① waitForAccount がストリーミング retry 経路で callerKey パラメータを欠いており、sticky バインディングがストリーミングリクエストで効いていなかった;② 新規の body.user マルチユーザー分離機構で、共有 API key の下で異なるユーザーが独立したセッションバインディングを維持できる。併せて stickyNoFallback スイッチ(バインディング失敗時に他アカウントへフォールバックしない)、stickyBindByUserOnly スイッチ(モデル次元を無視して user でバインディング)、Dashboard 設定パネルも追加。追加 320 行、sticky-session / auth / chat / messages / runtime-config / dashboard の 6 サブシステムにまたがる。","ko":"스트리밍 경로의 sticky session 핵심 문제 두 개를 수정: ① waitForAccount가 스트리밍 retry 경로에서 callerKey 파라미터를 빠뜨려 sticky 바인딩이 스트리밍 요청에서 작동하지 않았다. ② 새 body.user 멀티유저 격리 메커니즘으로 공유 API key 아래에서도 서로 다른 사용자가 독립적인 세션 바인딩을 유지할 수 있다. 함께 stickyNoFallback 스위치(바인딩 실패 시 다른 계정으로 폴백하지 않음), stickyBindByUserOnly 스위치(모델 차원을 무시하고 user 기준 바인딩), Dashboard 설정 패널도 추가. 추가 320줄, sticky-session / auth / chat / messages / runtime-config / dashboard 여섯 서브시스템에 걸침.","fr":"Corrigé deux problèmes critiques de sticky session sur le chemin streaming : ① le chemin retry streaming de waitForAccount ne recevait pas le paramètre callerKey, donc les bindings sticky ne prenaient jamais effet sur les requêtes streaming ; ② ajout d'un mécanisme d'isolation multi-utilisateurs body.user","de":"Zwei kritische sticky-Session-Probleme im Streaming-Pfad gefixt: ① der Streaming-Retry-Pfad von waitForAccount ließ den callerKey-Parameter weg, daher griffen sticky-Bindings bei Streaming-Requests nie; ② neue body.user-Multi-User-Isolation","es":"Arreglados dos problemas críticos de sticky session en la ruta de streaming: ① la ruta de retry en streaming de waitForAccount no recibía el parámetro callerKey, así que los bindings sticky nunca surtían efecto en requests de streaming; ② nuevo mecanismo de aislamiento multi-usuario body.user"},"bio_lovivi":{"zh":"首次贡献就解了 glm-5.2 的工具调用哑火。glm 系列默认走 glm47 那套 XML tool-call 标记,但 glm-5.2 直接无视标记、用纯文本作答,工具调用根本 emit 不出来。lovivi 在 pickToolDialect 里给 glm-5.2 和它的 glm-5-2-* SKU 别名单独切到 gpt_native 的 function_call JSON dialect —— 这套是模型稳定 emit、parser 也能正确解析的;老 GLM 维持 glm47 不动,没经验证不乱动。顺手加了 WINDSURFAPI_FORCE_TOOL_DIALECT 环境变量(限定 glm47/openai_json_xml/kimi_k2/gpt_native 四值)方便测试时强制方言。10 行精准改动,是新模型上线后\"工具协议方言对不上\"这类长尾问题的标准排查范式。","en":"First contribution, solving glm-5.2's tool call silence. The glm family defaults to glm47's XML tool-call markup, but glm-5.2 ignores the markup entirely and answers in plain text, so tool calls never emit. lovivi switched glm-5.2 and its glm-5-2-* SKU aliases to the gpt_native function_call JSON dialect in pickToolDialect — one the model emits stably and the parser parses correctly; old GLM keeps glm47 untouched, nothing changed without verification. Also added the WINDSURFAPI_FORCE_TOOL_DIALECT env var (limited to the four values glm47/openai_json_xml/kimi_k2/gpt_native) for forcing dialects in tests. 10 precise lines — the standard troubleshooting pattern for the \"tool protocol dialect mismatch\" long-tail after a new model goes live.","zh-TW":"首次貢獻就解了 glm-5.2 的工具呼叫啞火。glm 系列預設走 glm47 那套 XML tool-call 標記,但 glm-5.2 直接無視標記、用純文字作答,工具呼叫根本 emit 不出來。lovivi 在 pickToolDialect 裡給 glm-5.2 和它的 glm-5-2-* SKU 別名單獨切到 gpt_native 的 function_call JSON dialect —— 這套是模型穩定 emit、parser 也能正確解析的;舊 GLM 維持 glm47 不動,沒經驗證不亂動。順手加了 WINDSURFAPI_FORCE_TOOL_DIALECT 環境變數(限定 glm47/openai_json_xml/kimi_k2/gpt_native 四值)方便測試時強制方言。10 行精準改動,是新模型上線後「工具協定方言對不上」這類長尾問題的標準排查範式。","ja":"初コントリビューションで glm-5.2 のツール呼び出し不発を解決。glm 系はデフォルトで glm47 の XML tool-call マークアップを使うが、glm-5.2 はマークアップを完全に無視してプレーンテキストで答えるため、ツール呼び出しがまったく emit されない。lovivi は pickToolDialect で glm-5.2 とその glm-5-2-* SKU エイリアスを個別に gpt_native の function_call JSON dialect へ切り替えた —— これはモデルが安定的に emit し、parser も正しく解析できる方式。旧 GLM は glm47 のまま動かさず、未検証のまま触らない。ついでに WINDSURFAPI_FORCE_TOOL_DIALECT 環境変数(glm47/openai_json_xml/kimi_k2/gpt_native の 4 値に限定)を追加し、テスト時に方言を強制できるようにした。10 行の精密な変更で、新モデル投入後の「ツールプロトコル方言の不一致」というロングテール問題の標準的な調査パラダイム。","ko":"첫 기여로 glm-5.2의 툴 호출 불발을 해결했다. glm 계열은 기본적으로 glm47의 XML tool-call 마크업을 쓰지만, glm-5.2는 마크업을 완전히 무시하고 평문으로 답해 툴 호출이 전혀 emit되지 않는다. lovivi는 pickToolDialect에서 glm-5.2와 그 glm-5-2-* SKU 별칭만 따로 gpt_native의 function_call JSON dialect로 전환했다 — 모델이 안정적으로 emit하고 parser도 올바르게 파싱하는 방식. 기존 GLM은 glm47을 그대로 두고, 검증 없이 건드리지 않는다. 덤으로 WINDSURFAPI_FORCE_TOOL_DIALECT 환경변수(glm47/openai_json_xml/kimi_k2/gpt_native 네 값으로 한정)를 추가해 테스트 때 방언을 강제할 수 있게 했다. 10줄의 정밀한 변경이며, 신모델 출시 후 '툴 프로토콜 방언 불일치' 같은 롱테일 문제의 표준 탐사 패러다임이다.","fr":"Première contribution, et elle a réparé les tool calls muets de glm-5.2. La famille glm utilise par défaut les marqueurs XML tool-call de glm47, mais glm-5.2 les ignore totalement et répond en texte brut — les tool calls ne s'émettent jamais. lovivi dans pickToolDia","de":"Erster Beitrag, und er fixt die stummen Tool-Calls von glm-5.2. Die glm-Familie nutzt standardmäßig die XML-tool-call-Marker von glm47, aber glm-5.2 ignoriert sie völlig und antwortet in Klartext — Tool-Calls werden schlicht nie emittiert. lovivi in pickToolDia","es":"Primera contribución, y arregló los tool calls mudos de glm-5.2. La familia glm usa por defecto los marcadores XML tool-call de glm47, pero glm-5.2 los ignora por completo y responde en texto plano — los tool calls nunca se emiten. lovivi en pickToolDia"},"bio_brandonedley":{"zh":"PR #201 把 GLM 5.2 和 Kimi K2.7 补进模型 catalog,同时同步 README、英文 README、package description、模型文档生成检查和 catalog correctness 测试。模型新增最怕只改一处导致 dashboard、docs、测试互相漂移,这个 PR 把代码、文档和校验一起补齐,是后续普通模型 catalog 更新可以参考的提交形态。","en":"PR #201 adds GLM 5.2 and Kimi K2.7 to the model catalog while syncing the README, English README, package description, model doc generation check, and catalog correctness tests. Model additions are most dangerous when they touch only one place and leave dashboard, docs, and tests drifting apart — this PR completes code, docs, and verification together, and is the commit shape future ordinary model catalog updates should follow.","zh-TW":"PR #201 把 GLM 5.2 和 Kimi K2.7 補進模型 catalog,同時同步 README、英文 README、package description、模型文件生成檢查和 catalog correctness 測試。模型新增最怕只改一處導致 dashboard、docs、測試互相漂移,這個 PR 把程式碼、文件和校驗一起補齊,是後續普通模型 catalog 更新可以參考的提交形態。","ja":"PR #201 は GLM 5.2 と Kimi K2.7 をモデルカタログに追加し、README・英語 README・package description・モデルドキュメント生成チェック・catalog correctness テストも同期。モデル追加で一番怖いのは 1 箇所しか直さず dashboard と docs とテストが互いに乖離すること —— この PR はコード・ドキュメント・検証をまとめて揃えており、今後の通常のモデルカタログ更新が参考にすべきコミット形態。","ko":"PR #201은 GLM 5.2와 Kimi K2.7을 모델 카탈로그에 추가하면서 README, 영문 README, package description, 모델 문서 생성 검사, 카탈로그 정확성 테스트까지 동기화했다. 모델 추가는 한 곳만 고쳐 dashboard, docs, 테스트가 서로 어긋나는 게 가장 위험하다 — 이 PR은 코드, 문서, 검증을 한 번에 채웠으며, 앞으로의 일반적인 모델 카탈로그 업데이트가 참고할 커밋 형태다.","fr":"PR #201 a ajouté GLM 5.2 et Kimi K2.7 au catalogue de modèles, en synchronisant au passage le README, le README anglais, la description du package, la vérification de génération de doc modèles et les tests de correctesse du catalogue. Le modèle","de":"PR #201 fügte GLM 5.2 und Kimi K2.7 zum Modellkatalog hinzu und synchronisierte dabei README, englisches README, Package-Description, Model-Doc-Generierungscheck und Katalog-Korrektheitstests. Das Modell","es":"El PR #201 añadió GLM 5.2 y Kimi K2.7 al catálogo de modelos, sincronizando de paso el README, el README en inglés, la descripción del package, el check de generación de documentación de modelos y los tests de correctitud del catálogo. El modelo"},"bio_MatrixNeoKozak":{"zh":"PR #195 修掉 dashboard API malformed JSON 仍按 200 返回的问题。改动很小,但错误语义重要:前端、脚本和自动化调用方现在能用 HTTP status 正确区分请求体格式错误,而不是从一个成功响应里猜 `ok:false`。配套跑过 dashboard API 和 cache-control 测试,适合作为小型、聚焦、可验证 bugfix 的样板。","en":"PR #195 fixes the dashboard API returning 200 for malformed JSON. Small change, but the error semantics matter: frontend, scripts, and automation callers can now distinguish a malformed request body via the HTTP status instead of guessing `ok:false` from a success response. The dashboard API and cache-control tests were run alongside — a good template for a small, focused, verifiable bugfix.","zh-TW":"PR #195 修掉 dashboard API malformed JSON 仍按 200 返回的問題。改動很小,但錯誤語義重要:前端、腳本和自動化呼叫方現在能用 HTTP status 正確區分請求體格式錯誤,而不是從一個成功響應裡猜 `ok:false`。配套跑過 dashboard API 和 cache-control 測試,適合作為小型、聚焦、可驗證 bugfix 的樣板。","ja":"PR #195 は dashboard API が malformed JSON にも 200 を返していた問題を修正。変更は小さいがエラー意味論は重要:フロントエンド / スクリプト / 自動化コール側が、成功レスポンスから `ok:false` を推測する代わりに HTTP status でリクエストボディの形式エラーを正しく区別できるようになった。dashboard API と cache-control のテストも併せて実行済みで、小規模・焦点が絞られた・検証可能な bugfix の手本。","ko":"PR #195는 dashboard API가 malformed JSON에도 200을 반환하던 문제를 수정했다. 변경은 작지만 오류 의미론이 중요하다: 프론트엔드, 스크립트, 자동화 호출자가 성공 응답에서 `ok:false`를 추측하는 대신 HTTP status로 요청 본문 형식 오류를 제대로 구분할 수 있게 됐다. dashboard API와 cache-control 테스트도 함께 돌렸고, 작고 집중적이며 검증 가능한 버그픽스의 본보기로 적합하다.","fr":"PR #195 a corrigé l'API dashboard qui renvoyait 200 pour du JSON malformé. Un petit changement aux sémantiques d'erreur importantes : les frontends, scripts et appelants automatisés peuvent désormais distinguer un corps malformé via le statut HTTP au lieu de deviner à partir d'une réponse 200 ","de":"PR #195 fixt, dass die Dashboard-API für malformed JSON immer noch 200 zurückgab. Eine winzige Änderung mit wichtiger Fehlersemantik: Frontends, Skripte und automatisierte Aufrufer können malformed Bodies jetzt über den HTTP-Status unterscheiden, statt aus einer 200-Antwort zu raten ","es":"El PR #195 arregló que la API del dashboard devolviera 200 para JSON malformado. Un cambio diminuto con semántica de error importante: los frontends, scripts y llamadores automatizados ahora pueden distinguir cuerpos malformados por el status HTTP en lugar de adivinar desde una respuesta 200 "},"bio_ucloudnb666":{"zh":"让 WindsurfAPI 支持 Astraflow 作为第三方提供商路由。config.js 新增 ASTRAFLOW_API_KEY 配置项,models.js 注册 astraflow/* 模型前缀,30 行精准修改。","en":"Makes WindsurfAPI route Astraflow as a third-party provider. config.js gains an ASTRAFLOW_API_KEY config item, models.js registers the astraflow/* model prefix — 30 precise lines.","zh-TW":"讓 WindsurfAPI 支援 Astraflow 作為第三方提供者路由。config.js 新增 ASTRAFLOW_API_KEY 設定項,models.js 註冊 astraflow/* 模型前綴,30 行精準修改。","ja":"WindsurfAPI がサードパーティプロバイダーとして Astraflow をルーティングできるように。config.js に ASTRAFLOW_API_KEY 設定項目を追加、models.js に astraflow/* モデルプレフィックスを登録、30 行の精密な修正。","ko":"WindsurfAPI가 Astraflow를 서드파티 제공자 라우트로 지원하게 만든다. config.js에 ASTRAFLOW_API_KEY 설정 항목 추가, models.js에 astraflow/* 모델 프리픽스 등록, 정밀한 30줄 수정.","fr":"Ajoute Astraflow comme route de fournisseur tiers pour WindsurfAPI. config.js gagne l'option ASTRAFLOW_API_KEY, models.js enregistre le préfixe de modèle astraflow/* — un changement précis de 30 lignes.","de":"Macht Astraflow als Drittanbieter-Route für WindsurfAPI verfügbar. config.js bekommt die ASTRAFLOW_API_KEY-Option, models.js registriert das Modellpräfix astraflow/* — eine präzise 30-Zeilen-Änderung.","es":"Añade Astraflow como ruta de proveedor de terceros para WindsurfAPI. config.js gana la opción ASTRAFLOW_API_KEY, models.js registra el prefijo de modelo astraflow/* — un cambio preciso de 30 líneas."},"bio_lauvww":{"zh":"重写了 Dashboard 的账号批量导入解析器 (import-parser.js):支持 JSON / CSV / 纯文本多种格式混合粘贴,自动检测分隔符,容错处理格式不一致的行。588 行新增含完整测试覆盖和中英双语 i18n。","en":"Rewrote the Dashboard's account batch import parser (import-parser.js): supports mixed JSON / CSV / plain text formats pasted together, auto-detects the delimiter, and tolerates inconsistent rows. 588 lines added with full test coverage and bilingual zh/en i18n.","zh-TW":"重寫了 Dashboard 的帳號批次匯入解析器 (import-parser.js):支援 JSON / CSV / 純文字多種格式混合貼上,自動偵測分隔符,容錯處理格式不一致的行。588 行新增含完整測試覆蓋和中英雙語 i18n。","ja":"Dashboard のアカウント一括インポートパーサー(import-parser.js)を書き直し:JSON / CSV / プレーンテキストの混在形式のまとめ貼りに対応し、区切り文字を自動検出、フォーマット不一致の行にも寛容。追加 588 行、完全なテストカバレッジと中英バイリンガル i18n 付き。","ko":"Dashboard의 계정 일괄 가져오기 파서(import-parser.js)를 재작성: JSON / CSV / 평문 등 여러 형식이 섞인 붙여넣기를 지원하고, 구분자를 자동 감지하며, 형식이 안 맞는 줄도 관대하게 처리. 추가 588줄, 전체 테스트 커버리지와 중영 이중 언어 i18n 포함.","fr":"Réécrit le parseur d'import de comptes en masse du Dashboard (import-parser.js) : prend en charge les collages mixtes JSON / CSV / texte brut, détecte automatiquement les délimiteurs et tolère les lignes incohérentes. 588 lignes ajoutées avec couverture de tests complète et i18n bilingue zh/en.","de":"Den Batch-Account-Import-Parser des Dashboards (import-parser.js) neu geschrieben: unterstützt gemischte JSON-/CSV-/Klartext-Pastes, erkennt Trennzeichen automatisch und toleriert inkonsistente Zeilen. 588 neue Zeilen mit vollständiger Testabdeckung und zweisprachiger zh/en-i18n.","es":"Reescribió el parser de importación masiva de cuentas del Dashboard (import-parser.js): soporta pastes mixtos de JSON / CSV / texto plano, detecta delimitadores automáticamente y tolera líneas inconsistentes. 588 líneas añadidas con cobertura completa de tests e i18n bilingüe zh/en."},"bio_Fermiz":{"zh":"三件事一个 PR:① 单用户场景下 Cascade 复用优化 — 跳过多余的账号轮询直接命中唯一账号;② HTTPS 代理中间层 (https-proxy.js) 让反代本身也能跑 TLS;③ conversation-pool 大小可配置化。323 行新增,改动集中在 chat.js 和 conversation-pool.js 核心路径。","en":"Three things in one PR: ① Cascade reuse optimization for single-user setups — skip redundant account polling and hit the sole account directly; ② an HTTPS proxy middle layer (https-proxy.js) so the reverse proxy itself can run TLS; ③ configurable conversation-pool size. 323 lines added, concentrated in the core chat.js and conversation-pool.js paths.","zh-TW":"三件事一個 PR:① 單一使用者場景下 Cascade 重用最佳化 — 跳過多餘的帳號輪詢直接命中唯一帳號;② HTTPS 代理中間層 (https-proxy.js) 讓反代本身也能跑 TLS;③ conversation-pool 大小可設定化。323 行新增,改動集中在 chat.js 和 conversation-pool.js 核心路徑。","ja":"3 件を 1 つの PR に:① 単一ユーザーシナリオの Cascade 再利用最適化 —— 冗長なアカウントポーリングをスキップして唯一のアカウントに直接ヒット;② HTTPS プロキシ中間層(https-proxy.js)でリバースプロキシ自体も TLS 対応;③ conversation-pool サイズの設定化。追加 323 行、変更は chat.js と conversation-pool.js のコア経路に集中。","ko":"세 가지를 하나의 PR로: ① 단일 사용자 시나리오의 Cascade 재사용 최적화 — 불필요한 계정 폴링을 건너뛰고 유일한 계정에 직접 히트. ② HTTPS 프록시 중간 계층(https-proxy.js)으로 리버스 프록시 자체도 TLS 실행 가능. ③ conversation-pool 크기 설정 가능. 추가 323줄, 변경은 chat.js와 conversation-pool.js 코어 경로에 집중.","fr":"Trois choses en une PR : ① optimisation de la réutilisation Cascade pour les configurations mono-utilisateur — sauter le polling redondant des comptes pour viser directement le seul compte ; ② une couche proxy HTTPS (https-proxy.js) pour que le reverse proxy puisse lui-même servir du TLS ; ③ taille de conversation-pool configurable","de":"Drei Dinge in einem PR: ① Cascade-Wiederverwendungsoptimierung für Single-User-Setups — redundantes Account-Polling überspringen und das eine Konto direkt treffen; ② eine HTTPS-Proxy-Schicht (https-proxy.js), damit der Reverse-Proxy selbst TLS servieren kann; ③ konfigurierbare conversation-pool-Größe","es":"Tres cosas en una PR: ① optimización de reutilización de Cascade para setups de un solo usuario — saltar el polling redundante de cuentas y dar directo en la única cuenta; ② una capa de proxy HTTPS (https-proxy.js) para que el propio proxy inverso pueda servir TLS; ③ tamaño configurable de conversation-pool"},"bio_linqichenggg":{"zh":"Windows / macOS / Linux 三平台的 LS 二进制路径、数据目录、安装脚本全部对齐。之前 macOS 用 ~/Library 路径但 install-ls.sh 写的是 Linux 路径,Windows 更是直接找不到 LS。统一到 config.js 集中管理 + platform-ls-paths.test.js 覆盖三平台断言。","en":"LS binary paths, data directories, and install scripts aligned across Windows / macOS / Linux. Previously macOS used a ~/Library path while install-ls.sh wrote Linux paths, and Windows couldn't even find the LS. Everything is now centralized in config.js, with platform-ls-paths.test.js covering assertions for all three platforms.","zh-TW":"Windows / macOS / Linux 三平台的 LS 二進位路徑、資料目錄、安裝腳本全部對齊。之前 macOS 用 ~/Library 路徑但 install-ls.sh 寫的是 Linux 路徑,Windows 更是直接找不到 LS。統一到 config.js 集中管理 + platform-ls-paths.test.js 覆蓋三平台斷言。","ja":"Windows / macOS / Linux の 3 プラットフォームで LS バイナリのパス、データディレクトリ、インストールスクリプトをすべて統一。以前は macOS が ~/Library パスを使う一方 install-ls.sh は Linux パスを書き、Windows は LS 自体を見つけられなかった。config.js に一元管理し、platform-ls-paths.test.js で 3 プラットフォームのアサーションをカバー。","ko":"Windows / macOS / Linux 세 플랫폼의 LS 바이너리 경로, 데이터 디렉터리, 설치 스크립트를 모두 정렬했다. 기존에는 macOS가 ~/Library 경로를 쓰는데 install-ls.sh는 Linux 경로를 썼고, Windows는 LS를 아예 찾지 못했다. config.js에 일원화하고 platform-ls-paths.test.js로 세 플랫폼 단언을 커버.","fr":"Aligné les chemins de binaires LS, les répertoires de données et les scripts d'installation sur Windows / macOS / Linux. macOS utilisait des chemins ~/Library alors que install-ls.sh écrivait des chemins Linux, et Windows ne pouvait même pas trouver ","de":"LS-Binärpfade, Datenverzeichnisse und Installationsskripte über Windows / macOS / Linux angeglichen. macOS nutzte ~/Library-Pfade, während install-ls.sh Linux-Pfade schrieb, und Windows konnte sie nicht einmal ","es":"Alineadas las rutas de binarios LS, los directorios de datos y los scripts de instalación en Windows / macOS / Linux. macOS usaba rutas ~/Library mientras install-ls.sh escribía rutas de Linux, y Windows ni siquiera podía encontrar "},"bio_datfooldive":{"zh":"Dashboard 前端大重构:1177 行新增 / 465 行删除,全部集中在 index.html。清理了冗余样式、统一了组件风格、优化了卡片布局和响应式适配。","en":"Big Dashboard frontend refactor: 1177 lines added / 465 deleted, all inside index.html. Cleaned up redundant styles, unified component styling, optimized card layout and responsive adaptation.","zh-TW":"Dashboard 前端大重構:1177 行新增 / 465 行刪除,全部集中在 index.html。清理了冗餘樣式、統一了元件風格、最佳化了卡片佈局和響應式適配。","ja":"Dashboard フロントエンドの大規模リファクタリング:追加 1177 行 / 削除 465 行、すべて index.html に集中。冗長なスタイルを整理し、コンポーネントスタイルを統一、カードレイアウトとレスポンシブ対応を最適化。","ko":"Dashboard 프론트엔드 대규모 리팩터링: 추가 1177줄 / 삭제 465줄, 전부 index.html에 집중. 중복 스타일 정리, 컴포넌트 스타일 통일, 카드 레이아웃과 반응형 대응 최적화.","fr":"Grosse refonte du frontend Dashboard : 1177 lignes ajoutées / 465 supprimées, tout dans index.html. Styles redondants nettoyés, styles de composants unifiés, mise en page des cartes et adaptation responsive polies.","de":"Großes Dashboard-Frontend-Refactoring: 1177 Zeilen hinzugefügt / 465 gelöscht, alles in index.html. Redundante Styles entfernt, Komponenten-Stile vereinheitlicht, Kartenlayout und responsives Verhalten poliert.","es":"Gran refactor del frontend del Dashboard: 1177 líneas añadidas / 465 eliminadas, todo en index.html. Estilos redundantes limpiados, estilos de componentes unificados, layout de tarjetas y adaptación responsive pulidos."},"bio_youfak":{"zh":"Dockerfile / docker-compose.yml / .dockerignore / .gitattributes 一整套,配 DATA_DIR 配置项把 accounts.json / stats.json / logs/ 等持久化文件统一收敛到可挂载的数据目录。附带修两个 LS 稳定性问题:Windows 宿主机构建时 install-ls.sh 的 CRLF 会导致容器内 set: pipefail 报错;Dashboard 点重启 LS 时若启动失败会把整个 Node 进程打崩。","en":"A complete Dockerfile / docker-compose.yml / .dockerignore / .gitattributes set, with a DATA_DIR config item that converges persistent files like accounts.json / stats.json / logs/ into a mountable data directory. Also fixes two LS stability issues: CRLF in install-ls.sh breaks `set: pipefail` inside the container when building on a Windows host; and clicking restart-LS in the Dashboard could crash the entire Node process when startup fails.","zh-TW":"Dockerfile / docker-compose.yml / .dockerignore / .gitattributes 一整組,配 DATA_DIR 設定項把 accounts.json / stats.json / logs/ 等持久化檔案統一收斂到可掛載的資料目錄。附帶修兩個 LS 穩定性問題:Windows 宿主機構建時 install-ls.sh 的 CRLF 會導致容器內 set: pipefail 報錯;Dashboard 點重啟 LS 時若啟動失敗會把整個 Node 程序打崩。","ja":"Dockerfile / docker-compose.yml / .dockerignore / .gitattributes 一式に、DATA_DIR 設定項目を追加し、accounts.json / stats.json / logs/ などの永続ファイルをマウント可能なデータディレクトリに一元化。さらに LS の安定性問題を 2 つ修正:Windows ホストでのビルド時、install-ls.sh の CRLF がコンテナ内の `set: pipefail` エラーを引き起こす問題と、Dashboard から LS を再起動した際に起動失敗が Node プロセス全体を落とす問題。","ko":"Dockerfile / docker-compose.yml / .dockerignore / .gitattributes 한 벌에 DATA_DIR 설정 항목을 더해 accounts.json / stats.json / logs/ 같은 영속 파일을 마운트 가능한 데이터 디렉터리로 모았다. LS 안정성 문제 두 개도 수정: Windows 호스트에서 빌드할 때 install-ls.sh의 CRLF가 컨테이너 안 `set: pipefail` 오류를 일으키는 문제와, Dashboard에서 LS 재시작 시 시작 실패가 Node 프로세스 전체를 죽이는 문제.","fr":"Un ensemble complet Dockerfile / docker-compose.yml / .dockerignore / .gitattributes, avec une option DATA_DIR qui route accounts.json / stats.json / logs/ ","de":"Ein komplettes Dockerfile- / docker-compose.yml- / .dockerignore- / .gitattributes-Paket, mit einer DATA_DIR-Option, die accounts.json / stats.json / logs/ ","es":"Un conjunto completo de Dockerfile / docker-compose.yml / .dockerignore / .gitattributes, con una opción DATA_DIR que dirige accounts.json / stats.json / logs/ "},"bio_motto1":{"zh":"官网可以登但 Dashboard 登录失败 —— 原因是旧 Firebase 路径已不是主链路。逆向补出 /_devin-auth/connections → /_devin-auth/password/login → WindsurfPostAuth → GetOneTimeAuthToken 四步流程,保留旧 Firebase 作为兜底,再顺手加了邮箱/密码批量导入 + 剪贴板读取。","en":"The official site logs in fine but Dashboard login fails — because the old Firebase path is no longer the main chain. Reverse-engineered the four-step flow /_devin-auth/connections → /_devin-auth/password/login → WindsurfPostAuth → GetOneTimeAuthToken, kept the old Firebase as fallback, and threw in email/password batch import + clipboard reading.","zh-TW":"官網可以登但 Dashboard 登入失敗 —— 原因是舊 Firebase 路徑已不是主鏈路。逆向補出 /_devin-auth/connections → /_devin-auth/password/login → WindsurfPostAuth → GetOneTimeAuthToken 四步流程,保留舊 Firebase 作為兜底,再順手加了信箱/密碼批次匯入 + 剪貼簿讀取。","ja":"公式サイトはログインできるのに Dashboard のログインだけ失敗する —— 原因は旧 Firebase 経路がもはや主経路ではないこと。逆向き解析で /_devin-auth/connections → /_devin-auth/password/login → WindsurfPostAuth → GetOneTimeAuthToken の 4 段階フローを補完し、旧 Firebase はフォールバックとして温存。さらにメール/パスワードの一括インポートとクリップボード読み取りも追加。","ko":"공식 사이트는 로그인이 되는데 Dashboard 로그인만 실패한다 — 이유는 기존 Firebase 경로가 더는 메인 체인이 아니기 때문. 역분석으로 /_devin-auth/connections → /_devin-auth/password/login → WindsurfPostAuth → GetOneTimeAuthToken 4단계 흐름을 보강하고, 기존 Firebase는 폴백으로 유지한 뒤 이메일/비밀번호 일괄 가져오기 + 클립보드 읽기도 덧붙였다.","fr":"Le site officiel se connecte mais pas le Dashboard — l'ancien chemin Firebase n'est plus la route principale. Chaîne rétro-ingéniée /_devin-auth/connections → /_devin-auth/password/login → WindsurfPo","de":"Die offizielle Site kann sich einloggen, das Dashboard aber nicht — der alte Firebase-Pfad ist nicht mehr die Hauptroute. Kette rückentwickelt /_devin-auth/connections → /_devin-auth/password/login → WindsurfPo","es":"El sitio oficial inicia sesión pero el Dashboard no — el antiguo camino de Firebase ya no es la ruta principal. Cadena de ingeniería inversa /_devin-auth/connections → /_devin-auth/password/login → WindsurfPo"},"bio_dd373156":{"zh":"MODEL_TIER_ACCESS.pro 引用的是 ALL_MODEL_KEYS —— 模块加载时的 Object.keys(MODELS) 快照。mergeCloudModels 启动后加进来的五个 claude-opus-4-7-* 永远不在这个冻结数组里,每个请求都 403 model_not_entitled。一行改成 getter 让每次访问都重新枚举 live MODELS dict。三步 curl 复现写得教科书级。","en":"MODEL_TIER_ACCESS.pro references ALL_MODEL_KEYS — a snapshot of Object.keys(MODELS) taken at module load. The five claude-opus-4-7-* models that mergeCloudModels adds after startup never appear in this frozen array, so every request hits 403 model_not_entitled. One line turning it into a getter re-enumerates the live MODELS dict on every access. The three-step curl reproduction is textbook-grade.","zh-TW":"MODEL_TIER_ACCESS.pro 引用的是 ALL_MODEL_KEYS —— 模組載入時的 Object.keys(MODELS) 快照。mergeCloudModels 啟動後加進來的五個 claude-opus-4-7-* 永遠不在這個凍結陣列裡,每個請求都 403 model_not_entitled。一行改成 getter 讓每次存取都重新列舉 live MODELS dict。三步 curl 重現寫得教科書級。","ja":"MODEL_TIER_ACCESS.pro が参照しているのは ALL_MODEL_KEYS —— モジュールロード時の Object.keys(MODELS) のスナップショット。mergeCloudModels が起動後に追加する 5 つの claude-opus-4-7-* はこの凍結配列に永遠に入らず、すべてのリクエストが 403 model_not_entitled になる。一行で getter に変え、アクセスのたびに生きた MODELS dict を列挙し直す。3 ステップの curl 再現手順は教科書級。","ko":"MODEL_TIER_ACCESS.pro가 참조하는 것은 ALL_MODEL_KEYS — 모듈 로드 시점의 Object.keys(MODELS) 스냅샷이다. mergeCloudModels가 시작 후 추가하는 claude-opus-4-7-* 다섯 개는 이 얼어붙은 배열에 영원히 들어가지 못해 모든 요청이 403 model_not_entitled이 된다. 한 줄을 getter로 바꿔 접근할 때마다 살아있는 MODELS dict를 다시 열거하게 한다. 세 단계 curl 재현이 교과서급이다.","fr":"MODEL_TIER_ACCESS.pro référence ALL_MODEL_KEYS — un snapshot de Object.keys(MODELS) pris au chargement du module. Les cinq modèles claude-opus-4-7-* ajoutés par mergeCloudModels après le démarrage ","de":"MODEL_TIER_ACCESS.pro referenziert ALL_MODEL_KEYS — einen Snapshot von Object.keys(MODELS) beim Modul-Laden. Die fünf claude-opus-4-7-*-Modelle, die mergeCloudModels nach dem Start hinzufügt ","es":"MODEL_TIER_ACCESS.pro referencia ALL_MODEL_KEYS — un snapshot de Object.keys(MODELS) tomado al cargar el módulo. Los cinco modelos claude-opus-4-7-* que mergeCloudModels añade tras el arranque "},"bio_forrinzhao":{"zh":"发现 codex 的 apply_patch 工具描述里 \"FREEFORM tool, so do not wrap the patch in JSON.\" 这句会触发 Devin content policy,飞书 codex bot 全量请求被拦。live-bisect 7/7 确定性复现,并做了关键的双片段 A/B:只改 FREEFORM 一处仍然被拦,两处都改才通过 —— 面对 Devin 这个非确定性 content policy(同 prompt 先拦后过),这种重复验证是唯一靠谱的定位方法,项目里 a6 规则当初正是因为拿不到可靠 A/B 才 ship 成默认关。这个发现顺带暴露出一个更深的结构缺陷:工具描述 preamble 在 chat.js 里是注入在 neutralizeClientIdentity 之后的,导致 native 路径上任何经由工具描述进来的触发词都完整绕过 a1-a6 整条防线 —— 也就是说漏的不只是 FREEFORM。最终落地版把中和调整到 preamble 注入之后,并把这两条改写做成 (a7) 规则进既有序列(复用主开关与测试体系,而非新加 env),因此覆盖的是所有客户端、所有触发词。另采纳其 responses.js 修复:Codex 发的 input item 是裸 {role, content} 不带 type:'message',此前被静默丢弃导致上游收到空 messages → UPSTREAM_INTERNAL。PR 中另外附带的 __route 改动因会回退 gpt_native dialect(源码部署默认路径)未采纳。","en":"Discovered that the \"FREEFORM tool, so do not wrap the patch in JSON.\" sentence in codex's apply_patch tool description triggers Devin's content policy, blocking every request of the Feishu codex bot. live-bisect reproduced it deterministically 7/7, with a key two-fragment A/B: changing only FREEFORM still gets blocked, changing both passes — against Devin's nondeterministic content policy (the same prompt blocked first, passed later), this kind of repeated verification is the only reliable localization method; the project's a6 rule shipped default-off precisely because no reliable A/B was obtainable back then. The discovery also exposed a deeper structural flaw: in chat.js the tool description preamble is injected AFTER neutralizeClientIdentity, so on the native path any trigger word arriving through a tool description completely bypasses the entire a1-a6 defense line — the leak is bigger than just FREEFORM. The final landed version moved neutralization after the preamble injection and made the two rewrites an (a7) rule in the existing sequence (reusing the master switch and test system rather than adding a new env), covering all clients and all trigger words. Its responses.js fix was also adopted: Codex sends input items as bare {role, content} without type:'message', previously silently dropped, leaving upstream with empty messages → UPSTREAM_INTERNAL. The __route change bundled in the PR was not adopted because it would regress the gpt_native dialect (the default path for source deployments).","zh-TW":"發現 codex 的 apply_patch 工具描述裡「FREEFORM tool, so do not wrap the patch in JSON.」這句會觸發 Devin content policy,飛書 codex bot 全量請求被攔。live-bisect 7/7 確定性重現,並做了關鍵的雙片段 A/B:只改 FREEFORM 一處仍然被攔,兩處都改才通過 —— 面對 Devin 這個非確定性 content policy(同 prompt 先攔後過),這種重複驗證是唯一靠譜的定位方法,專案裡 a6 規則當初正是因為拿不到可靠 A/B 才 ship 成預設關。這個發現順帶暴露出一個更深的結構缺陷:工具描述 preamble 在 chat.js 裡是注入在 neutralizeClientIdentity 之後的,導致 native 路徑上任何經由工具描述進來的觸發詞都完整繞過 a1-a6 整條防線 —— 也就是說漏的不只是 FREEFORM。最終落地版把中和調整到 preamble 注入之後,並把這兩條改寫做成 (a7) 規則進既有序列(複用主開關與測試體系,而非新加 env),因此覆蓋的是所有客戶端、所有觸發詞。另採納其 responses.js 修復:Codex 發的 input item 是裸 {role, content} 不帶 type:'message',此前被靜默丟棄導致上游收到空 messages → UPSTREAM_INTERNAL。PR 中另外附帶的 __route 改動因會回退 gpt_native dialect(源碼部署預設路徑)未採納。","ja":"codex の apply_patch ツール説明にある「FREEFORM tool, so do not wrap the patch in JSON.」という一文が Devin の content policy をトリガーし、飛書 codex bot の全リクエストがブロックされることを発見。live-bisect で 7/7 の決定的再現を行い、重要な二断片 A/B も実施:FREEFORM 一箇所だけ変えてもブロックされ、両方変えてようやく通る —— Devin の非決定的 content policy(同じ prompt が先にブロック、後に通過)に対して、このような反復検証こそ唯一の信頼できる特定方法。プロジェクトの a6 ルールが当時デフォルト OFF で ship されたのは、まさに信頼できる A/B が取れなかったからだ。この発見は同時に深い構造欠陥を露呈した:chat.js ではツール説明 preamble が neutralizeClientIdentity の後に注入されているため、native 経路でツール説明を通じて入ってくるあらゆるトリガーワードが a1-a6 の防衛線全体を完全に迂回する —— 漏れは FREEFORM だけではない。最終的にマージされた版は中和を preamble 注入の後に移動し、この 2 つの書き換えを既存シーケンスの (a7) ルールとして追加(新規 env ではなく既存のマスタースイッチとテスト体系を再利用)、全クライアント・全トリガーワードをカバーする。その responses.js 修正も採用:Codex が送る input item は type:'message' を持たない裸の {role, content} で、以前は黙って破棄され、上流に空の messages が届いて UPSTREAM_INTERNAL になっていた。PR に同梱されていた __route 変更は gpt_native dialect(ソースデプロイのデフォルト経路)を後退させるため不採用。","ko":"codex의 apply_patch 툴 설명에 있는 'FREEFORM tool, so do not wrap the patch in JSON.' 문장이 Devin content policy를 트리거해 페이수 codex bot의 전체 요청이 차단되는 것을 발견했다. live-bisect로 7/7 결정적 재현을 했고, 핵심인 두 조각 A/B도 수행했다: FREEFORM 한 곳만 바꿔도 여전히 차단되고, 둘 다 바꿔야 통과한다 — Devin의 비결정적 content policy(같은 prompt가 먼저 차단됐다가 나중에 통과) 앞에서는 이런 반복 검증이 유일하게 신뢰할 만한 특정 방법이다. 프로젝트의 a6 규칙이 당시 기본 OFF로 ship된 것도 바로 신뢰할 만한 A/B를 얻지 못했기 때문이다. 이 발견은 동시에 더 깊은 구조적 결함을 드러냈다: chat.js에서 툴 설명 preamble이 neutralizeClientIdentity 이후에 주입되므로, native 경로에서 툴 설명을 통해 들어오는 모든 트리거 단어가 a1-a6 방어선 전체를 완전히 우회한다 — 새는 건 FREEFORM만이 아니다. 최종 병합본은 중화 처리를 preamble 주입 뒤로 옮기고, 이 두 개정을 (a7) 규칙으로 기존 시퀀스에 넣었다(새 env가 아니라 기존 마스터 스위치와 테스트 체계를 재사용), 따라서 모든 클라이언트, 모든 트리거 단어를 커버한다. 그의 responses.js 수정도 채택했다: Codex가 보내는 input item은 type:'message'가 없는 맨 {role, content}인데, 기존에는 조용히 버려져 업스트림에 빈 messages가 전달되어 UPSTREAM_INTERNAL이 됐다. PR에 딸려 있던 __route 변경은 gpt_native dialect(소스 배포의 기본 경로)를 퇴보시킬 수 있어 채택하지 않았다.","fr":"Découvert que la ligne « FREEFORM tool, so do not wrap the patch in JSON. » dans la description de l'outil apply_patch de codex déclenche la content policy de Devin, cassant le bot codex de Feishu ","de":"Entdeckt, dass die Zeile „FREEFORM tool, so do not wrap the patch in JSON.“ in der apply_patch-Tool-Beschreibung von codex Devins Content Policy auslöst und den Feishu-codex-Bot bricht ","es":"Descubrió que la línea \"FREEFORM tool, so do not wrap the patch in JSON.\" en la descripción de la herramienta apply_patch de codex dispara la content policy de Devin, rompiendo el bot de codex de Feishu "},"bio_clarezoe":{"zh":"根因修复 + 带全套测试,一次到位。fetchAndMergeModelCatalog() 只在 initAuth() 启动时跑一次:如果那一刻池里没有活跃账号,云端 catalog 同步就被整个跳过、再也不重试 —— 后来才加进来的账号永远触发不了 re-sync,导致新上线的上游模型(比如 2026-07-01 重新部署的 Claude Fable 5)不手动重启就不出现在 /v1/models。clarezoe 加了 _modelCatalogSynced 标志 + trySyncModelCatalog() 这个会合并并发调用的 fire-and-forget 包装,并把它挂到每一条账号激活路径上:addAccountByKey / addAccountByToken / addAccountByEmail / setAccountStatus(active) / resetAccountErrors / reportSuccess / maybeRecoverErrorAccount —— 加号、登录、手动改状态、错误重置、成功自愈、半开恢复,任意一条让账号变活都会补触发同步;四道 guard(已同步 / 同步进行中 / 无活跃账号 / 否则后台 fire)避免重复和并发风暴。PR 本身还捎带了 opus-4-8 全 effort 梯队、reasoning_effort 替换、以及从 thinking 里提取 tool_call(这块正好治 #178 那类工具不被调用)。+392/-32 跨 auth / chat / models / server 五个源文件 + 四个测试文件。作者已 rebase 手动合进 master(d4c7259..baa8524),去重一处路由 + 一行调试日志,其余原样保留,全量测试绿。","en":"Root-cause fix with a full test suite, done in one shot. fetchAndMergeModelCatalog() only runs once at initAuth() startup: if the pool has no active account at that moment, the cloud catalog sync is skipped entirely and never retried — accounts added later can never trigger a re-sync, so newly shipped upstream models (like Claude Fable 5, redeployed on 2026-07-01) don't show up in /v1/models without a manual restart. clarezoe added a _modelCatalogSynced flag plus trySyncModelCatalog(), a fire-and-forget wrapper that coalesces concurrent calls, and hooked it onto every account activation path: addAccountByKey / addAccountByToken / addAccountByEmail / setAccountStatus(active) / resetAccountErrors / reportSuccess / maybeRecoverErrorAccount — adding, logging in, manual status change, error reset, success self-heal, half-open recovery: any path that makes an account active re-triggers the sync; four guards (already synced / sync in progress / no active account / otherwise background fire) prevent duplication and concurrency storms. The PR also carried opus-4-8's full effort ladder, the reasoning_effort replacement, and tool_call extraction from thinking (which fixes the #178 class of tools never being called). +392/-32 across five source files in auth / chat / models / server plus four test files. The author rebased and manually merged into master (d4c7259..baa8524), deduped one route and one debug log line, kept the rest as-is — full test suite green.","zh-TW":"根因修復 + 帶全套測試,一次到位。fetchAndMergeModelCatalog() 只在 initAuth() 啟動時跑一次:如果那一刻池裡沒有活躍帳號,雲端 catalog 同步就被整個跳過、再也不重試 —— 後來才加進來的帳號永遠觸發不了 re-sync,導致新上線的上游模型(比如 2026-07-01 重新部署的 Claude Fable 5)不手動重啟就不出現在 /v1/models。clarezoe 加了 _modelCatalogSynced 標誌 + trySyncModelCatalog() 這個會合併並發呼叫的 fire-and-forget 包裝,並把它掛到每一條帳號啟用路徑上:addAccountByKey / addAccountByToken / addAccountByEmail / setAccountStatus(active) / resetAccountErrors / reportSuccess / maybeRecoverErrorAccount —— 加號、登入、手動改狀態、錯誤重置、成功自癒、半開恢復,任何一條讓帳號變活都會補觸發同步;四道 guard(已同步 / 同步進行中 / 無活躍帳號 / 否則背景 fire)避免重複和並發風暴。PR 本身還捎帶了 opus-4-8 全 effort 梯隊、reasoning_effort 替換、以及從 thinking 裡提取 tool_call(這塊正好治 #178 那類工具不被呼叫)。+392/-32 跨 auth / chat / models / server 五個源檔案 + 四個測試檔案。作者已 rebase 手動合進 master(d4c7259..baa8524),去重一處路由 + 一行除錯日誌,其餘原樣保留,全量測試綠。","ja":"根本原因修正 + フルテスト一式、一発で完了。fetchAndMergeModelCatalog() は initAuth() 起動時に一度しか走らない:その時点でプールにアクティブなアカウントがなければ、クラウドカタログ同期は丸ごとスキップされ、再試行もされない —— 後から追加されたアカウントは re-sync を永遠に発火できず、新しく上がった上流モデル(たとえば 2026-07-01 に再デプロイされた Claude Fable 5)が手動再起動しない限り /v1/models に現れない。clarezoe は _modelCatalogSynced フラグ + 並行呼び出しをマージする fire-and-forget ラッパー trySyncModelCatalog() を追加し、すべてのアカウント活性化経路にフックした:addAccountByKey / addAccountByToken / addAccountByEmail / setAccountStatus(active) / resetAccountErrors / reportSuccess / maybeRecoverErrorAccount —— 追加、ログイン、手動ステータス変更、エラーリセット、成功自己回復、ハーフオープン回復、どの経路でアカウントが活性化しても同期が補発火される。4 つのガード(同期済み / 同期中 / アクティブなアカウントなし / それ以外はバックグラウンドで fire)で重複と並行ストームを防止。PR はさらに opus-4-8 のフル effort 梯子、reasoning_effort 置換、thinking からの tool_call 抽出(#178 系のツール呼び出しされない問題を治療)も同梱。+392/-32 で auth / chat / models / server の 5 つのソースファイル + 4 つのテストファイルにまたがる。作者が rebase して master に手動マージ済み(d4c7259..baa8524)、ルート 1 箇所とデバッグログ 1 行を重複排除、残りはそのまま、全テスト green。","ko":"근본 원인 수정 + 전체 테스트 세트를 한 번에. fetchAndMergeModelCatalog()는 initAuth() 시작 시 한 번만 돈다: 그 순간 풀에 활성 계정이 없으면 클라우드 카탈로그 동기화가 통째로 건너뛰고 다시 시도하지 않는다 — 나중에 추가된 계정은 re-sync를 영원히 발화할 수 없어, 새로 올라온 업스트림 모델(예: 2026-07-01에 재배포된 Claude Fable 5)이 수동 재시작 전까지 /v1/models에 나타나지 않는다. clarezoe는 _modelCatalogSynced 플래그 + 동시 호출을 병합하는 fire-and-forget 래퍼 trySyncModelCatalog()를 추가하고, 모든 계정 활성화 경로에 걸었다: addAccountByKey / addAccountByToken / addAccountByEmail / setAccountStatus(active) / resetAccountErrors / reportSuccess / maybeRecoverErrorAccount — 추가, 로그인, 수동 상태 변경, 오류 리셋, 성공 자가 치유, 하프오픈 복구, 어느 경로로든 계정이 살아나면 동기화를 보조 발화한다. 네 개 가드(이미 동기화됨 / 동기화 진행 중 / 활성 계정 없음 / 그 외 백그라운드 fire)가 중복과 동시성 폭풍을 막는다. PR은 또 opus-4-8의 전체 effort 사다리, reasoning_effort 교체, thinking에서 tool_call 추출(#178류 툴이 호출되지 않는 문제를 치료)도 함께 가져왔다. +392/-32로 auth / chat / models / server 다섯 소스 파일 + 테스트 파일 네 개에 걸친다. 저자가 rebase해 master에 수동 병합했고(d4c7259..baa8524), 라우트 한 곳과 디버그 로그 한 줄을 중복 제거했으며 나머지는 그대로, 전체 테스트 green.","fr":"Fix de cause racine avec suite de tests complète, en une passe. fetchAndMergeModelCatalog() tourne exactement une fois au démarrage d'initAuth() : si aucun compte n'est actif à ce moment-là, la synchro du catalogue cloud est totalement sautée et jamais relancée — les comptes ajoutés plus tard ne sont jamais ","de":"Root-Cause-Fix mit kompletter Testsuite, in einem Durchgang. fetchAndMergeModelCatalog() läuft genau einmal beim initAuth()-Start: ist in dem Moment kein Konto aktiv, wird der Cloud-Katalog-Sync komplett übersprungen und nie erneut versucht — später hinzugefügte Konten werden nie ","es":"Fix de causa raíz con suite de tests completa, de una pasada. fetchAndMergeModelCatalog() corre exactamente una vez al arrancar initAuth(): si no hay ninguna cuenta activa en ese momento, el sync del catálogo cloud se salta entero y nunca se reintenta — las cuentas añadidas después nunca "},"bio_Yuuqq":{"zh":"Windows 上跑 WindsurfAPI 的第一手反馈。#73 一次扫了 7 处 bug,处置时挑了三处方向干净的落进 commit `44ad502`,作者署名走 Co-authored-by trailer:① `src/sanitize.js` 用 `fileURLToPath(import.meta.url)` 替代手撸 `file://` 转换 —— Windows 上 URL pathname 是 `/e:/path` 形式,老正则把 repo root 解析成空串,所有 sanitize 标记都失效;② `src/server.js` 让 OPTIONS preflight 真返 204 No Content + 显式 CORS 头(含 `anthropic-version`),不再让 OPTIONS fall through 到 JSON 200/404 body;③ `src/conversation-pool.js` 动态扩 META_TAG_NAMES 时给名字做 regex escape,防止用户消息里的 `<example.*>` 之类 tag 名注入元字符破坏整张 strip 表。其余四处(LS health watchdog、HTTP 明文 SEC warning、Windows langserver 路径重排)是因为跟现网部署有交互需要单独验证才没并,不是否定。","en":"First-hand feedback from running WindsurfAPI on Windows. #73 swept 7 bugs in one pass; three with clean directions landed in commit `44ad502`, authorship via Co-authored-by trailer: ① `src/sanitize.js` now uses `fileURLToPath(import.meta.url)` instead of a hand-rolled `file://` conversion — on Windows the URL pathname takes the `/e:/path` form, the old regex parsed the repo root as an empty string, and every sanitize mark silently stopped working; ② `src/server.js` makes the OPTIONS preflight truly return 204 No Content with explicit CORS headers (including `anthropic-version`), instead of falling through to a JSON 200/404 body; ③ `src/conversation-pool.js` regex-escapes names when dynamically extending META_TAG_NAMES, so tag names like `<example.*>` in user messages can no longer inject metacharacters and corrupt the whole strip table. The other four (LS health watchdog, HTTP plaintext SEC warning, Windows langserver path rearrangement) weren't merged because they interact with production deployments and need separate verification — not a rejection.","zh-TW":"Windows 上跑 WindsurfAPI 的第一手反饋。#73 一次掃了 7 處 bug,處置時挑了三處方向乾淨的落進 commit `44ad502`,作者署名走 Co-authored-by trailer:① `src/sanitize.js` 用 `fileURLToPath(import.meta.url)` 替代手撸 `file://` 轉換 —— Windows 上 URL pathname 是 `/e:/path` 形式,舊正則把 repo root 解析成空字串,所有 sanitize 標記都失效;② `src/server.js` 讓 OPTIONS preflight 真返 204 No Content + 明確 CORS 頭(含 `anthropic-version`),不再讓 OPTIONS fall through 到 JSON 200/404 body;③ `src/conversation-pool.js` 動態擴 META_TAG_NAMES 時給名字做 regex escape,防止使用者訊息裡的 `<example.*>` 之類 tag 名注入元字元破壞整張 strip 表。其餘四處(LS health watchdog、HTTP 明文 SEC warning、Windows langserver 路徑重排)是因為跟現網部署有互動需要單獨驗證才沒併,不是否定。","ja":"Windows で WindsurfAPI を動かした第一線のフィードバック。#73 は一度に 7 箇所のバグを洗い出し、方向のきれいな 3 点を commit `44ad502` に落とした。著者名は Co-authored-by trailer で残している。① `src/sanitize.js` で手書きの `file://` 変換をやめ `fileURLToPath(import.meta.url)` を使用 —— Windows では URL の pathname が `/e:/path` 形式になり、旧正規表現は repo root を空文字列に解析してしまい、すべての sanitize マークが無効になっていた。② `src/server.js` で OPTIONS preflight が本当に 204 No Content + 明示的な CORS ヘッダー(`anthropic-version` 含む)を返すようにし、JSON 200/404 body へのフォールスルーをやめた。③ `src/conversation-pool.js` で META_TAG_NAMES を動的拡張する際に名前を regex escape し、ユーザーメッセージ内の `<example.*>` のようなタグ名がメタ文字を注入して strip 表全体を壊すのを防いだ。残りの 4 点(LS health watchdog、HTTP 平文 SEC 警告、Windows の langserver パス再配置)は現行デプロイと相互作用があるため個別検証が必要で、並べなかっただけであり否定ではない。","ko":"Windows에서 WindsurfAPI를 돌려본 첫 손 피드백. #73은 한 번에 버그 7곳을 훑었고, 방향이 깨끗한 세 곳을 commit `44ad502`에 담았다. 저자 서명은 Co-authored-by trailer로 남겼다. ① `src/sanitize.js`에서 손으로 짠 `file://` 변환 대신 `fileURLToPath(import.meta.url)` 사용 — Windows에서는 URL pathname이 `/e:/path` 형태라 기존 정규식이 repo root를 빈 문자열로 파싱해 모든 sanitize 마크가 무력화됐다. ② `src/server.js`에서 OPTIONS preflight가 진짜로 204 No Content + 명시적 CORS 헤더(`anthropic-version` 포함)를 반환하게 해 JSON 200/404 body로 떨어지지 않게 했다. ③ `src/conversation-pool.js`에서 META_TAG_NAMES를 동적으로 확장할 때 이름을 regex escape해, 사용자 메시지의 `<example.*>` 같은 태그 이름이 메타문자를 주입해 strip 테이블 전체를 망치는 것을 방지한다. 나머지 네 곳(LS health watchdog, HTTP 평문 SEC 경고, Windows langserver 경로 재배치)은 운영 배포와 상호작용이 있어 별도 검증이 필요해 합치지 않은 것일 뿐, 부정이 아니다.","fr":"Retour de première main sur WindsurfAPI sous Windows. #73 a ramassé 7 bugs d'un coup ; trois aux directions propres ont été retenus dans le commit `44ad502`, crédité via un trailer Co-authored-by : ① `src/sanit","de":"Ersthand-Feedback vom WindsurfAPI-Lauf unter Windows. #73 räumte 7 Bugs auf einmal ab; drei mit sauberen Richtungen landeten im Commit `44ad502`, gutgeschrieben per Co-authored-by-Trailer: ① `src/sanit","es":"Feedback de primera mano de ejecutar WindsurfAPI en Windows. #73 barrió 7 bugs de una vez; tres con dirección limpia se eligieron para el commit `44ad502`, acreditados con un trailer Co-authored-by: ① `src/sanit"},"bio_sandleft":{"zh":"我之前那条 #63 已经把 sandleft 标过 S 级,#72 是第二次出手,质量稳。Codex Responses API 的四种非 function tool(custom / namespace / web_search / tool_search)反代之前直接 throw `Unsupported Responses tool type` 把整个请求干 500。这个 PR 不只是丢弃—— flattenResponseTool 把 namespace 用 `__` 分隔扁平化展开成 function,custom 转成单 string `input` 函数,web_search/tool_search 给到 `query` 函数;同时回程通过 `__response_tool` metadata 把原 type / namespace / originalName 还原回 `custom_tool_call` / `web_search_call` / 带 namespace 的 `function_call`。流式分支里 tool name 后到的 chunk 也兜住了。第一轮 PR review 我点出 tool_choice 没翻译 + 流式回转 metadata 透传不全 + 0 测试三个问题,sandleft 全补了,加了 4 条最小测试覆盖关键路径。我 cherry-pick 时叠了一层 UNBRIDGED 集合让 file_search / computer_use_preview / mcp 走 silent drop,web_search_preview 走和 web_search 一样的翻译,最后 fallback 是 warn-and-drop 不再 throw。","en":"My earlier #63 already graded sandleft S; #72 is a second pass and the quality holds. The proxy previously threw `Unsupported Responses tool type` for the four non-function tools of the Codex Responses API (custom / namespace / web_search / tool_search), killing the whole request with a 500. This PR doesn't just drop them — flattenResponseTool flattens namespace into functions joined by `__`, turns custom into a single-string `input` function, and maps web_search/tool_search to a `query` function; on the way back, `__response_tool` metadata restores the original type / namespace / originalName into `custom_tool_call` / `web_search_call` / namespaced `function_call`. The streaming branch also covers tool names arriving in later chunks. In the first review round I flagged three issues — tool_choice not translated, incomplete streaming return metadata, zero tests — and sandleft fixed all of them, adding 4 minimal tests covering the critical paths. When cherry-picking I layered an UNBRIDGED set so file_search / computer_use_preview / mcp get silently dropped, web_search_preview follows the same translation as web_search, and the final fallback is warn-and-drop instead of throwing.","zh-TW":"我先前那條 #63 已經把 sandleft 標過 S 級,#72 是第二次出手,品質穩定。Codex Responses API 的四種非 function tool(custom / namespace / web_search / tool_search)反代之前直接 throw `Unsupported Responses tool type` 把整個請求幹 500。這個 PR 不只是丟棄——flattenResponseTool 把 namespace 用 `__` 分隔扁平化展開成 function,custom 轉成單 string `input` 函式,web_search/tool_search 給到 `query` 函式;同時回程透過 `__response_tool` metadata 把原 type / namespace / originalName 還原回 `custom_tool_call` / `web_search_call` / 帶 namespace 的 `function_call`。串流分支裡 tool name 後到的 chunk 也兜住了。第一輪 PR review 我點出 tool_choice 沒翻譯 + 串流回轉 metadata 透傳不全 + 0 測試三個問題,sandleft 全補了,加了 4 條最小測試覆蓋關鍵路徑。我 cherry-pick 時疊了一層 UNBRIDGED 集合讓 file_search / computer_use_preview / mcp 走 silent drop,web_search_preview 走和 web_search 一樣的翻譯,最後 fallback 是 warn-and-drop 不再 throw。","ja":"以前の #63 で sandleft を S 級と評価したが、#72 は二度目の参加で品質は安定している。Codex Responses API の 4 種の非 function ツール(custom / namespace / web_search / tool_search)は、従来のリバースプロキシでは直接 `Unsupported Responses tool type` を throw してリクエスト全体を 500 にしていた。この PR は単に捨てるのではなく、flattenResponseTool が namespace を `__` 区切りでフラットに展開して function 化し、custom は単一の string `input` 関数に、web_search/tool_search は `query` 関数にする。復路では `__response_tool` metadata 経由で元の type / namespace / originalName を `custom_tool_call` / `web_search_call` / namespace 付き `function_call` に復元する。ストリーミング分岐では tool name が後続の chunk で届くケースも拾っている。初回 review で tool_choice が翻訳されていない・ストリーミング復路の metadata 引き継ぎが不完全・テストゼロの三つを指摘したが、sandleft はすべて補完し、主要経路をカバーする最小テストを 4 本追加した。cherry-pick する際に UNBRIDGED 集合を重ね、file_search / computer_use_preview / mcp は静かに drop、web_search_preview は web_search と同じ翻訳にし、最終フォールバックは throw ではなく warn-and-drop にした。","ko":"이전 #63에서 sandleft를 S급으로 평가했고, #72는 두 번째 참여인데 품질이 안정적이다. Codex Responses API의 네 가지 비-function 툴(custom / namespace / web_search / tool_search)은 기존 리버스 프록시에서 바로 `Unsupported Responses tool type`을 throw해 요청 전체를 500으로 만들었다. 이 PR은 단순히 버리는 게 아니라 flattenResponseTool이 namespace를 `__` 구분자로 평탄화해 function으로 만들고, custom은 단일 string `input` 함수로, web_search/tool_search는 `query` 함수로 변환한다. 복귀 경로에서는 `__response_tool` metadata를 통해 원래 type / namespace / originalName을 `custom_tool_call` / `web_search_call` / namespace가 붙은 `function_call`로 복원한다. 스트리밍 분기에서 tool name이 나중에 도착하는 chunk로 오는 경우도 처리한다. 첫 리뷰에서 tool_choice가 번역되지 않음 + 스트리밍 복귀 metadata 전달 불완전 + 테스트 0개라는 세 문제를 지적했고, sandleft는 전부 보완해 핵심 경로를 덮는 최소 테스트 4개를 추가했다. cherry-pick할 때 UNBRIDGED 집합을 얹어 file_search / computer_use_preview / mcp는 조용히 drop, web_search_preview는 web_search와 동일한 번역을 거치게 하고, 최종 폴백은 throw 대신 warn-and-drop으로 만들었다.","fr":"Mon #63 précédent avait déjà noté sandleft S-grade ; #72 est la deuxième sortie et le niveau tient. Les quatre outils non-fonction de l'API Codex Responses (custom / namespace / web_search / to","de":"Mein früheres #63 hat sandleft schon auf S-Grade gestellt; #72 ist der zweite Einsatz, und das Niveau hält. Die vier Nicht-Function-Tools der Codex-Responses-API (custom / namespace / web_search / to","es":"Mi #63 anterior ya calificó a sandleft con S-grade; #72 es la segunda salida y el nivel se mantiene. Las cuatro herramientas no-función de la API Codex Responses (custom / namespace / web_search / to"},"bio_colin1112a":{"zh":"系统性扫了一遍仓库提了 CRITICAL/HIGH/MEDIUM 15 条修复建议:XSS 转义、shell 注入防护、10MB body 上限、auth 路由顺序、gRPC 双回调保护、LS pool 并发竞态、HTTP/2 frame 16MB 上限、varint BigInt 支持。PR 因改动面过大未直接合并,但其中多项被后续独立重做。","en":"Systematically swept the repo and raised CRITICAL/HIGH/MEDIUM 15 fix suggestions: XSS escaping, shell injection protection, 10MB body cap, auth route ordering, gRPC double-callback protection, LS pool concurrency race, HTTP/2 frame 16MB cap, varint BigInt support. The PR wasn't merged directly because the change surface was too large, but several items were independently redone later.","zh-TW":"系統性掃了一遍倉庫提了 CRITICAL/HIGH/MEDIUM 15 條修復建議:XSS 轉義、shell 注入防護、10MB body 上限、auth 路由順序、gRPC 雙回調保護、LS pool 並發競態、HTTP/2 frame 16MB 上限、varint BigInt 支援。PR 因改動面過大未直接合併,但其中多項被後續獨立重做。","ja":"リポジトリ全体を体系的にスキャンし、CRITICAL/HIGH/MEDIUM の修正提案を 15 件上げた:XSS エスケープ、shell インジェクション対策、10MB body 上限、auth ルートの順序、gRPC の二重コールバック保護、LS pool の並行レース、HTTP/2 frame の 16MB 上限、varint の BigInt 対応。PR は変更面が大きすぎて直接マージされなかったが、そのうち複数項目が後に独立して再実装された。","ko":"저장소 전체를 체계적으로 훑어 CRITICAL/HIGH/MEDIUM 수정 제안 15건을 올렸다: XSS 이스케이프, shell 인젝션 방어, 10MB body 상한, auth 라우트 순서, gRPC 이중 콜백 보호, LS pool 동시성 레이스, HTTP/2 frame 16MB 상한, varint BigInt 지원. 변경 면이 너무 넓어 PR은 직접 병합되지 않았지만, 그중 여러 항목이 이후 독립적으로 재구현됐다.","fr":"Un balayage systématique du dépôt produisant 15 suggestions de fix CRITICAL/HIGH/MEDIUM : échappement XSS, durcissement anti-injection shell, limite de corps 10MB, ordre des routes auth, protection des double-callbacks gRPC, courses de concurrence du pool LS, frame HTTP/2 ","de":"Ein systematischer Durchlauf des Repos mit 15 CRITICAL/HIGH/MEDIUM-Fixvorschlägen: XSS-Escaping, Shell-Injection-Härtung, 10MB-Body-Limit, Auth-Routen-Reihenfolge, gRPC-Doppel-Callback-Schutz, LS-Pool-Konkurrenz-Races, HTTP/2-frame ","es":"Un barrido sistemático del repo produciendo 15 sugerencias de fix CRITICAL/HIGH/MEDIUM: escaping XSS, endurecimiento contra inyección de shell, límite de body de 10MB, orden de rutas auth, protección de doble callback gRPC, carreras de concurrencia en el pool LS, frame HTTP/2 "},"oneoff_wjurkowlaniec_0":{"zh":"Fix/swe family tool dialect","en":"Fix/swe family tool dialect","zh-TW":"Fix/swe family tool dialect","ja":"Fix/swe family tool dialect","ko":"Fix/swe family tool dialect","fr":"Fix/swe family tool dialect","de":"Fix/swe family tool dialect","es":"Fix/swe family tool dialect"},"oneoff_moduvoice_0":{"zh":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","en":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","zh-TW":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","ja":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","ko":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","fr":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","de":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher","es":"feat(i18n,dashboard): add Korean (ko) locale — full translation + 3-way lang switcher"},"oneoff_clarezoe_0":{"zh":"fix: retry cloud model catalog sync when first account becomes active","en":"fix: retry cloud model catalog sync when first account becomes active","zh-TW":"fix: retry cloud model catalog sync when first account becomes active","ja":"fix: retry cloud model catalog sync when first account becomes active","ko":"fix: retry cloud model catalog sync when first account becomes active","fr":"fix: retry cloud model catalog sync when first account becomes active","de":"fix: retry cloud model catalog sync when first account becomes active","es":"fix: retry cloud model catalog sync when first account becomes active"},"oneoff_lovivi_0":{"zh":"fix(tool-emulation): use gpt_native dialect for glm-5.2","en":"fix(tool-emulation): use gpt_native dialect for glm-5.2","zh-TW":"fix(tool-emulation): use gpt_native dialect for glm-5.2","ja":"fix(tool-emulation): use gpt_native dialect for glm-5.2","ko":"fix(tool-emulation): use gpt_native dialect for glm-5.2","fr":"fix(tool-emulation): use gpt_native dialect for glm-5.2","de":"fix(tool-emulation): use gpt_native dialect for glm-5.2","es":"fix(tool-emulation): use gpt_native dialect for glm-5.2"},"oneoff_brandonedley_0":{"zh":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","en":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","zh-TW":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","ja":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","ko":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","fr":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","de":"feat: add GLM 5.2 and Kimi K2.7 catalog entries","es":"feat: add GLM 5.2 and Kimi K2.7 catalog entries"},"oneoff_limweb_0":{"zh":"update","en":"update","zh-TW":"update","ja":"update","ko":"update","fr":"update","de":"update","es":"update"},"oneoff_kevin5251984_0":{"zh":"feat(dashboard): default mode.","en":"feat(dashboard): default mode.","zh-TW":"feat(dashboard): default mode.","ja":"feat(dashboard): default mode.","ko":"feat(dashboard): default mode.","fr":"feat(dashboard): default mode.","de":"feat(dashboard): default mode.","es":"feat(dashboard): default mode."},"oneoff_MatrixNeoKozak_0":{"zh":"fix: return 400 for malformed JSON in dashboard API","en":"fix: return 400 for malformed JSON in dashboard API","zh-TW":"fix: return 400 for malformed JSON in dashboard API","ja":"fix: return 400 for malformed JSON in dashboard API","ko":"fix: return 400 for malformed JSON in dashboard API","fr":"fix: return 400 for malformed JSON in dashboard API","de":"fix: return 400 for malformed JSON in dashboard API","es":"fix: return 400 for malformed JSON in dashboard API"},"oneoff_ucloudnb666_0":{"zh":"feat: add Astraflow provider support","en":"feat: add Astraflow provider support","zh-TW":"feat: add Astraflow provider support","ja":"feat: add Astraflow provider support","ko":"feat: add Astraflow provider support","fr":"feat: add Astraflow provider support","de":"feat: add Astraflow provider support","es":"feat: add Astraflow provider support"},"oneoff_lauvww_0":{"zh":"feat: improve dashboard batch import parsing","en":"feat: improve dashboard batch import parsing","zh-TW":"feat: improve dashboard batch import parsing","ja":"feat: improve dashboard batch import parsing","ko":"feat: improve dashboard batch import parsing","fr":"feat: improve dashboard batch import parsing","de":"feat: improve dashboard batch import parsing","es":"feat: improve dashboard batch import parsing"},"oneoff_Fermiz_0":{"zh":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","en":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","zh-TW":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","ja":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","ko":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","fr":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","de":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy","es":"feat: Cascade reuse optimization for single-user setups & HTTPS proxy"},"oneoff_linqichenggg_0":{"zh":"fix: align language server paths across platforms","en":"fix: align language server paths across platforms","zh-TW":"fix: align language server paths across platforms","ja":"fix: align language server paths across platforms","ko":"fix: align language server paths across platforms","fr":"fix: align language server paths across platforms","de":"fix: align language server paths across platforms","es":"fix: align language server paths across platforms"},"oneoff_datfooldive_0":{"zh":"refactor(dashboard): UI cleanup","en":"refactor(dashboard): UI cleanup","zh-TW":"refactor(dashboard): UI cleanup","ja":"refactor(dashboard): UI cleanup","ko":"refactor(dashboard): UI cleanup","fr":"refactor(dashboard): UI cleanup","de":"refactor(dashboard): UI cleanup","es":"refactor(dashboard): UI cleanup"},"oneoff_UntaDotMy_0":{"zh":"feat: add Codex Responses WebSocket support","en":"feat: add Codex Responses WebSocket support","zh-TW":"feat: add Codex Responses WebSocket support","ja":"feat: add Codex Responses WebSocket support","ko":"feat: add Codex Responses WebSocket support","fr":"feat: add Codex Responses WebSocket support","de":"feat: add Codex Responses WebSocket support","es":"feat: add Codex Responses WebSocket support"},"oneoff_Kloping_0":{"zh":"fix(dashboard): dashboard account management page width adaptive","en":"fix(dashboard): dashboard account management page width adaptive","zh-TW":"fix(dashboard): dashboard account management page width adaptive","ja":"fix(dashboard): dashboard account management page width adaptive","ko":"fix(dashboard): dashboard account management page width adaptive","fr":"fix(dashboard): dashboard account management page width adaptive","de":"fix(dashboard): dashboard account management page width adaptive","es":"fix(dashboard): dashboard account management page width adaptive"},"oneoff_Await-d_0":{"zh":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","en":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","zh-TW":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","ja":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","ko":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","fr":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","de":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login","es":"fix(dashboard): send Auth1 token in PostAuth header for v2.0.89 login"},"oneoff_suhaihui-git_0":{"zh":"新增响应缓存关闭开关","en":"feat: add toggle to disable response cache","zh-TW":"新增響應快取關閉開關","ja":"レスポンスキャッシュを無効化するスイッチを追加","ko":"응답 캐시 끄기 스위치 추가","fr":"feat: ajout d'un toggle pour désactiver le cache de réponse","de":"feat: Schalter zum Deaktivieren des Antwort-Caches hinzugefügt","es":"feat: añadir toggle para desactivar la caché de respuesta"},"oneoff_chukangkang_0":{"zh":"fix: add X-Devin-Auth1-Token header to PostAuth request","en":"fix: add X-Devin-Auth1-Token header to PostAuth request","zh-TW":"fix: add X-Devin-Auth1-Token header to PostAuth request","ja":"fix: add X-Devin-Auth1-Token header to PostAuth request","ko":"fix: add X-Devin-Auth1-Token header to PostAuth request","fr":"fix: add X-Devin-Auth1-Token header to PostAuth request","de":"fix: add X-Devin-Auth1-Token header to PostAuth request","es":"fix: add X-Devin-Auth1-Token header to PostAuth request"},"oneoff_RogueBinary_0":{"zh":"Harden defaults and add secure startup launcher","en":"Harden defaults and add secure startup launcher","zh-TW":"Harden defaults and add secure startup launcher","ja":"Harden defaults and add secure startup launcher","ko":"Harden defaults and add secure startup launcher","fr":"Harden defaults and add secure startup launcher","de":"Harden defaults and add secure startup launcher","es":"Harden defaults and add secure startup launcher"},"oneoff_Yuuqq_0":{"zh":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","en":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","zh-TW":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","ja":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","ko":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","fr":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","de":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)","es":"fix: Windows compatibility + 6 bug fixes (LS health watchdog, sanitize, regex injection, HTTP 204)"},"oneoff_sandleft_0":{"zh":"fix: 为 Codex 增加 Responses 工具兼容性","en":"fix: add Responses tool compatibility for Codex","zh-TW":"fix: 為 Codex 增加 Responses 工具相容性","ja":"fix: Codex に Responses ツール互換性を追加","ko":"fix: Codex에 Responses 도구 호환성 추가","fr":"fix: ajout de la compatibilité des outils Responses pour Codex","de":"fix: Responses-Tool-Kompatibilität für Codex hinzugefügt","es":"fix: añadir compatibilidad de herramientas Responses para Codex"},"oneoff_youfak_0":{"zh":"增加了 Docker 部署支持","en":"feat: add Docker deployment support","zh-TW":"增加了 Docker 部署支援","ja":"Docker デプロイのサポートを追加","ko":"Docker 배포 지원 추가","fr":"feat: ajout du support de déploiement Docker","de":"feat: Docker-Deployment-Support hinzugefügt","es":"feat: añadir soporte de despliegue Docker"},"oneoff_motto1_0":{"zh":"修复 Dashboard 登录取号:支持 Auth1 登录流并新增批量导入","en":"fix: restore Dashboard login with Auth1 flow and add batch import","zh-TW":"修復 Dashboard 登入取號:支援 Auth1 登入流並新增批次匯入","ja":"Dashboard ログイン時のアカウント取得を修正:Auth1 ログインフロー対応と一括インポートを新設","ko":"Dashboard 로그인 계정 확보 수정: Auth1 로그인 흐름 지원 + 일괄 가져오기 추가","fr":"fix: rétablir le login Dashboard avec le flux Auth1 et ajouter l'import en masse","de":"fix: Dashboard-Login mit Auth1-Flow wiederhergestellt und Batch-Import hinzugefügt","es":"fix: restaurar el login del Dashboard con el flujo Auth1 y añadir importación masiva"},"oneoff_colin1112a_0":{"zh":"fix: 修复 15 个安全和逻辑 bug (CRITICAL/HIGH/MEDIUM)","en":"fix: address 15 security and logic bugs (CRITICAL/HIGH/MEDIUM)","zh-TW":"fix: 修復 15 個安全和邏輯 bug (CRITICAL/HIGH/MEDIUM)","ja":"fix: セキュリティとロジックの bug を 15 件修正(CRITICAL/HIGH/MEDIUM)","ko":"fix: 보안·로직 버그 15개 수정 (CRITICAL/HIGH/MEDIUM)","fr":"fix: corriger 15 bugs de sécurité et de logique (CRITICAL/HIGH/MEDIUM)","de":"fix: 15 Sicherheits- und Logik-Bugs behoben (CRITICAL/HIGH/MEDIUM)","es":"fix: corregir 15 bugs de seguridad y lógica (CRITICAL/HIGH/MEDIUM)"},"oneoff_dd373156_0":{"zh":"fix(models): make pro tier include dynamically merged cloud models","en":"fix(models): make pro tier include dynamically merged cloud models","zh-TW":"fix(models): make pro tier include dynamically merged cloud models","ja":"fix(models): make pro tier include dynamically merged cloud models","ko":"fix(models): make pro tier include dynamically merged cloud models","fr":"fix(models): make pro tier include dynamically merged cloud models","de":"fix(models): make pro tier include dynamically merged cloud models","es":"fix(models): make pro tier include dynamically merged cloud models"},"bio_ZLin98":{"zh":"#80 一次塞了 16 个文件 700 行的安全加固大改,原 PR 因为面太大不便逐项 review 被关掉,这是当时处置不到位的一条 —— v2.0.12 单独把安全方向最稳的三件事 cherry-pick 进主线,作者署名通过 Co-authored-by trailer 保留。① LS env allowlist:以前直接把整份 process.env 喂给 LS 子进程,AWS_SECRET / GITHUB_TOKEN / 任何 CI secret 都漏出去;新加 `buildLanguageServerEnv` 只白名单 HOME / PATH / LANG / TMPDIR / proxy / SSL trust 八类,剩下全部丢弃,并保留 proxy override 与 /root fallback 两条原行为。② 跨平台 workspace 重置:以前用 `execSync('mkdir -p /tmp/... && rm -rf')` 走 POSIX shell,Windows 直接炸;改用 Node fs `mkdirSync + readdirSync + rmSync`,三大平台一致行为。③ Dashboard auth 收紧:移除 `?pwd=` query string fallback —— logs/stream 早就从 EventSource 迁到 fetch + ReadableStream,query 路径已无客户端在用,留着只会让 password 进 URL 访问日志和浏览器历史。其余 6 项(HOST 默认 127.0.0.1 + ensurePublicAuth、ENABLE_SELF_UPDATE/BATCH_LOGIN/LS_RESTART feature flag 默认全关、`getAccountList` 默认不返完整 apiKey、sessionStorage 替代 localStorage、FORWARD_CALLER_ENV opt-in、scripts/start.js launcher)都是行为变化或 UX 倒退,没并是怕一次性破坏现有 docker / dashboard 部署,但思路完全合理。","en":"#80 was a single 16-file, 700-line security hardening mega-change; the original PR was closed because it was too big to review item by item — one where the handling at the time wasn't up to par. v2.0.12 cherry-picked the three most solid security items into mainline, with authorship preserved via Co-authored-by trailer. ① LS env allowlist: previously the entire process.env was fed to the LS child process, leaking AWS_SECRET / GITHUB_TOKEN / any CI secret; the new `buildLanguageServerEnv` whitelists only HOME / PATH / LANG / TMPDIR / proxy / SSL trust — eight categories total — and discards the rest, while preserving the proxy override and /root fallback behaviors. ② Cross-platform workspace reset: previously `execSync('mkdir -p /tmp/... && rm -rf')` ran a POSIX shell and broke on Windows; switched to Node fs `mkdirSync + readdirSync + rmSync` for consistent behavior on all three platforms. ③ Dashboard auth tightening: removed the `?pwd=` query string fallback — logs/stream migrated from EventSource to fetch + ReadableStream long ago and no client uses the query path anymore; keeping it only put the password into URL access logs and browser history. The remaining 6 items (HOST defaulting to 127.0.0.1 + ensurePublicAuth, ENABLE_SELF_UPDATE/BATCH_LOGIN/LS_RESTART feature flags all off by default, `getAccountList` not returning full apiKey by default, sessionStorage replacing localStorage, FORWARD_CALLER_ENV opt-in, scripts/start.js launcher) are behavior changes or UX regressions; they weren't merged for fear of breaking existing docker/dashboard deployments in one shot — but the reasoning is entirely sound.","zh-TW":"#80 一次塞了 16 個檔案 700 行的安全加固大改,原 PR 因為面太大不便逐項 review 被關掉,這是當時處置不到位的一條 —— v2.0.12 單獨把安全方向最穩的三件事 cherry-pick 進主線,作者署名透過 Co-authored-by trailer 保留。① LS env allowlist:以前直接把整份 process.env 餵給 LS 子程序,AWS_SECRET / GITHUB_TOKEN / 任何 CI secret 都漏出去;新加 `buildLanguageServerEnv` 只白名單 HOME / PATH / LANG / TMPDIR / proxy / SSL trust 八類,剩下全部丟棄,並保留 proxy override 與 /root fallback 兩條原行為。② 跨平台 workspace 重置:以前用 `execSync('mkdir -p /tmp/... && rm -rf')` 走 POSIX shell,Windows 直接炸;改用 Node fs `mkdirSync + readdirSync + rmSync`,三大平台一致行為。③ Dashboard auth 收緊:移除 `?pwd=` query string fallback —— logs/stream 早就從 EventSource 遷到 fetch + ReadableStream,query 路徑已無客戶端在用,留著只會讓 password 進 URL 存取日誌和瀏覽器歷史。其餘 6 項(HOST 預設 127.0.0.1 + ensurePublicAuth、ENABLE_SELF_UPDATE/BATCH_LOGIN/LS_RESTART feature flag 預設全關、`getAccountList` 預設不返完整 apiKey、sessionStorage 替代 localStorage、FORWARD_CALLER_ENV opt-in、scripts/start.js launcher)都是行為變化或 UX 倒退,沒併是怕一次破壞現有 docker / dashboard 部署,但思路完全合理。","ja":"#80 は 16 ファイル・700 行のセキュリティ強化という大掛かりな 1 本の PR だった。変更面が広すぎて項目ごとのレビューができないという理由で元 PR は閉じられており、当時の対処が不十分だった一件 —— v2.0.12 でセキュリティ方向で最も堅実な 3 点をメインラインに cherry-pick し、著者名は Co-authored-by trailer で残した。① LS env allowlist:従来は process.env 全体を LS 子プロセスに渡しており、AWS_SECRET / GITHUB_TOKEN / あらゆる CI secret が漏れていた。新設の `buildLanguageServerEnv` は HOME / PATH / LANG / TMPDIR / proxy / SSL trust の 8 類のみをホワイトリストし、残りはすべて破棄。proxy override と /root fallback の従来動作は維持。② クロスプラットフォームの workspace リセット:従来は `execSync('mkdir -p /tmp/... && rm -rf')` で POSIX shell に依存しており Windows で壊れていた。Node の fs `mkdirSync + readdirSync + rmSync` に変更し、三大プラットフォームで同じ挙動に。③ Dashboard 認証の引き締め:`?pwd=` query string のフォールバックを削除 —— logs/stream はとっくに EventSource から fetch + ReadableStream へ移行済みで、query 経路を使うクライアントはもういない。残しておくとパスワードが URL のアクセスログやブラウザ履歴に残るだけ。残りの 6 項目(HOST デフォルト 127.0.0.1 + ensurePublicAuth、ENABLE_SELF_UPDATE/BATCH_LOGIN/LS_RESTART の feature flag をデフォルトで全部 OFF、`getAccountList` がデフォルトで完全な apiKey を返さない、localStorage から sessionStorage へ、FORWARD_CALLER_ENV opt-in、scripts/start.js launcher)は挙動変更か UX の後退で、既存の docker / dashboard デプロイを一撃で壊す恐れがあるため並べなかったが、考え方は完全に合理的。","ko":"#80은 16개 파일 700줄짜리 보안 강화 대형 변경 한 방이었다. 변경 면이 너무 넓어 항목별 리뷰가 어렵다는 이유로 원래 PR은 닫혔고, 당시 처리가 부족했던 한 건이다 — v2.0.12에서 보안 방향으로 가장 탄탄한 세 가지를 메인라인에 cherry-pick했고, 저자 서명은 Co-authored-by trailer로 보존했다. ① LS env allowlist: 기존에는 process.env 전체를 LS 자식 프로세스에 넘겨 AWS_SECRET / GITHUB_TOKEN / 모든 CI secret이 새어나갔다. 새 `buildLanguageServerEnv`는 HOME / PATH / LANG / TMPDIR / proxy / SSL trust 여덟 종류만 허용 목록에 넣고 나머지는 전부 버리며, proxy override와 /root fallback이라는 기존 동작 두 가지는 유지한다. ② 크로스 플랫폼 workspace 리셋: 기존에는 `execSync('mkdir -p /tmp/... && rm -rf')`로 POSIX shell에 의존해 Windows에서 바로 깨졌다. Node fs `mkdirSync + readdirSync + rmSync`로 바꿔 세 플랫폼에서 동일한 동작을 보장한다. ③ Dashboard 인증 강화: `?pwd=` query string 폴백 제거 — logs/stream은 오래전에 EventSource에서 fetch + ReadableStream으로 이전했고 query 경로를 쓰는 클라이언트가 더는 없으므로, 남겨두면 비밀번호가 URL 액세스 로그와 브라우저 기록에 남을 뿐이다. 나머지 6개 항목(HOST 기본 127.0.0.1 + ensurePublicAuth, ENABLE_SELF_UPDATE/BATCH_LOGIN/LS_RESTART feature flag 기본 전부 OFF, `getAccountList`가 기본적으로 전체 apiKey를 반환하지 않음, localStorage를 sessionStorage로 교체, FORWARD_CALLER_ENV opt-in, scripts/start.js launcher)은 동작 변경이거나 UX 후퇴라서, 기존 docker/dashboard 배포를 한 번에 깨뜨릴까 봐 합치지 않았지만 접근 자체는 완전히 합리적이다."},"bio_you922":{"zh":"和 #162 sticky session 一起出手的第二个 PR(#162 那条已经把 you922 标过 S)。LS 二进制偶发因内存压力 / 上游协议变更 / 瞬态错误崩掉,老逻辑只 log 崩溃 + 清理,后续所有打到这个 LS 的请求一路 ECONNRESET / ERR_HTTP2_CONNECT 挂着,直到手动重启或下个请求触发 ensureLs()(#157 报的就是这个)。新加 scheduleLsRestart() 挂进 proc.on('exit'):指数退避 1s→2s→4s→8s(base delay 可配),max 3 次重试封死 permanent error 的无限循环,per-key _restartAttempts Map 防重复定时器,setTimeout 用 .unref() 不阻塞 Node 进程退出,再加 _intentionalShutdown Set 让 stopLanguageServer / restartLsForProxy 的主动关闭不会误触自动重启,getRestartStats() 暴露重启计数给监控。LS_AUTO_RESTART=0 可回退旧行为。让单个 LS 挂掉不再拖垮整个账号池。","en":"The second PR alongside #162 sticky session (#162 already graded you922 S). The LS binary occasionally dies from memory pressure / upstream protocol changes / transient errors; the old logic only logged the crash and cleaned up, leaving every request to that LS hanging with ECONNRESET / ERR_HTTP2_CONNECT until a manual restart or the next request triggers ensureLs() (#157 reported exactly this). The new scheduleLsRestart() hooks into proc.on('exit'): exponential backoff 1s→2s→4s→8s (base delay configurable), max 3 retries to cap the infinite loop on permanent errors, a per-key _restartAttempts Map to prevent duplicate timers, setTimeout with .unref() so it never blocks Node process exit, plus an _intentionalShutdown Set so stopLanguageServer / restartLsForProxy's intentional close doesn't accidentally trigger auto-restart, and getRestartStats() exposing the restart count for monitoring. LS_AUTO_RESTART=0 reverts to the old behavior. A single dead LS no longer drags down the whole account pool.","zh-TW":"和 #162 sticky session 一起出手的第二個 PR(#162 那條已經把 you922 標過 S)。LS 二進位偶發因記憶體壓力 / 上游協定變更 / 瞬態錯誤崩掉,舊邏輯只 log 崩潰 + 清理,後續所有打到這個 LS 的請求一路 ECONNRESET / ERR_HTTP2_CONNECT 掛著,直到手動重啟或下個請求觸發 ensureLs()(#157 報的就是這個)。新加 scheduleLsRestart() 掛進 proc.on('exit'):指數退避 1s→2s→4s→8s(base delay 可配),max 3 次重試封死 permanent error 的無限循環,per-key _restartAttempts Map 防重複定時器,setTimeout 用 .unref() 不阻塞 Node 程序退出,再加 _intentionalShutdown Set 讓 stopLanguageServer / restartLsForProxy 的主動關閉不會誤觸自動重啟,getRestartStats() 暴露重啟次數給監控。LS_AUTO_RESTART=0 可回退舊行為。讓單個 LS 掛掉不再拖垮整個帳號池。","ja":"#162 の sticky session と同時に出た 2 本目の PR(#162 で you922 はすでに S 評価)。LS バイナリがメモリプレッシャー / 上流プロトコル変更 / 一時エラーで偶発的に落ちる。旧ロジックはクラッシュのログとクリーンアップのみで、以降その LS へのリクエストは手動再起動か次のリクエストが ensureLs() を発火するまで ECONNRESET / ERR_HTTP2_CONNECT のままぶら下がっていた(#157 がまさにこれを報告)。新設の scheduleLsRestart() は proc.on('exit') にフック:指数バックオフ 1s→2s→4s→8s(base delay 設定可)、permanent error の無限ループを封じる最大 3 回のリトライ、重複タイマー防止の per-key _restartAttempts Map、Node プロセス終了をブロックしない setTimeout の .unref()、さらに _intentionalShutdown Set で stopLanguageServer / restartLsForProxy の意図的なシャットダウンが自動再起動を誤発火しないようにし、getRestartStats() で再起動回数をモニタリングに公開。LS_AUTO_RESTART=0 で旧挙動に戻せる。LS が 1 本落ちてもアカウントプール全体を巻き込まない。","ko":"#162 sticky session과 함께 나온 두 번째 PR(#162에서 you922는 이미 S 판정). LS 바이너리가 메모리 압박 / 업스트림 프로토콜 변경 / 일시적 오류로 간헐적으로 죽는다. 기존 로직은 크래시 로그 + 정리만 했고, 이후 이 LS로 향하는 요청은 수동 재시작이나 다음 요청이 ensureLs()를 발화하기 전까지 ECONNRESET / ERR_HTTP2_CONNECT로 계속 걸려 있었다(#157이 정확히 이 문제를 보고). 새 scheduleLsRestart()는 proc.on('exit')에 훅을 단다: 지수 백오프 1s→2s→4s→8s(base delay 설정 가능), permanent error의 무한 루프를 막는 최대 3회 재시도, 중복 타이머 방지 per-key _restartAttempts Map, Node 프로세스 종료를 막지 않는 setTimeout .unref(), 그리고 _intentionalShutdown Set으로 stopLanguageServer / restartLsForProxy의 의도적 종료가 자동 재시작을 오발화하지 않게 하고, getRestartStats()로 재시작 횟수를 모니터링에 노출한다. LS_AUTO_RESTART=0으로 기존 동작으로 되돌릴 수 있다. LS 하나가 죽어도 계정 풀 전체를 끌고 가지 않는다."}};
const I18N_LANGS = ['zh', 'en', 'zh-TW', 'ja', 'ko', 'fr', 'de', 'es'];
let LANG = 'zh';
function tr(key) { return (I18N[key] && I18N[key][LANG]) || (I18N[key] && I18N[key]['zh']) || key; }
function applyLang() {
document.querySelectorAll('[data-i18n]').forEach(el => {
const txt = tr(el.dataset.i18n);
if (el.dataset.i18nHtml) el.innerHTML = txt;
else el.textContent = txt;
if (el.dataset.text !== undefined) el.dataset.text = txt; // typewriter 同步
});
document.documentElement.lang = LANG;
}
function initLang() {
let saved = null;
try { saved = localStorage.getItem('viz_lang'); } catch (e) {}
const browser = (navigator.language || 'zh').slice(0, 2);
if (saved && I18N_LANGS.includes(saved)) { LANG = saved; return; }
const euro = { fr: 'fr', de: 'de', es: 'es' };
if (browser === 'ja') LANG = 'ja';
else if (browser === 'ko') LANG = 'ko';
else if (browser === 'zh') LANG = (navigator.language.includes('TW') || navigator.language.includes('HK')) ? 'zh-TW' : 'zh';
else if (euro[browser]) LANG = euro[browser];
else LANG = 'en';
}
function setLang(l) {
LANG = l;
try {
localStorage.setItem('viz_lang', l);
localStorage.setItem('viz_scroll', String(window.scrollY || 0));
} catch (e) {}
location.reload();
}
(function restoreScroll() {
try {
const y = localStorage.getItem('viz_scroll');
if (y) setTimeout(() => window.scrollTo(0, parseInt(y, 10) || 0), 50);
} catch (e) {}
})();
/* ============ 常量 ============ */
const NS = "http://www.w3.org/2000/svg";
const C_ACCENT = "#d97757", C_INFO = "#4c9aff", C_SUCC = "#4a9d6a", C_WARN = "#d9a257", C_ERR = "#e0524a";
const DIM = "#71717a";
function svgEl(id){ return document.getElementById(id); }
/* 柱体渐变 defs(同色系顶亮底暗模拟光照) */
function ensureGradients(svg) {
if (!svg || svg.querySelector('defs#viz-grads')) return;
const defs = document.createElementNS(NS, 'defs');
defs.setAttribute('id', 'viz-grads');
const specs = [
['g-bar', '#e58a68', '#b95a3e'],
['g-info', '#6db3ff', '#3d7fd1'],
['g-warn', '#e8b468', '#c18f42'],
];
for (const [id, top, bottom] of specs) {
const lg = document.createElementNS(NS, 'linearGradient');
lg.setAttribute('id', id); lg.setAttribute('x1', '0'); lg.setAttribute('y1', '0'); lg.setAttribute('x2', '0'); lg.setAttribute('y2', '1');
const s1 = document.createElementNS(NS, 'stop'); s1.setAttribute('offset', '0'); s1.setAttribute('stop-color', top);
const s2 = document.createElementNS(NS, 'stop'); s2.setAttribute('offset', '1'); s2.setAttribute('stop-color', bottom);
lg.appendChild(s1); lg.appendChild(s2); defs.appendChild(lg);
}
svg.insertBefore(defs, svg.firstChild);
}
/* ============ 浮窗(hover 跟随 + 点击锁定) ============ */
const tip = document.getElementById("tl-tip");
let tipLocked = false;
const escHtml = s => String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
function tipHTML(title, meta, desc, links){
return `<div class="tt-title">${escHtml(title)}</div>` +
`<div class="tt-meta">${escHtml(meta)}</div>` +
(desc ? `<div class="tt-desc">${escHtml(desc)}</div>` : "") +
(links && links.length ? `<div class="tt-links">${links.map(l=>`<a href="${l[1]}" target="_blank" rel="noopener">${l[0]}</a>`).join("")}</div>` : "");
}
function moveTip(e){
if (tipLocked) return;
const tw = tip.offsetWidth || 300, th = tip.offsetHeight || 160;
let lx = e.clientX + 14, ly = e.clientY + 14;
if (lx + tw > window.innerWidth - 8) lx = e.clientX - tw - 14;
if (ly + th > window.innerHeight - 8) ly = e.clientY - th - 14;
tip.style.left = lx + "px"; tip.style.top = ly + "px";
}
function showTip(html, e){ tip.innerHTML = html; tip.style.display = "block"; moveTip(e); }
function hideTip(){ if (tipLocked) return; tip.style.display = "none"; }
document.addEventListener("click", (e)=>{
if (tipLocked && !tip.contains(e.target)) { tipLocked = false; tip.style.display = "none"; }
});
function bindBar(g, ev){
g.addEventListener("mouseenter", (e)=> showTip(ev.tipHtml || tipHTML(ev.name, ev.meta, ev.desc, ev.links), e));
g.addEventListener("mousemove", moveTip);
g.addEventListener("mouseleave", hideTip);
g.addEventListener("click", (e)=>{ e.stopPropagation(); tipLocked = true; });
}
/* ============ 柱状图(生长动画 + 交互) ============ */
function barChart(svg, items, {color=C_ACCENT, h=220, pad=30, barColor=null, cls="bar"}={}){
ensureGradients(svg);
const W=1000, H=h;
const max = Math.max(...items.map(i=>i.v));
const n = items.length;
const bw = (W - pad*2) / n;
for (const f of [.25, .5, .75]) {
const gl = document.createElementNS(NS,'line');
gl.setAttribute('x1',pad); gl.setAttribute('y1',H-30-(H-70)*f); gl.setAttribute('x2',W-pad); gl.setAttribute('y2',H-30-(H-70)*f);
gl.setAttribute('stroke','rgba(255,255,255,.045)'); gl.setAttribute('stroke-width',1);
svg.appendChild(gl);
}
const base = document.createElementNS(NS,'line');
base.setAttribute('x1',pad); base.setAttribute('y1',H-30); base.setAttribute('x2',W-pad); base.setAttribute('y2',H-30);
base.setAttribute('stroke','rgba(255,255,255,.14)'); base.setAttribute('stroke-width',1.5);
svg.appendChild(base);
items.forEach((item, i) => {
const bh = (item.v/max) * (H - 70);
const x = pad + i*bw + bw*0.15, y = H - 30 - bh;
const g = document.createElementNS(NS,"g");
const r = document.createElementNS(NS,"rect");
r.setAttribute("x",x); r.setAttribute("y",y); r.setAttribute("width",bw*0.7); r.setAttribute("height",bh);
r.setAttribute("rx",4);
r.setAttribute("class", cls);
r.style.transformOrigin = "bottom";
r.style.animationDelay = (i * 70) + "ms";
r.style.setProperty("--i", i);
const bc = barColor ? (typeof barColor === "function" ? barColor(item) : barColor) : color;
if (bc === '#d97757') r.setAttribute("fill", "url(#g-bar)");
else if (bc === '#4c9aff') r.setAttribute("fill", "url(#g-info)");
else if (bc === '#d9a257') r.setAttribute("fill", "url(#g-warn)");
else r.setAttribute("fill", bc);
g.appendChild(r);
const t = document.createElementNS(NS,"text");
t.setAttribute("x",x+bw*0.35); t.setAttribute("y",y-6); t.setAttribute("text-anchor","middle");
t.setAttribute("fill","#8b8b93"); t.setAttribute("font-size",10.5);
t.setAttribute("font-family","var(--mono)"); t.style.fontVariantNumeric = "tabular-nums";
t.textContent = item.v;
g.appendChild(t);
const t2 = document.createElementNS(NS,"text");
t2.setAttribute("x",x+bw*0.35); t2.setAttribute("y",H-10); t2.setAttribute("text-anchor","middle");
t2.setAttribute("fill",DIM); t2.setAttribute("font-size",10); t2.setAttribute("font-family","var(--mono)");
t2.textContent = item.label;
g.appendChild(t2);
if (item.note) {
const n2 = document.createElementNS(NS,"text");
n2.setAttribute("x",x+bw*0.35); n2.setAttribute("y",y-18); n2.setAttribute("text-anchor","middle");
n2.setAttribute("fill", C_SUCC); n2.setAttribute("font-size",9);
n2.textContent = item.note;
g.appendChild(n2);
}
bindBar(g, item);
svg.appendChild(g);
});
}
/* ============ 各图渲染 ============ */
barChart(svgEl("months"), D.months.map(m=>({
label:m.m.slice(2), v:m.v,
name:m.m+" "+m.label, meta:m.m+" · "+m.v+" 条提交 · "+m.label,
desc:m.desc, links:[["账本详细", m.link]]
})), {h:220});
{
const svg = svgEl("weeks"), W=1000, H=290, pad=30;
const items = D.weeks;
const max = Math.max(...items.map(i=>i.v));
const n = items.length, bw=(W-pad*2)/n;
items.forEach((item,i)=>{
const bh=(item.v/max)*(H-80);
const x=pad+i*bw+bw*0.2, y=H-40-bh;
const color = item.v>150 ? C_ERR : item.v>80 ? C_WARN : item.v>20 ? C_ACCENT : DIM;
const g = document.createElementNS(NS,"g");
const r = document.createElementNS(NS,"rect");
r.setAttribute("x",x);r.setAttribute("y",y);r.setAttribute("width",bw*0.6);r.setAttribute("height",bh);
r.setAttribute("rx",3);r.setAttribute("fill",color);
r.setAttribute("class","bar"); r.style.transformOrigin = "bottom"; r.style.animationDelay = (i*70)+"ms";
g.appendChild(r);
const t = document.createElementNS(NS,"text");
t.setAttribute("x",x+bw*0.3);t.setAttribute("y",y-4);t.setAttribute("text-anchor","middle");
t.setAttribute("fill",DIM);t.setAttribute("font-size",9);t.setAttribute("font-family","var(--mono)");t.textContent=item.v;
g.appendChild(t);
if (item.note) {
const n2 = document.createElementNS(NS,"text");
n2.setAttribute("x",x+bw*0.3);n2.setAttribute("y",H-20);n2.setAttribute("text-anchor","middle");
n2.setAttribute("fill",C_SUCC);n2.setAttribute("font-size",9);n2.textContent=item.note;
g.appendChild(n2);
}
bindBar(g, {
name: item.w, meta: item.w + "