chore(repo): initialize team collaboration repository
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run

This commit is contained in:
2026-07-27 20:40:12 +08:00
commit c91a64fddb
109 changed files with 21121 additions and 0 deletions
+272
View File
@@ -0,0 +1,272 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="stylesheet" href="_ds/industry-22c874fd-9888-4e99-8234-d60d39b3d719/styles.css">
<script src="_ds/industry-22c874fd-9888-4e99-8234-d60d39b3d719/_ds_bundle.js"></script>
<style>
body{margin:0;background:#f2f2f3;min-width:1280px}
*{scrollbar-width:thin;scrollbar-color:var(--color-accent-300) transparent}
a{color:#5980a6}a:hover{color:#416180}
@keyframes omIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes omScan{to{background-position:28px 0}}
@keyframes omDraw{to{stroke-dashoffset:0}}
@keyframes omGrow{from{width:0}}
@keyframes omPulse{0%,100%{opacity:1}50%{opacity:.3}}
</style>
</helmet>
<dc-import name="ConsoleNav" active="evidence" hint-size="100%,92px"></dc-import>
<div data-screen-label="证据浏览器" style="max-width:1400px;margin:0 auto;padding:22px 24px 64px;font-family:var(--font-body);animation:omIn .5s cubic-bezier(.2,.7,.2,1) both">
<div style="display:flex;align-items:flex-end;gap:16px;margin-bottom:16px">
<div>
<div style="font-size:10px;letter-spacing:.18em;color:var(--color-accent-700);font-weight:500">04 · EVIDENCE</div>
<h2 style="margin:2px 0 0;font-size:30px">证据浏览器</h2>
<div class="text-muted" style="font-size:12.5px;margin-top:2px">失败时间线 · Evidence Bundle · 每个结论回链到原始证据</div>
</div>
<span style="margin-left:auto"></span>
<button class="btn btn-secondary" style="font-size:12.5px">创建复现任务</button>
<button class="btn btn-secondary" style="font-size:12.5px">生成缺陷草稿</button>
<button class="btn btn-primary" style="gap:7px">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline stroke-linejoin="round" stroke-linecap="round" points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
下载证据包 · 247 MB
</button>
</div>
<div style="display:grid;grid-template-columns:290px 1fr 330px;gap:18px;align-items:start">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">失败记录 · 近 7 天</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500)">3 / 214</span>
</div>
<input class="input" placeholder="搜索 run / 指纹 / 状态码…" style="font-size:12.5px;min-height:32px">
<div style="display:flex;flex-direction:column;gap:8px">
<sc-for list="{{ failures }}" as="f" hint-placeholder-count="3">
<div onClick="{{ f.pick }}" style="{{ f.itemStyle }}">
<div style="display:flex;align-items:center;gap:7px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:11.5px;font-weight:700">{{ f.run }}</span>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:9.5px;color:var(--color-neutral-500)">{{ f.when }}</span>
</div>
<div style="margin:5px 0 4px"><span style="{{ f.tagStyle }}">{{ f.code }}</span></div>
<div style="font-size:12px;line-height:1.45;color:var(--color-neutral-800)">{{ f.summary }}</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500);margin-top:4px">{{ f.meta }}</div>
</div>
</sc-for>
</div>
<div style="display:flex;gap:10px;font-size:10px;color:var(--color-neutral-600);border-top:1px solid var(--color-divider);padding-top:8px;flex-wrap:wrap">
<span><span style="display:inline-block;width:9px;height:8px;background:var(--color-accent-800);vertical-align:-1px"></span> DUT_*</span>
<span><span style="display:inline-block;width:9px;height:8px;background:repeating-linear-gradient(135deg,rgba(29,31,32,.35) 0 2px,transparent 2px 5px);border:1px solid var(--color-divider);vertical-align:-1px"></span> INFRA_*</span>
<span><span style="display:inline-block;width:9px;height:8px;border:1px dashed var(--color-neutral-500);vertical-align:-1px"></span> UNKNOWN</span>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:10px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:15px;font-weight:700">{{ selRun }}</span>
<span style="{{ selTagStyle }}">{{ selCode }}</span>
<span class="tag tag-outline" style="font-size:10px">指纹 {{ selFs }}</span>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;color:var(--color-neutral-600)">{{ selMeta }}</span>
</div>
<div style="font-size:13.5px;color:var(--color-neutral-800)">{{ selHeadline }}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-700)">
<span style="border:1px solid var(--color-divider);padding:2px 8px">HOST-L02 · Ubuntu 22.04 · 6.5.0-41</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">BIOS F26b</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">FW_3.3.0-rc2 · slot 2</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">nvme-cli 2.9.1 · fio 3.36</span>
<span style="border:1px solid var(--color-divider);padding:2px 8px">workflow v3 · sig 8e02…c4</span>
</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:2px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<h6 style="margin:0 0 10px;color:var(--color-neutral-700)">事件时间线 · TIMELINE</h6>
<div style="display:flex;flex-direction:column">
<sc-for list="{{ events }}" as="ev" hint-placeholder-count="8">
<div style="display:flex;gap:12px;position:relative;padding-bottom:14px">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;color:var(--color-neutral-500);width:58px;text-align:right;padding-top:2px;flex:none">{{ ev.t }}</span>
<div style="display:flex;flex-direction:column;align-items:center;flex:none;width:14px">
<span style="{{ ev.dotStyle }}"></span>
<span style="{{ ev.lineStyle }}"></span>
</div>
<div style="flex:1;margin-top:-2px">
<div style="display:flex;align-items:center;gap:8px">
<span style="{{ ev.laneStyle }}">{{ ev.lane }}</span>
<span style="{{ ev.titleStyle }}">{{ ev.title }}</span>
</div>
<div style="font-size:12px;color:var(--color-neutral-700);margin-top:2px;line-height:1.5">{{ ev.detail }}</div>
<sc-if value="{{ ev.links }}" hint-placeholder-val="{{ false }}">
<div style="display:flex;gap:6px;margin-top:5px;flex-wrap:wrap">
<sc-for list="{{ ev.linkList }}" as="lk" hint-placeholder-count="2">
<span style="font-family:ui-monospace,Menlo,monospace;font-size:10px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:2px 8px;cursor:pointer">{{ lk }}</span>
</sc-for>
</div>
</sc-if>
</div>
</div>
</sc-for>
</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:14px 16px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">AI 摘要 · 仅引用证据,不做最终判断</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-600)">置信度 0.82 · 本地模型</span>
</div>
<div style="font-size:13px;line-height:1.7;color:var(--color-neutral-800)">S4 唤醒后 DUT 未完成链路训练即被访问:kernel 在唤醒后 3s 内未出现 nvme 重枚举日志 <span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:1px 6px">kernel.log:L1042-1067</span>,而基线固件 A 平均 1.8s 完成 <span style="font-family:ui-monospace,Menlo,monospace;font-size:10.5px;border:1px solid var(--color-accent-400);color:var(--color-accent-700);padding:1px 6px">metrics.json#resume_enum</span>。与 FS-0091 簇内 11 次历史失败特征一致(均为 S4 唤醒 + rc 系列固件)。</div>
<div style="font-size:12px;color:var(--color-neutral-700);border:1px dashed var(--color-divider);padding:8px 10px">尚不能排除:主板 BIOS F26b 的 S4 电源时序差异(建议单变量验证:同盘同固件换 HOST-L01 复跑 20 循环)。</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:6px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<div style="display:flex;align-items:center;gap:8px">
<h6 style="margin:0;color:var(--color-neutral-700)">EVIDENCE BUNDLE</h6>
<span style="margin-left:auto;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:var(--color-neutral-500)">run-20260726-0007/</span>
</div>
<div style="display:flex;flex-direction:column;font-family:ui-monospace,Menlo,monospace;font-size:11px">
<sc-for list="{{ tree }}" as="n" hint-placeholder-count="12">
<div onClick="{{ n.toggle }}" style="{{ n.rowStyle }}">
<span style="{{ n.chevStyle }}">{{ n.chev }}</span>
<span>{{ n.name }}</span>
<sc-if value="{{ n.flag }}" hint-placeholder-val="{{ false }}">
<span style="font-size:8.5px;background:var(--color-accent-800);color:#f2f2f3;padding:1px 5px;letter-spacing:.06em">关键</span>
</sc-if>
<span style="margin-left:auto;color:var(--color-neutral-500);font-size:10px">{{ n.size }}</span>
</div>
</sc-for>
</div>
<div style="font-size:10.5px;color:var(--color-neutral-600);border-top:1px solid var(--color-divider);padding-top:8px;line-height:1.6">manifest 校验通过 · 42 文件 · SHA256 全部一致<br>生成组件 evidence-bus 0.9.4 · 不可覆盖</div>
</div>
<div class="card blueprint" style-hover="border-color:var(--color-accent-400);box-shadow:var(--shadow-sm)" style="transition:border-color .15s ease,box-shadow .15s ease;padding:12px;gap:8px">
<i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
<h6 style="margin:0;color:var(--color-neutral-700)">恢复过程 · RECOVERY.JSON</h6>
<div style="display:flex;flex-direction:column;font-family:ui-monospace,Menlo,monospace;font-size:10.5px">
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L1 Agent 软恢复</span><span style="color:#8a2e22">FAIL · 60s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L2 OS 通道重启</span><span style="color:#8a2e22">FAIL · 120s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid rgba(29,31,32,.08)"><span style="color:var(--color-neutral-600)">L3 带外 Reset</span><span style="color:var(--color-accent-700)">OK · 2m17s</span></div>
<div style="display:flex;justify-content:space-between;padding:4px 0"><span style="color:var(--color-neutral-600)">总耗时 / 介入</span><span>4m12s · 无人</span></div>
</div>
<a href="LiveRun.dc.html" style="font-size:11.5px">回放当时的实时视图 →</a>
</div>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;$preview&quot;:{&quot;width&quot;:1440,&quot;height&quot;:980}}">
class Component extends DCLogic {
state = { sel: 0, open: { environment: true, device: false, host: true, test: false, oob: true, recovery: false, summary: true } };
renderVals() {
const mono = { fontFamily: 'ui-monospace,Menlo,monospace' };
const tagOf = code => {
if (code.startsWith('DUT')) return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'var(--color-accent-800)', color: '#f2f2f3', letterSpacing: '.03em' };
if (code.startsWith('INFRA')) return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'repeating-linear-gradient(135deg,rgba(29,31,32,.12) 0 2px,transparent 2px 5px)', border: '1px solid var(--color-divider)', color: 'var(--color-neutral-800)', letterSpacing: '.03em' };
if (code === 'UNKNOWN') return { ...mono, fontSize: '10px', padding: '2px 8px', border: '1px dashed var(--color-neutral-500)', color: 'var(--color-neutral-700)', letterSpacing: '.03em' };
return { ...mono, fontSize: '10px', padding: '2px 8px', background: 'var(--color-neutral-200)', color: 'var(--color-neutral-800)', letterSpacing: '.03em' };
};
const F = [
{ run: 'run-20260726-0007', when: '昨日 02:29', code: 'DUT_ENUMERATION_FAILURE', fs: 'FS-0091', summary: 'S4 唤醒后 DUT 未枚举,链路未训练', meta: 'WS-03 · cycle 41/100 · FW_3.3.0-rc2', headline: '休眠唤醒后 nvme0n1 未出现;L1/L2 恢复失败,L3 带外 Reset 后设备恢复,任务在检查点冻结。' },
{ run: 'run-20260726-0011', when: '昨日 11:47', code: 'UNKNOWN', fs: 'FS-0102', summary: '校验超时且证据不足,等待工程师分类', meta: 'WS-02 · cycle 12/50 · FW_3.2.1', headline: 'verify_hash 超时,但主机、DUT 与网络信号均正常 — 证据不足,已标记待人工分类。' },
{ run: 'run-20260725-0018', when: '07-25 23:12', code: 'INFRA_HOST_OS_FAILURE', fs: 'FS-0088', summary: '蓝屏 DRIVER_POWER_STATE_FAILURE', meta: 'WS-01 · cycle 88/100 · FW_3.3.0-rc2', headline: 'Windows 蓝屏(电源状态驱动),判定为基础设施故障,不计入 DUT 结论;L4 AC 循环后恢复。' }
];
const failures = F.map((f, i) => ({
...f,
pick: () => this.setState({ sel: i }),
tagStyle: tagOf(f.code),
itemStyle: {
border: i === this.state.sel ? '1px solid var(--color-accent-600)' : '1px solid var(--color-divider)',
background: i === this.state.sel ? 'var(--color-accent-100)' : 'transparent',
padding: '10px 11px', cursor: 'pointer', transition: 'border-color .15s ease, background .15s ease'
}
}));
const sel = F[this.state.sel];
const EV = {
0: [
{ t: '02:28:19', lane: 'TEST', kind: 'n', title: 'cycle 41 · host.hibernate → S4', detail: '前序 40 循环全部 PASS · 检查点 cycle 40 已写入', links: null },
{ t: '02:29:44', lane: 'HOST', kind: 'n', title: '唤醒完成 · resume 11.8s', detail: '唤醒源 RTC · Agent 心跳恢复正常', links: null },
{ t: '02:29:47', lane: 'DUT', kind: 'f', title: 'dut.verify_enumeration 失败', detail: 'nvme0n1 未出现 · PCIe 链路未完成训练 · 重试 3 次无效', links: ['host/kernel.log', 'host/device-enumeration.json'] },
{ t: '02:29:48', lane: 'EVID', kind: 'e', title: '失败现场取证(恢复之前)', detail: 'HDMI 帧 ×3 · dmesg 尾部 · SMART 不可读已记录 · lspci 快照', links: ['oob/screenshots/', 'device/error-log.json'] },
{ t: '02:30:21', lane: 'RCVR', kind: 'f', title: 'L1 Agent 软恢复 → 失败', detail: 'PCIe rescan 无效 · 设备节点仍缺失 (60s)', links: null },
{ t: '02:31:07', lane: 'HB', kind: 'f', title: '主机心跳丢失', detail: 'Agent 停止上报 · ping 不可达 · 判定 HOST_UNREACHABLE', links: ['oob/heartbeat.ndjson'] },
{ t: '02:31:40', lane: 'RCVR', kind: 'f', title: 'L2 OS 通道重启 → 失败', detail: 'SSH 超时 120s · 按策略 esc-default-v2 升级', links: null },
{ t: '02:33:02', lane: 'OOB', kind: 'r', title: 'L3 带外 Reset 已执行', detail: '光耦 CH2 · 脉冲 250ms · 供电动作已留痕', links: ['oob/power-actions.ndjson'] },
{ t: '02:35:19', lane: 'HOST', kind: 'r', title: '主机上线 · 指纹一致', detail: 'Agent 自启 · BIOS/OS/驱动与任务登记一致', links: null },
{ t: '02:35:24', lane: 'DUT', kind: 'r', title: 'DUT 重新枚举成功', detail: 'Gen4 x4 · 1.8s · SMART 正常 · 数据区未写入', links: ['device/smart-health.json'] },
{ t: '02:35:31', lane: 'SYS', kind: 'c', title: '检查点对账 · 任务冻结', detail: '物理状态 = cycle 41 起点 · 等待复跑决策(不自动重试破坏性步骤)', links: null },
{ t: '02:36:02', lane: 'SYS', kind: 'n', title: '失败指纹 FS-0091 已生成', detail: '与 11 个历史运行聚类 · 均为 S4 唤醒 + rc 系列固件', links: ['summary/failure-signature.json'] }
],
1: [
{ t: '11:47:12', lane: 'TEST', kind: 'f', title: 'data.verify_hash 超时', detail: '300s 未返回 · fio 进程存活 · IO 无错误计数', links: ['test/stderr.log'] },
{ t: '11:47:13', lane: 'EVID', kind: 'e', title: '现场取证', detail: '进程树 · 资源占用 · SMART 正常 · 网络正常', links: ['host/system.log'] },
{ t: '11:48:40', lane: 'RCVR', kind: 'r', title: 'L1 终止校验进程并重跑 → 通过', detail: '重跑一次即 PASS · 无法稳定复现', links: null },
{ t: '11:50:02', lane: 'SYS', kind: 'c', title: '标记 UNKNOWN · 等待分类', detail: '证据不足 · 建议补充 blktrace 后纳入观察名单', links: ['summary/ai-summary.md'] }
],
2: [
{ t: '23:12:44', lane: 'HOST', kind: 'f', title: 'Windows 蓝屏', detail: 'DRIVER_POWER_STATE_FAILURE (0x9F) · 冷启动循环第 88 轮', links: ['oob/screenshots/'] },
{ t: '23:12:45', lane: 'EVID', kind: 'e', title: 'HDMI 蓝屏帧留证', detail: 'OS 不可达状态下由带外控制器抓取', links: ['oob/screenshots/'] },
{ t: '23:14:10', lane: 'OOB', kind: 'r', title: 'L4 AC 电源循环', detail: 'L3 Reset 无效 → 整机断电 · 安全间隔 30s', links: ['oob/power-actions.ndjson'] },
{ t: '23:18:02', lane: 'SYS', kind: 'c', title: '判定 INFRA_HOST_OS_FAILURE', detail: '不计入 DUT 结论 · 计入基础设施误报率监控', links: ['summary/failure-signature.json'] }
]
}[this.state.sel];
const laneColors = { HB: '#8a2e22', OOB: 'var(--color-accent-800)', RCVR: '#8a2e22', TEST: 'var(--color-accent-700)', SYS: 'var(--color-neutral-600)', EVID: 'var(--color-neutral-600)', DUT: 'var(--color-accent-800)', HOST: 'var(--color-neutral-700)' };
const events = EV.map((e, i) => {
let dot = { width: '9px', height: '9px', borderRadius: '50%', border: '1.5px solid var(--color-neutral-400)', background: 'var(--color-bg)', flex: 'none', marginTop: '4px' };
if (e.kind === 'f') dot = { ...dot, border: '1.5px solid #b03d2e', background: '#f7e9e6' };
if (e.kind === 'r') dot = { ...dot, border: 'none', background: 'var(--color-accent)' };
if (e.kind === 'c') dot = { ...dot, borderRadius: 0, border: 'none', background: 'var(--color-accent-800)' };
if (e.kind === 'e') dot = { ...dot, border: '1.5px dashed var(--color-neutral-500)' };
return {
...e,
linkList: e.links || [],
dotStyle: dot,
lineStyle: { width: '1px', flex: 1, background: i === EV.length - 1 ? 'transparent' : 'var(--color-divider)', marginTop: '3px' },
laneStyle: { ...mono, fontSize: '9.5px', color: laneColors[e.lane] || 'var(--color-neutral-600)', letterSpacing: '.06em', minWidth: '34px' },
titleStyle: { fontSize: '13px', fontWeight: e.kind === 'f' || e.kind === 'r' ? 600 : 500, color: e.kind === 'f' ? '#8a2e22' : 'var(--color-text)' }
};
});
const DIRS = [
{ id: 'root', name: 'manifest.json', size: '4 KB', file: true, depth: 0 },
{ id: 'environment', name: 'environment/', size: '4 项', kids: [['host.json', '2 KB'], ['dut.json', '2 KB'], ['firmware.json', '1 KB'], ['tool_versions.json', '3 KB']] },
{ id: 'timeline', name: 'timeline/events.ndjson', size: '1.2 MB', file: true, depth: 0 },
{ id: 'device', name: 'device/', size: '4 项', kids: [['identify.json', '18 KB'], ['smart-health.json', '9 KB'], ['error-log.json', '22 KB'], ['telemetry.bin', '64 MB']] },
{ id: 'host', name: 'host/', size: '3 项', kids: [['system.log', '8.4 MB'], ['kernel.log', '2.1 MB', true], ['device-enumeration.json', '11 KB', true]] },
{ id: 'test', name: 'test/', size: '4 项', kids: [['stdout.log', '96 MB'], ['stderr.log', '2 KB'], ['metrics.json', '340 KB'], ['hashes.json', '88 KB']] },
{ id: 'oob', name: 'oob/', size: '4 项', kids: [['heartbeat.ndjson', '420 KB', true], ['power-actions.ndjson', '2 KB', true], ['temperature.csv', '180 KB'], ['screenshots/ ×9', '38 MB', true]] },
{ id: 'recovery', name: 'recovery/recovery.json', size: '6 KB', file: true, depth: 0, flag: true },
{ id: 'summary', name: 'summary/', size: '3 项', kids: [['failure-signature.json', '3 KB', true], ['ai-summary.md', '5 KB'], ['issue-draft.md', '4 KB']] }
];
const tree = [];
for (const d of DIRS) {
const isOpen = !!this.state.open[d.id];
tree.push({
name: d.name, size: d.size, flag: !!d.flag,
chev: d.file ? '·' : (isOpen ? '▾' : '▸'),
chevStyle: { width: '12px', color: d.file ? 'var(--color-neutral-400)' : 'var(--color-accent-700)', flex: 'none' },
toggle: d.file ? null : () => this.setState(s => ({ open: { ...s.open, [d.id]: !s.open[d.id] } })),
rowStyle: { display: 'flex', alignItems: 'center', gap: '6px', padding: '4px 4px', cursor: d.file ? 'default' : 'pointer', borderBottom: '1px solid rgba(29,31,32,.06)' }
});
if (!d.file && isOpen) for (const [nm, sz, flag] of d.kids) tree.push({
name: nm, size: sz, flag: !!flag, chev: '', toggle: null,
chevStyle: { width: '12px', flex: 'none' },
rowStyle: { display: 'flex', alignItems: 'center', gap: '6px', padding: '3px 4px 3px 22px', color: 'var(--color-neutral-700)', borderBottom: '1px solid rgba(29,31,32,.04)' }
});
}
return {
failures, events, tree,
selRun: sel.run, selCode: sel.code, selFs: sel.fs, selMeta: sel.meta, selHeadline: sel.headline,
selTagStyle: tagOf(sel.code)
};
}
}
</script>
</body>
</html>