"""Render a source-linked engineering audit from explicit inputs."""
import csv
import hashlib
import html
import json
import sys
from collections import Counter
from pathlib import Path

sys.stdout.reconfigure(encoding="utf-8")
HERE = Path(__file__).resolve().parent
DEVICE = HERE.parent
calc = json.loads((HERE / "system_calculations.json").read_text(encoding="utf-8"))
register = json.loads((HERE / "issues.json").read_text(encoding="utf-8"))
rows = register["rows"]
assert len({r[0] for r in rows}) == len(rows)
assert all(len(r) == len(register["columns"]) for r in rows)

sources = {
    "USER": dict(title="用户输入与本轮确认", note="环境箱6×4×2.5m、500kg/m²；薄不锈钢面层问题；可搬运与现场使用需求；本轮已确认以稳态轮载摩擦为主。"),
    "L31": dict(title="v0.31旧实心轮CAD／载荷／验证记录", local="common_support_v0_31", note="参考总质量100.15kg、旧轮125×25、局部轮架数值比较；实际质量／主传感器安装／冷态配合与制造发布未闭合。"),
    "L32": dict(title="v0.32压力与静态稳定比较", local="pneumatic_rebaseline_v0_32/calculation.json", note="四脚等刚度点支承，居中N=Fx100N所需净平移μ约0.17含假设1.5余量；非实测。"),
    "L33": dict(title="v0.33充气轮规格与接口影响单", local="pneumatic_wheel_selection_v0_33/wheel_brief.json", note="推荐K909200×50；轮辋／轴接口、新轮架CAD、确切国内SKU仍待闭合。"),
    "L27": dict(title="v0.27功能时序（沿用至v0.31）", local="common_support_v0_31/control_task_contract.json", note="S03悬空加速→S04缓接触→S05短时测量→S06健康卸载／停转；功能要求而非已运行PLC。"),
    "L26": dict(title="v0.26器件候选与配套约束", local="component_closure_v0_26/selection_contract.json", note="电机有型号候选；驱动／主六轴整套／−30℃杆制动仍需完成准确配套。"),
    "L11": dict(title="v0.11测量与数据合同", local="sensor_acquisition_v0_11/measurement_contract.json", note="1kHz、±0.2／±0.5N均为设计比较目标；带宽、时延与整机准确度未实测。"),
    "L12": dict(title="v0.12保护约束与待定参数", local="protection_wiring_v0_12/protection_contract.json", note="最大推力、响应／停止、故障峰值及独立保护未验收；50／100N不能充当保护阈值。"),
    "C34": dict(title="本轮可复算解析比较", local="system_calculations.json", note="脚底压力／偏心扭转分配、局部转动惯量情景、接触时延、热功与低载荷信号量级。"),
    "S1": dict(title="SRI M3725A Rev.A，第1页", url="https://www.srisensor.com/uploads/M3725A.pdf", note="Fx/Fy200N、Fz400N、50Nm；0.5%FS非线性；工作−40…100℃、补偿10…70℃；6×6矩阵，6.5V、CAN／串口。已查看本地保存的原厂单页图。"),
    "S2": dict(title="SENYDA SYD143厂家页", url="https://www.senyda.cn/archives/2182", note="本地原厂页快照的SYD143-0310L30行：3.1kW、3000rpm、9.8Nm；无制动惯量6.69×10⁻⁴kg·m²、质量10.5kg、长度182mm。厂家页当前读取不稳定，使用保留快照；惯量未替代未知整轮惯量。"),
    "S3": dict(title="Synapticon STO官方说明", url="https://doc.synapticon.com/circulo_safe_motion/smm/sto.htm", note="撤转矩不消除系统惯性与悬挂载荷，需要相应保持／制动措施；复位不应意外重启。仅用作功能原理参考，不是本机选型。"),
    "S4": dict(title="NI动态采集官方说明", url="https://www.ni.com/en/shop/data-acquisition-and-control/sound-and-vibration/dynamic-signal-acquisition-fundamentals.html", note="采样、带宽、滤波和抗混叠分别核对；不以刷新率代替真实采样。"),
    "S5": dict(title="SKF低温轴承润滑技术文章", url="https://evolution.skf.com/us/low-temperatures/", note="低温润滑影响摩擦与损耗，需按真实轴承与润滑组合评价；没有从文章指定本机脂牌号。"),
    "S6": dict(title="Gates GTX产品条件实例", url="https://www.gates.com/gb/en/power-transmission/synchronous-belts/rubber-synchronous-belts.p.9326-000000-000000.v.9327-50214.html", note="该页面实例为8M／14M系列，标温度−30…100℃；不能据此给本机5M带赋予同样能力。用于说明要查准确系列。"),
    "S7": dict(title="Kenda K909原厂规格表", url="https://wheelchair.kendatire.com/en-us/find-a-tire/wheelchair/powerchair-scooter/k909/", note="109090301B0、8×2／200×50、BLK4P TT、PSI50；未从该标注推出整轮冷态高速资格。"),
    "S8": dict(title="Siemens STO官方说明", url="https://docs.tia.siemens.cloud/r/fail_safe_modules_manual_collection_enus_20/device-specific-information/et-200sp-fail-safe-modules/fail-safe-technology-module-f-tm/f-tm-servodrive-st-1x24v..48v/safety-functions-integrated-in-the-drive/basic-safety-information/safety-integrated-functions/safe-torque-off-sto", note="STO下驱动滑行且不提供电气隔离；本机检修仍需按实际驱动隔离／消能。"),
}
assert all(s in sources for r in rows for s in r[-1].split(","))
(HERE / "sources.json").write_text(json.dumps(sources, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
with (HERE / "system_issues.csv").open("w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(register["columns"])
    writer.writerows(rows)

source_html = "".join(f"<li id='source-{sid}'><b>{sid} · {html.escape(s['title'])}</b>：{html.escape(s['note'])}" +
                      (f" <a href='{html.escape(s['url'])}' target='_blank' rel='noopener'>原始来源</a>" if "url" in s else "") + "</li>"
                      for sid, s in sources.items())
physics = calc["floor"]
dynamics = [x for x in calc["partial_dynamics"] if x["assumed_wheel_inertia_kg_m2"] == 0.01]
dynamic_rows = "".join(f"<tr><td>{x['ramp_s']:.2f}s</td><td>{x['airborne_frame_reaction_torque_magnitude_Nm']:.2f} Nm</td><td>{x['airborne_motor_acceleration_torque_Nm']:.2f} Nm</td></tr>" for x in dynamics)
counts = dict(Counter(r[2] for r in rows))
embedded = json.dumps(dict(register=register, sources=sources), ensure_ascii=False).replace("</", "<\\/")
doc = f"""<!doctype html><html lang='zh-CN'><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'>
<title>冰雪摩擦设备系统复核 v0.34</title>
<style>
body{{font:16px/1.8 'Microsoft YaHei',sans-serif;background:#f2f5f7;color:#23313c;margin:0}}main{{max-width:1100px;margin:auto;padding:26px}}
h1{{font-size:29px;line-height:1.4}}h2{{font-size:22px;margin:0 0 14px}}h3{{font-size:18px}}section{{background:white;border-radius:12px;padding:25px;margin:18px 0}}p{{margin:10px 0}}
table{{border-collapse:collapse;width:100%;font-size:15px}}td,th{{padding:11px;border-bottom:1px solid #dce3e8;text-align:left;vertical-align:top}}th{{background:#edf3f6}}a{{color:#176a83}}
.small{{font-size:14px;color:#53646f}}.scroll{{overflow:auto}}.lead{{font-size:19px}}img{{max-width:100%;height:auto}}label{{display:inline-flex;flex-direction:column;margin:6px 12px 6px 0;gap:5px}}
select,input,button{{font:inherit;padding:8px;border:1px solid #b8c7cf;border-radius:6px;background:white;color:#23313c}}input{{max-width:100%;box-sizing:border-box}}button{{cursor:pointer}}
.issue{{margin:12px 0;border:1px solid #dce4e8;border-radius:8px;padding:12px}}.issue summary{{font-weight:600;cursor:pointer}}.issue p{{margin:8px 0}}.meta{{font-size:14px;color:#596b78}}
.P0{{border-left:5px solid #ad5b3e}}.P1{{border-left:5px solid #b58a30}}.P2{{border-left:5px solid #367b9b}}.P3{{border-left:5px solid #78917e}}
ul,ol{{padding-left:24px}}li{{margin:8px 0}}#sourceList li{{font-size:14px}}@media(max-width:600px){{main{{padding:12px}}section{{padding:16px}}h1{{font-size:24px}}table{{font-size:14px}}}}
@media print{{body{{background:white}}main{{max-width:none;padding:0}}section{{border:1px solid #ddd;border-radius:0;break-inside:avoid}}#filters,button{{display:none}}.issue{{break-inside:avoid}}}}
</style><main>
<p class='small'>设备线 · 2026-10-15 · 系统复核v0.34 · 主CAD仍是v0.31，充气轮规格草案v0.33</p>
<h1>现有方案可以解释反力问题，但还不能据此确认箱底承载、便携和动态运行</h1>
<section><p class='lead'>首版目标已确认：稳定轮载下测固定点滑动抗力；加减速用于建立和结束工况。同行关于反力、薄板承载和加减速支撑的意见都需要落实。</p>
<p>旧模型已有总装、部分零件图、传感与功能时序，以及局部静力比较。当前阶段适合设计评审／询价；新充气轮尚未整合进CAD，地板结构、完整冷态测力与失电动作尚未取得实机证据。</p>
<p>本轮完成12类、<b>{len(rows)}项</b>具体排查，每项有影响、现状与关闭方法。P0定型前{counts['P0']}项、P1相关动作前{counts['P1']}项、P2正式数据前{counts['P2']}项、P3对应拓展前{counts['P3']}项。它们按工作包推进；扩展项不全部塞进首版。</p></section>

<section><h2>逐条回应同行提出的问题</h2><div class='scroll'><table><tr><th>问题</th><th>当前能支持的回答</th><th>还要落实什么</th></tr>
<tr><td>在环境箱做吗，放得下吗？</td><td>计划在6×4×2.5m箱内、关门后完成运动。旧模型外包络约750×900×957mm，几何上有布置空间。</td><td>新轮架、分载支承、门净空、检修和馈通接口还要复核。</td></tr>
<tr><td>环境箱有承载要求，薄不锈钢底板好不好放？</td><td>500kg/m²是用户提供的面积指标，不能直接证明四个小脚、移动轮或锚点可用。</td><td>地板局部承载、底部骨架与可拆分载支承；现阶段不能确认直接落脚可用。</td></tr>
<tr><td>没有固定，荷载是不是上不去？</td><td>开式地面测试确实受Mg−N及稳定性限制；泵或电动执行器不能替代反力。首版50—100N只有约5—10kgf量级。</td><td>精确质量、水平阻力与峰值未测；轮载小不等于所有结构作用都小。</td></tr>
<tr><td>要么配重，要么与结构固定？</td><td>这两条都是反力路线。可拆固定不等于永久留在箱内；但必须有真实承力点。</td><td>当前没有已确认锚点，不把薄箱板当反力结构。另比较闭合试件底板路线。</td></tr>
<tr><td>加减速需要稳定支撑？</td><td>正确。悬空时虽没有试样Fx，旋转加速度仍产生反作用力矩与振动。</td><td>完整惯量、加减速斜率、峰值和实测振动；正常卸载后减速与故障停机分开。</td></tr>
<tr><td>理论上便携，能否带去外场？</td><td>现图有拆分思路，但旧参考总质量约100kg，不能称为已经验证的便携整机。</td><td>实称单件／总套质量、包装与提手、重装时间和重装后的测量一致性；核外场供电／支承。</td></tr>
<tr><td>加载调小、主要看加减速可以吗？</td><td>用户已确认首版仍测稳态摩擦。减载可以降低接触阻力，但也使有效力信号变小。</td><td>不把“低载荷”当作默认解法；先核需要分辨的抗滑差异和测量误差。</td></tr>
</table></div></section>

<section><h2>本轮补充的关键量级</h2>
<h3>1. 箱底：总重量与局部压力是两个问题</h3>
<p>旧参考模型100.15kg，四脚名义面积合计0.09m²。悬空时平均脚底压强约<b>{physics['airborne_mean_foot_pressure_Pa']/1000:.1f}kPa</b>；居中N=Fx100N时最大单脚约290N，局部名义压强约<b>{physics['central_max_foot_pressure_Pa']/1000:.1f}kPa</b>。旧姿态比较最大单脚约384N，对应约17.1kPa。用户500kg/m²换算为约<b>{physics['equivalent_uniform_pressure_Pa']/1000:.1f}kPa</b>。</p>
<p>这不是把均布额定值当作点载荷上限来判失败，而是说明需要核承力骨架和局部变形。按均布数字简单摊开384N，方形面积边长约280mm；它没有计分载板自重、弯曲、真实接触与动态，不能直接发出“280mm板即可”的制造要求。</p>
<p>降低轮载不会减少整机给箱底的总重量；开式装置中，它反而把更多重量从试样转回支脚。铺雪重量、配重、附件、搬运人员与运输轮的局部作用也需单列。</p>
<h3>2. 配重：净平移、偏心扭转与脚下接触要一起核</h3>
<p>旧居中N=Fx100N、1.5力余量的净平移μ需求约0.17。本轮加入Y偏150mm产生的15Nm绕竖轴力矩，构造一个只含纵向脚剪力且能满足平衡的分配，比较需要μ约<b>{calc['yaw_support']['worst']['sufficient_mu_with_assumed_1_5_reserve']:.3f}</b>。这是四脚等刚度、均一脚底μ的充分分配，不是实测系数或最小值。</p>
<p>同样的100N轮载／100N水平阻力与1.5力余量，仅按平移防滑：μ0.2需约87kg总质量；μ0.1需约163kg。实际先测脚垫／底面和机器质量，再定配重。防滑用质量下界；压地板和构件用质量上界。</p>
<h3>3. 加减速：减轮载不消除转子反作用力矩</h3>
<p>Ø200mm轮的60km/h对应约1592rpm、166.7rad/s。只比较假设轮组J=0.01kg·m²与厂家无制动电机转子J=0.000669kg·m²、1.5:1传动，悬空从零升速的局部贡献如下：</p>
<div class='scroll'><table><tr><th>等角加速时间</th><th>机架反作用力矩幅值</th><th>电机加速转矩（假设η0.9）</th></tr>{dynamic_rows}</table></div>
<p>这里未包括全部带轮、编码器、皮带与损耗，不能据此定实际最短升速时间。驱动／动能折算用J轮+i²J电机；平行同向转子的整机角动量变化用J轮+iJ电机，不能混用倍率。完整惯量确定后再核斜率；先用平滑、受限加减速验证，而不是追求最快动作。</p>
<p>若仅有1g在100mm半径的不平衡，60km/h示例激励约2.8N；其轮频约26.5Hz。对于只有5N的摩擦信号，这已值得辨识。</p>
<h3>4. 小轮载：可降低接触阻力，但可能测不清</h3>
<p>候选M3725A的Fx量程200N、目录非线性0.5%FS，对应单项量级1N；N50N且假设μ0.1时，目标Fx只有5N。该传感器工作−40…100℃，温度补偿10…70℃没有覆盖−30℃。这不是完整整机误差预算，需要低温装配后的局部标定／整链验证，才能选择合理轮载与准确度。</p>
<h3>5. 加载轴与短时发热仍不可忽略</h3>
<p>旧移动部分约43.7kg，N50N时理想托举力仍约378N。失电若落10mm，重力释放约4.3J；实际接触峰值取决于落移、刚度与停止方式，不能拿100N目标作上限。</p>
<p>Fx50N、60km/h、5秒摩擦机械功约4.2kJ；轮胎／试样的吸热比例未知。它会和磨耗、压陷一起改变接触区。摩擦耗散能与停止时的旋转储能需分别核算。</p></section>

<section><h2>反力路线值得重新比较</h2><img src='reaction_paths.svg' alt='开式地面测试与闭合试件底板的受力路径对照'>
<div class='scroll'><table><tr><th>路线</th><th>适合什么</th><th>代价与未闭合项</th></tr>
<tr><td>开式地面测试：现有方向</td><td>直接测箱内或外场已有冰雪表面；配重／脚垫或确认的可拆固定提供反力。</td><td>质量由N、Fx、偏心和脚底条件决定；箱底局部承载与不同外场基础分别核。</td></tr>
<tr><td>闭合机架＋可拆试件底板</td><td>轮子与试样都不平移；N与稳态Fx在机器内部闭合，有减少外部配重需求的机会。</td><td>要按模块试件制样，重画底部支承／框架；不能直接测任意大面天然冰雪。悬空加速反转矩仍存在。</td></tr>
</table></div><p>建议下一项构型比较围绕“是否允许室内用可拆试件底板”展开。同一测量模块可保留外场接口思路，但首版不必同时做齐两套基础。</p></section>

<section id='first'><h2>将124项排查收敛为六个工作包</h2><ol>
<li><b>研究与测量边界：</b>固定点稳态滑动、需要分辨的最小差异、Fx/N主测量链；明确给实验线的真实能力表。</li>
<li><b>反力与场地：</b>比较开式／闭合式；查箱底局部承载和馈通；确定分载、脚下防滑与配重／可拆固定边界。</li>
<li><b>新轮组与动态：</b>K909轮胎＋真实轮辋／驱动接口、完整惯量和冷态转速；更新梁、电机、护罩和传动空间。</li>
<li><b>加载与测力：</b>准确六轴整套、安装面、低温标定；接触刚度／时延、Z真实行程、失电承托与独立保护。</li>
<li><b>本地控制与记录：</b>落实现有时序、通道同步、原始记录、异常退出与不自动重启；先低能量台架。</li>
<li><b>运输与验收：</b>实机模块重量与承托、反复拆装／重装标定；常温→低温→速度阶梯，再考虑外场迁移。</li>
</ol><p>给厂家的下一份材料应是：当前主构型、载荷与动态工况、准确采购件接口、制造图修改单和分阶段验收任务。v0.31旧实心轮STEP可作参考，不能直接与v0.33轮胎规格拼成加工发布包。</p></section>

<section><h2>可用于与同行／导师交流的说明</h2>
<p>“是在环境箱内做，首版先测稳定轮载下的摩擦，轮载暂按50—100N。没有外部固定时，确实要靠自重和配重提供反力；加减速也要单独核惯量和支撑，不能只看轮载小。现在还要补箱底局部承载、冷态测量准确度和失电动作，不能直接把小脚放在薄板上就认为没问题。为了便携，我们会比较可拆试件底板组成闭合反力框的路线；如果仍直接测地面冰雪，则继续用模块化配重／可拆固定方案。”</p></section>

<section id='register'><h2>完整问题清单</h2><div id='filters'>
<label>类别<select id='category' aria-label='按类别筛选'><option value=''>全部类别</option></select></label>
<label>处理阶段<select id='priority' aria-label='按处理阶段筛选'><option value=''>全部阶段</option><option value='P0'>P0 · 定型前</option><option value='P1'>P1 · 相关动作前</option><option value='P2'>P2 · 正式数据前</option><option value='P3'>P3 · 拓展前</option></select></label>
<label>关键词<input id='query' aria-label='搜索问题' placeholder='例如：反力、地板、低温、同步'></label>
<p><button id='expand' type='button'>展开筛选结果</button> <button id='collapse' type='button'>收起筛选结果</button> <button id='reset' type='button'>清除筛选</button></p>
</div><p id='count' aria-live='polite'></p><div id='issues'></div></section>

<section><h2>依据与本轮验证</h2><p>本轮重新读取当前资料并完成解析量级比较；没有新跑轮架有限元，没有实现PLC程序，没有样机／箱底实测。当前CAD、局部FE、器件资料、原理计算和将来的样机验收分别保留证据。</p>
<ul id='sourceList'>{source_html}</ul><p><a href='system_calculations.json'>完整计算与假设</a> · <a href='issues.json'>问题清单JSON</a> · <a href='system_issues.csv'>问题清单CSV</a> · <a href='sources.json'>来源登记</a> · <a href='calculate_audit.py'>核算脚本</a> · <a href='build_audit.py'>评审页生成脚本</a></p></section></main>
<script id='data' type='application/json'>{embedded}</script>
<script>
const data=JSON.parse(document.querySelector('#data').textContent), all=data.register.rows;
const category=document.querySelector('#category'),priority=document.querySelector('#priority'),query=document.querySelector('#query'), container=document.querySelector('#issues');
for(const c of [...new Set(all.map(r=>r[1]))]){{const o=document.createElement('option');o.value=o.textContent=c;category.append(o);}}
function render(){{const text=query.value.trim().toLowerCase();const selected=all.filter(r=>(!category.value||r[1]===category.value)&&(!priority.value||r[2]===priority.value)&&(!text||r.join(' ').toLowerCase().includes(text)));container.replaceChildren();
for(const r of selected){{const detail=document.createElement('details');detail.className='issue '+r[2];detail.dataset.id=r[0];const summary=document.createElement('summary');summary.textContent=r[0]+' · '+r[5];detail.append(summary);const meta=document.createElement('p');meta.className='meta';meta.textContent=r[1]+' / '+r[2]+' '+data.register.priorities[r[2]]+' / '+r[3]+' / '+r[4];detail.append(meta);
for(const [label,value] of [['影响／依据',r[6]],['关闭方法',r[7]]]){{const p=document.createElement('p');const b=document.createElement('b');b.textContent=label+'：';p.append(b,document.createTextNode(value));detail.append(p);}}
const refs=document.createElement('p');refs.className='small';refs.append('依据：');for(const s of r[8].split(',')){{const a=document.createElement('a');a.href='#source-'+s;a.textContent=s;refs.append(a,' ');}}detail.append(refs);container.append(detail);}}
document.querySelector('#count').textContent='显示 '+selected.length+' / '+all.length+' 项。';}}
category.addEventListener('change',render);priority.addEventListener('change',render);query.addEventListener('input',render);
document.querySelector('#expand').onclick=()=>container.querySelectorAll('details').forEach(d=>d.open=true);
document.querySelector('#collapse').onclick=()=>container.querySelectorAll('details').forEach(d=>d.open=false);
document.querySelector('#reset').onclick=()=>{{category.value=priority.value=query.value='';render();}};render();
</script></html>"""
(HERE / "audit_review.html").write_text(doc, encoding="utf-8")
svg = """<svg xmlns="http://www.w3.org/2000/svg" width="1020" height="460" viewBox="0 0 1020 460">
<defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10Z" fill="context-stroke"/></marker></defs>
<rect width="1020" height="460" fill="#f4f7f9"/><g font-family="Microsoft YaHei,sans-serif" font-size="17" fill="#23313c">
<text x="24" y="32" font-size="20">开式地面测试</text><text x="535" y="32" font-size="20">闭合机架＋可拆试件底板</text>
<g fill="#bac6ce"><rect x="75" y="80" width="20" height="260"/><rect x="395" y="80" width="20" height="260"/><rect x="75" y="80" width="340" height="22"/><rect x="585" y="80" width="20" height="260"/><rect x="905" y="80" width="20" height="260"/><rect x="585" y="80" width="340" height="22"/></g>
<g fill="#507e90"><rect x="232" y="102" width="25" height="73"/><rect x="742" y="102" width="25" height="73"/></g>
<g fill="#d1e6ea" stroke="#407b8d" stroke-width="3"><circle cx="245" cy="222" r="47"/><circle cx="755" cy="222" r="47"/></g>
<g fill="#dfece8" stroke="#77a394"><rect x="173" y="269" width="144" height="46"/><rect x="683" y="269" width="144" height="46"/></g>
<rect x="583" y="315" width="344" height="23" fill="#bac6ce"/><path d="M50 340H440M560 340H950" stroke="#52646e" stroke-width="3"/>
<g marker-end="url(#arrow)" stroke-width="3"><path d="M245 269v-37" stroke="#ad6745"/><path d="M755 269v-37" stroke="#ad6745"/><path d="M755 281v45" stroke="#ad6745"/><path d="M212 269h-49" stroke="#3d7ea5"/><path d="M722 269h-49" stroke="#3d7ea5"/><path d="M792 285h49" stroke="#3d7ea5"/><path d="M85 360v-39M405 360v-39M595 360v-39M915 360v-39" stroke="#56816b"/></g>
<text x="270" y="251" fill="#ad6745">N</text><text x="790" y="251" fill="#ad6745">N</text><text x="202" y="260" fill="#3d7ea5">Fx</text><text x="725" y="260" fill="#3d7ea5">Fx</text>
<text x="60" y="394">静态脚反力合计 R＝Mg−N</text><text x="570" y="394">稳态接触N／Fx内部闭合，R＝Mg</text>
<text x="60" y="426" font-size="15">水平抗力依靠脚底摩擦／确认的外部固定。</text><text x="570" y="426" font-size="15">悬空升速仍须抵抗角动量变化产生的反转矩。</text>
<text x="24" y="454" font-size="13">受力原理示意，未发布新底板／机架尺寸；两种路线的试样在测试时均保持不动。</text></g></svg>"""
(HERE / "reaction_paths.svg").write_text(svg, encoding="utf-8")
manifest = dict(version="v0.34", date="2026-10-15", issue_count=len(rows), categories=12,
                counts=counts, scope="engineering audit, not a new CAD or hardware acceptance",
                files=["audit_review.html", "reaction_paths.svg", "issues.json", "system_issues.csv", "sources.json", "system_calculations.json", "calculate_audit.py", "build_audit.py"],
                hardware_enabled=False, CAD_manufacturing_release=False,
                sample_tests_completed=False, original_conversation_included=False)
(HERE / "delivery.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps(manifest, ensure_ascii=False, indent=2))
