from pathlib import Path
import csv,json,hashlib,io
R=Path(__file__).resolve().parent
BASE=R.parents[1]/'tri-product-continuation-2026-10-10.nosync'
src=BASE/'vacuum_manual_A105/whole_product_28_requirements_A105.csv'
rows=list(csv.DictReader(src.open(encoding='utf-8-sig')))
replacements={
'1':'逐台核对50,000 rpm标称及同载荷转速保持能力；不得以参考机95%作为通过线。负载工况、转速仪不确定度与比较方法须在试验前冻结；标称达成和相对参考机的表现分别判定。',
'2':'同钻头、同材料、同加载条件下逐台比较速度保持、恢复时间及电流/电压瞬态，不预留5%性能下降。仪器不确定度和事前等效判据未确定，或结果不能区分优劣时，记未验证而非通过。',
'3':'匹配电池状态、负载和截止条件，逐台比较无线工作时长，不预留5%续航下降；单独核对10小时宣传适用工况，不因参考机未达到标称而豁免候选的相应功能核验。充电、温升及保护另行验证。',
'4':'在匹配背景、距离、工况及夹具下逐台比较噪声、振动和偏摆；取消未获依据的+2 dBA允许增量。事前明确仪器不确定度和可重复性，不能仅凭主观听感或区间重叠判定等效。',
'8':'同粉尘、装载、距离、滤网和档位逐台比较实际捕集效果，不预留5%捕集性能下降；压差和风量作为辅助量，不能单独替代捕集结果。等效判据及不确定度未冻结前不得判通过。',
'9':'固定背景、距离、姿态、滤网和有效捕集任务，逐台比较声压与捕集效果，取消无实测依据的+2 dBA增量；不能以降低风机输出获得低噪声而使捕集效果下降。',
'14':'核对365/405 nm相应光谱、全工作区辐照分布、温升和实际材料固化效果，不预留5%固化性能下降。须区分物理辐照、真实甲面识别及软件概率，不以单点照度或软件模型测试替代实物对比。',
}
scopes={
 '电磨':'A104目标main采集按键/编码器与诊断；A102 UI/速度监督是独立候选，尚未进入该main；motor_foc仅编译硬浮点对象、未链接进软浮点目标。完整启动/FOC、显示、手柄、充电运行链待集成。',
 '吸尘器':'A125已接主机压力读数有效期、解码及显示/面板诊断；风机仍关闭。四档实物控制、吸力显示含义、学习与告警、风道捕集及标定未闭合。',
 'AI美甲灯':'A118为合成输入的视觉映射/协议候选；未证明真实相机、模型和Linux部署。A119/A120指出显示支路未落板，面板电气/背光资料待确认；灯输出及物理固化未验。',
}
for row in rows:
 row['PreviousAcceptanceCriteria']=row['AcceptanceCriteria']
 if row['Requirement'] in replacements:row['AcceptanceCriteria']=replacements[row['Requirement']]
 row['ImplementationEvidenceScope']=scopes[row['Product']]
 row['CurrentPhysicalEvidence']='本轮未收到参考整机/部件测试记录；不能由USB检查推断远程工厂资源。'
 row['Status']='NOT_VERIFIED'
 row['ReferenceIdentity']='原始用户链接B0HBWRZR4G保留；本行历史单品ASIN仅用于追溯，实物型号/版次须与套装参考对应。'
 row['AcceptanceMargin']='UNSET_REQUIRES_PRETEST_JUSTIFICATION; no automatic 0.95 ratio or +2dBA'
fields=list(rows[0])
with (R/'whole-product-28-requirements-A126.csv').open('w',encoding='utf-8-sig',newline='') as f:
 w=csv.DictWriter(f,fieldnames=fields);w.writeheader();w.writerows(rows)
receipts=[]
paths={
 'drill_target_main':BASE/'drill_encoder_target_A104/firmware/固件/drill_target/src/main.c',
 'drill_target_build':BASE/'drill_encoder_target_A104/firmware/固件/drill_target/build.sh',
 'drill_ui_separate':BASE/'drill_ui_supervisor_A102/src/drill_ui.c',
 'vacuum_target_main':R.parent/'vacuum_pressure_age_A125/firmware/固件/vacuum_target/src/main.c',
 'lamp_vision_scope':BASE/'lamp_vision_mapping_A118/README.md',
 'lamp_display_current':BASE/'lamp_display_audit_A119/README.md',
 'lamp_panel_constraints':BASE/'lamp_panel_sources_A120/README.md',
 'prior_requirements':src,
}
for name,p in paths.items():
 b=p.read_bytes();receipts.append({'role':name,'path':str(p.resolve()),'bytes':len(b),'sha256':hashlib.sha256(b).hexdigest()})
main=paths['drill_target_main'].read_text();build=paths['drill_target_build'].read_text()
checks={'drill_target_has_input_capture':all(x in main for x in ['drill_button_poll(now)','drill_encoder_poll(now)']),
'drill_target_has_no_ui_tick_call':'dui_tick(' not in main,
'drill_foc_object_explicitly_excluded':'deliberately kept out' in build,
'vacuum_age_called_from_main':'pv_tick(&pressure_age,now_ms)' in paths['vacuum_target_main'].read_text(),
'vacuum_output_still_locked':'vacuum_board_force_fan_disabled();' in paths['vacuum_target_main'].read_text(),
'full_scope_preserved':len(rows)==28 and {r['Product'] for r in rows}==set(scopes),
'legacy_downgrade_removed_from_active_criteria':all(r['AcceptanceCriteria']==replacements[r['Requirement']] and r['AcceptanceCriteria']!=r['PreviousAcceptanceCriteria'] for r in rows if r['Requirement'] in replacements)}
assert all(checks.values()),checks
(R/'source-checks.json').write_text(json.dumps({'checks':checks,'source_receipts':receipts},ensure_ascii=False,indent=2))
(R/'acceptance-state.json').write_text(json.dumps({'objective':'PCBA功能效果上要求和链接一致，落地级别','previous_goal_turn_classification':'progress: A125 target implementation and tested package','status':'NOT_VERIFIED','requirements':28,'verified_requirements':0,'legacy_downgrade_rows_superseded':list(replacements),'physical_equivalence_proven':False,'manufacturing_release':False,'public_site_modified':False},ensure_ascii=False,indent=2))
print('PASS source-bound requirements revision:',len(rows),'requirements;',len(replacements),'unsupported downgrade criteria superseded')
