| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- """Record the independent S5 acceptance locally; never call business APIs."""
- from datetime import datetime, timezone
- from hashlib import sha256
- import json
- from pathlib import Path
- ROOT = Path('F:/kangshifu-board')
- DOCS = Path('F:/文档/工作坊当前资料')
- WORK = ROOT / 'work'
- BACKUP = WORK / 'S5-progress-before-acceptance-20261103'
- RECEIPT = WORK / 'live-S5-independent-acceptance-20261103.json'
- REPORT = DOCS / '55-S5方案与课堂报告独立验收结果.md'
- def digest(path):
- return sha256(path.read_bytes()).hexdigest()
- data_path = WORK / 'live-S5-readback-20261103-02.json'
- browser_path = WORK / 'live-S5-browser-verified-20261103.json'
- data = json.loads(data_path.read_text(encoding='utf-8'))
- browser = json.loads(browser_path.read_text(encoding='utf-8'))
- assert data['ok'] and data['checksPassed'] == 14
- assert browser['ok'] and browser['browserVerified']
- assert len(browser['checks']) == 235 and all(c['passed'] for c in browser['checks'])
- assert not browser['errors']
- assert data['executionConformsToFixedRequestIds'] is False
- assert data['requestIdDeviation']['expectedKeyCommitted'] is False
- assert data['artifactId'] == browser['artifactId'] == 'tLkz2MAoIw'
- assert data['conceptId'] == browser['conceptId'] == 'QO0uo8nisk'
- immutable = {
- ROOT / 'work/S5-classroom-next-20261103/01-project-S5-progress.template.json': '049251b5c1d1984cae72c216ac025e9eb68bd87d02de16166cc7dd4f19bce213',
- ROOT / 'work/S5-classroom-next-20261103/02-concept-save.template.json': '5c182b5c5733218577bca330df90b85b91d69ad1b6b1e5494bab5b06e2238e4d',
- ROOT / 'work/S5-classroom-next-20261103/03-project-S5-save.template.json': 'f8dc7d8a9f0b2a998f90453add9ca02f34a60cfc52be3bc592deab87d49ada8c',
- DOCS / 'ksf-S5-classroom-next-20261103.zip': '70039fcbecd3bd2bef9f36659c8a55c270f8472e57ea7b6488c8a9f8ff5f5ff1',
- }
- for path, expected in immutable.items():
- assert digest(path) == expected, path
- for name in ['52-S5桌面方案审阅与定稿.md', '53-发给运维001-S5方案与报告准备.txt', '54-发给王刚001-S5方案与课堂报告保存.txt']:
- path = DOCS / name
- immutable[path] = digest(path)
- assert not BACKUP.exists() and not RECEIPT.exists() and not REPORT.exists()
- originals = {name: (DOCS / name).read_bytes() for name in ['01-工作进展汇报.txt', '02-后续待补资料.md', 'README.md']}
- visual_files = [
- 'work/live-S5-project-desktop-1793683536092.png',
- 'work/live-S5-table-0-desktop-1793683536092.png',
- 'work/live-S5-project-mobile-1793683536092.png',
- 'work/live-S5-table-0-mobile-1793683536092.png',
- 'work/live-S5-table-4-mobile-1793683536092.png',
- 'work/live-S5-evidence-public-p1-mobile-1793683536092.png',
- ]
- for path in browser['screenshots']:
- assert (ROOT / path).is_file()
- receipt = {
- 'at': datetime.now(timezone.utc).isoformat(),
- 'operation': 'live-S5-independent-acceptance',
- 'contentAndPageAccepted': True,
- 'executionConformsToFixedRequestIds': False,
- 'requestIdDeviation': data['requestIdDeviation'],
- 'readOnly': True,
- 'businessWrites': 0,
- 'sessionId': data['sessionId'],
- 'opportunityId': data['opportunityId'],
- 'projectId': data['projectId'],
- 'conceptId': data['conceptId'],
- 'artifactId': data['artifactId'],
- 'outputSourceId': data['outputSourceId'],
- 'reportUrl': data['reportUrl'],
- 'stageCode': 'S5',
- 'progressState': 'in_progress',
- 'checks': {'data': 14, 'productionBrowser': 235},
- 'browserVerified': True,
- 'browserVerificationStoredLocallyOnly': True,
- 'viewports': browser['viewports'],
- 'visualReview': {'performed': True, 'screenshots': visual_files},
- 'reportTables': data['reportTables'],
- 'rawTextCodepoints': 3655,
- 'chapterCount': 5,
- 'sectionCount': 15,
- 'tableCount': 5,
- 'sourceIds': data['sourceIds'],
- 'evidenceIds': data['evidenceIds'],
- 'pendingQuestionCount': 13,
- 'projectArtifactIds': data['projectArtifactIds'],
- 'projectEvents': data['projectEvents'],
- 'committedWriteCount': 3,
- 'committedWriteRequests': data['committedWriteRequests'],
- 'runbookReadCommandCount': 5,
- 'countsBasis': 'Three committed write commands verified from operation audit; five read commands specified by the eight-step runbook. Remote CLI execution counts, exit codes and invocation IDs are operator-reported.',
- 'oldReportsPreserved': True,
- 'formalReportPublished': False,
- 'employeeConfirmed': False,
- 'enterpriseApproval': False,
- 'researchValidationPerformed': False,
- 'fieldLocations': data['fieldLocations'],
- 'retainedDisplayObservations': [
- '390px four-column tables need substantial vertical scrolling; all cells remain reachable.',
- 'Retailer listing drawer retains the generic unknown-person-count label; the dash means unknown, not one consumer.',
- 'Long source locator in the mobile drawer needs inner horizontal scrolling; no page-level overflow.',
- ],
- 'remoteFilesystemIndependentlyVerified': False,
- 'initialStrictAuditRetained': str(WORK / 'live-S5-readback-20261103.json'),
- 'auditInspectionRetained': str(WORK / 'S5-write-audit-inspection-20261103.json'),
- 'evidenceFiles': [
- {'path': str(p), 'sha256': digest(p)}
- for p in [data_path, browser_path, WORK / 'S5-write-audit-inspection-20261103.json', WORK / 'live-S5-readback-20261103.json']
- ],
- 'screenshots': [{'path': str(ROOT / p), 'sha256': digest(ROOT / p)} for p in browser['screenshots']],
- 'immutableFiles': [{'path': str(p), 'sha256': h} for p, h in immutable.items()],
- 'documentationBackup': str(BACKUP),
- }
- report = '''# S5方案与课堂报告独立验收结果
- 2026年11月3日。Codex独立核对生产数据及匿名电脑、手机页面后,确认S5概念、报告内容与项目关联通过验收。执行过程存在一处报告保存requestId偏差,必须保留,不能表述为“全部按54号原业务键完成”。本轮仅只读验收及本地文件整理,业务写入0次。
- | 项目 | 独立核实结果 |
- |---|---|
- | 方向 | 燃魂份量与用餐目的适配,沿用用户选择 |
- | 会话 / 机会卡 | btrFqkz0yw / QE9hjtCj8E;机会仍为假设,课堂approved不代表企业批准 |
- | 项目 | S_CikxZeBs,S5 / in_progress,dataMode=live |
- | 概念 | QO0uo8nisk,version=1,燃魂份量选择提示与搭配指引(待验证概念) |
- | S5报告 / 正文来源 | tLkz2MAoIw / 5zsPtYFL1u;version=1 |
- | 报告类型 | artifactType=concept;reportType=workshop_auxiliary;artifactSubtype=classroom_task |
- | 系列 / 标签 | KSF-真实资料-20261102-01:S5;拌面、课堂过程成果 |
- | 数据检查 | 14项通过,正文3655码点、概念内容、4来源/4引文、13待补问题与固定模板一致 |
- | 生产页面 | 1440px电脑、390px手机,235项功能检查通过;22张截图,6张代表截图目视复核 |
- | 项目成果 | 恰3份:XF4h-Z7l5o(S3)、U3_W7WDliP(S4)、tLkz2MAoIw(S5) |
- | 当前方案 | currentConceptRef→KfConcept/QO0uo8nisk;同项目概念恰1个,无配方条目 |
- | 仍未发生 | 课堂完成、员工确认、正式发布、企业批准、实物/市场/量产验证 |
- 报告:[燃魂份量选择提示与搭配指引|S5桌面方案 第1版](https://kangshifu.fmode.cn/report/tLkz2MAoIw)。
- ## 执行键偏差及处置
- | 第06步报告保存 | 完整requestId |
- |---|---|
- | 54号规定 | KSF-真实资料-20261102-01:S5:save:v1 |
- | 生产实际成功 | KSF-真实资料-20261102-01:portion-fit-project:S5:save:v1 |
- 独立读取KsfOperation成功审计及对应回执,确认实际键关联报告tLkz2MAoIw和正文5zsPtYFL1u;规定键没有成功提交审计。其余两个写入键与执行单一致。reportSeriesKey仍正确为KSF-真实资料-20261102-01:S5,不能把requestId与报告系列键混为一谈。
- 首次严格验收在此项停止,原失败记录保留;随后按已返回业务编号定位实际审计,再进行内容和页面只读验收。执行键为什么变化尚未查明,Codex未取得远端命令和原始日志,不作归因。以后交接沿用实际成功键与业务编号;**不得用尚未使用的规定键补跑保存,不重存、不换键、不删记录,不回改54号或原包来掩盖偏差。** 只需下次交接附上实际第06步命令与回执用于溯源,不影响现有报告使用。
- S5本轮实测恰3条成功写入审计:workshop-transition(转S5)、workshop-concept(保存概念)、workshop-task(保存报告)。对应3个写命令,事件属于这些命令的副作用;不能另计为第四个写命令。8步执行单由3个写命令和5个读命令组成。读命令的实际次数、退出码、stderr、两层invocationId和远端29个文件权限仍依据王刚001回报,Codex没有独立读取远端文件系统,也不用写审计证明只读命令从未运行。
- ## 内容及字段核对
- 概念九项内容字段及specification九个子字段与模板相同。netWeightG/noodleWeightG/packCount/currentSku等未知项保留null,formulaItems与productionSteps为空;没有把P1图示克重当新品规格,没有补造配方、成本或工艺。KfConcept.reviewState=pending、proposalArtifactRef=null,不声称方案已正式批准或已建立报告Pointer。
- 字段落点符合当前服务实现:concept输入的13条pendingQuestions保存在项目meta.pendingQuestions及保存回执,报告另有meta.workshop.pendingQuestions;KfConcept本身没有同名键。输入taskKey写入reportSeriesKey,不要求记录出现taskKey键。project-status的formulaItems为响应顶层数组,不要求项目记录出现同名字段;独立配方条目数为0。这些不作为数据丢失,也不补写旧记录。
- 报告实际5章15节、5张表,修正回报中的“13节/4个table”说法:
- | 表 | 列×行 |
- |---|---|
- | 设计对照 | 4×4 |
- | 设计、观察与未知 | 3×8 |
- | 主引文 | 3×4 |
- | 继续、调整与暂停 | 4×4 |
- | 待补问题 | 3×13 |
- 13个待补问题逐字等于模板;前8条沿用S4,后5条为设计问题。可见表第三列前8行是“原S4问题”,后5行分别为“承原1、4”“承原4、6”“承原7”“承原8”“承原3、6”,不写成统一“新增设计问题”。executive仅由服务端前置课堂声明,report.title/generatedAt属于预期服务补充。
- 四条来源及证据逐字核对,编号保留下划线:
- | 用途 | sourceId | evidenceId |
- |---|---|---|
- | cq-03历史份量自述 | m1OQx9aynA | _9mUml0wmh |
- | cq-04历史份量自述 | QdeMcyTTjX | 2bFGP7axMo |
- | P1苏宁公开图片页观察 | PjORcvaRHn | xHGV_mRWcG |
- | P2历史个人煮制叙述 | 5S_Y4IgT5q | CeAEIwymTa |
- 四条productRef均为空,不合并同SKU或同批次,不认定当前在售、真实购买身份或普遍需求。P1不证明信息分散,P2不证明包装错误;信息布局与可选搭配仍是待验证设计,不能称已验证卖点。
- ## 项目及页面
- 项目恰7条有序事件,没有completed事件:
- | 序号 | eventId | eventType |
- |---|---|---|
- | 1 | GGDTcg5YTy | project_created |
- | 2 | RkkTTE_xeb | task_submitted(S3) |
- | 3 | hOFghbcMsY | stage_changed(S4) |
- | 4 | VlGFu_jv4b | task_submitted(S4) |
- | 5 | 0ya0a03U9G | stage_changed(S5) |
- | 6 | qKnPnVkZA8 | concept_saved |
- | 7 | iUs3JwZ9Kf | task_submitted(S5) |
- 同工作坊现有9份报告,S5系列只有第1版;没有第二套机会卡、项目或概念。旧S1—S4正文、结构与原有待补问题集仍保持原样,当前项目13条未决项不覆盖旧报告的8条。
- 匿名生产浏览器核实了成员项目行、阶段页、报告列表及报告详情。成员项目行的模型/桌面显示对应S5、3份成果、方案已保存、进行中;手机折叠行展示项目名和S5进行中,不把隐藏列写成全在首屏可见。两种屏宽逐表对照所有行列,滚动检查可达性,真实点击4条引文、核逐字文本和原来源,Escape关闭。引文比较保留原换行,仅忽略显示插入的U+2009细空格。未发现页面级横向溢出、运行或网络错误,旧报告仍可读。
- 保留显示改进:390px四列表格列窄、需较多纵向滚动;商品页证据抽屉仍沿用“—人独立提到”的人数未知标签(不是1人);手机抽屉长出处需内部横滚。来源内容可达,功能通过;本轮未改前端,也不声称这些显示问题已经修复。
- boardVisible=true、formalReportPublished=false、employeeConfirmed=false由实际成功保存审计核实。dashboardState=workshop不等于正式发布。browserVerified=true仅记录在本地Codex验收证据,未回写生产。enterpriseApproval=false。
- 本次交付完成S5保存与只读独立验收;当前仍S5/in_progress。下一步可整理课堂总结和完成条件,另行执行对应状态操作;本轮不自动completed、不代员工正式确认或发布。实物研究需要时再明确样品、资源和方法,不要求用户先补实物才能继续软件流程。
- 本地证据(均不含凭据或浏览器档案):
- - F:/kangshifu-board/work/live-S5-independent-acceptance-20261103.json
- - F:/kangshifu-board/work/live-S5-readback-20261103.json(首次严格验收失败,保留)
- - F:/kangshifu-board/work/S5-write-audit-inspection-20261103.json(实际成功键定位)
- - F:/kangshifu-board/work/live-S5-readback-20261103-02.json(14项数据核对)
- - F:/kangshifu-board/work/live-S5-browser-verified-20261103.json(235项及22张截图索引)
- 01、02及README更新前完整快照保留在F:/kangshifu-board/work/S5-progress-before-acceptance-20261103/。52/53/54号、三个固定模板、原S5包及所有旧回执保留原样。
- '''
- progress = '''工作坊进展(截至2026年11月3日,最新独立验收见55号)
- 真实资料课堂流程已推进到S5 / in_progress,选定方向为“燃魂份量与用餐目的适配”。概念QO0uo8nisk和S5报告tLkz2MAoIw均已保存,Codex独立数据核对14项、电脑/手机生产浏览器检查235项通过,并已目视复核代表截图。课堂仍进行中,未标完成、未员工确认、未正式发布、未企业批准;实物、市场和量产验证均未做。
- 会话btrFqkz0yw;课堂机会卡QE9hjtCj8E;项目S_CikxZeBs。项目恰3份成果:S3基线XF4h-Z7l5o、S4评估U3_W7WDliP、S5方案tLkz2MAoIw;当前概念1个、事件7条。报告正文3655码点,5章15节5张表,4来源/4引文、13待补问题与固定模板一致,旧S1—S4保持原样。
- 必须保留的执行偏差:54号规定S5报告保存requestId为KSF-真实资料-20261102-01:S5:save:v1,实际成功键为KSF-真实资料-20261102-01:portion-fit-project:S5:save:v1。实际键与报告/正文编号已核实;规定键没有成功提交审计。不得用规定键补跑保存,不重存、不换键、不删记录,也不回改原执行单掩盖差异。原因尚未查明;下次交接附实际第06步命令与原回执即可,不必重跑业务。
- 本轮S5成功写命令是3个(转阶段、保存概念、保存报告);8步执行单另有5个读命令,事件为写命令副作用,不另计第4个写命令。远端文件数量/权限、CLI退出码和invocationId依据王刚001回报,Codex独立确认的是生产数据、成功写入审计与匿名页面。
- 方案是信息呈现与可选搭配设计,尚未确定新品规格。净含量、面饼重量、件数和当前SKU等未知值保留null,配方/工艺为空。历史帖子仅证明个体自述,P1公开图片页仅证明页面/图示展示;不合并同SKU、不证明当前在售或普遍需求。13待补含原S4八项和新增五项设计问题。
- 保留显示改进:手机四列表格较窄、需纵向滚动;商品页抽屉仍用人数未知标签;手机长出处需抽屉内横滚。功能可用,本轮未改前端。browserVerified=true仅为本地验收记录,未回写生产。
- 下一步可整理课堂总结与完成条件,再执行相应状态操作;本次没有自动completed。真实研究缺口继续保留,不要求用户先供实物才能继续软件课堂流程。员工实名绑定、新数字人接入及旧机器人密钥轮换仍待处理。
- 最新S5报告:https://kangshifu.fmode.cn/report/tLkz2MAoIw
- S4桌面评估:https://kangshifu.fmode.cn/report/U3_W7WDliP
- 看板:https://kangshifu.fmode.cn/
- 详细验收:55-S5方案与课堂报告独立验收结果.md。
- 更新前汇报快照:F:/kangshifu-board/work/S5-progress-before-acceptance-20261103/。
- S5固定材料包及52/53/54号保留原样,不再次执行。
- '''
- needs = originals['02-后续待补资料.md'].decode('utf-8-sig')
- old_intro = '2026年11月3日更新。课堂系统当前S4 / in_progress;S4报告U3_W7WDliP的数据与电脑/手机功能已独立验收,详见50号。下一步可写S5桌面方案草稿;下面研究缺口不作为全部软件流程测试的统一前置,不要求用户现在提供原件,也不重查已知登录墙。'
- assert old_intro in needs
- needs = needs.replace(old_intro, '2026年11月3日更新。课堂系统当前S5 / in_progress;概念QO0uo8nisk和S5报告tLkz2MAoIw已保存,数据14项、电脑/手机生产浏览器235项检查通过,详见55号。报告保存键存在已留档偏差,不补跑保存。下面研究缺口继续保留,不作为全部软件流程测试的统一前置,不要求用户现在提供原件,也不重查已知登录墙。', 1)
- start = needs.index('软件流程下一步:')
- end = needs.index('\n\n前端两项独立待改:', start)
- needs = needs[:start] + '''软件流程状态:S5保存与独立验收完成,项目恰3报告、1概念、7事件;仍S5/in_progress,尚未课堂完成、确认或发布。当前方案未知规格为null、formulaItems/productionSteps为空。13条未决问题中前8条沿用S4,新增5条设计问题如下:
- 1. 净含量/面饼/件数三行分区能否正确理解,会不会更乱。
- 2. 信息呈现是否增加货架识别、比价或停留负担。
- 3. 包材版面和渠道素材能否执行,实际成本未知。
- 4. 可选搭配会不会被读成必须添加,反向暗示份量不足。
- 5. 记录补主食原因会不会影响行为,记录负担和观察者效应待评估。
- 下一步可整理课堂总结与完成条件;不重跑53/54号,不补存新版本。第06步实际成功requestId为KSF-真实资料-20261102-01:portion-fit-project:S5:save:v1,与54号规定的KSF-真实资料-20261102-01:S5:save:v1有差异。实际报告已存在,严禁用规定键补跑;只需下次交接附原命令与回执说明原因,不要求重新执行。员工正式确认和发布另走各自流程。''' + needs[end:]
- needs = needs.replace('前端两项独立待改:商品页证据抽屉的人数标签、390px长出处自动换行。功能验收通过,本轮未改前端。', '前端独立待改:商品页证据抽屉的人数未知标签、390px长出处自动换行,以及窄屏四列表格阅读体验。功能验收通过,本轮未改前端。')
- needs = needs.replace('更新前资料清单完整快照位于F:/kangshifu-board/work/S4-progress-before-acceptance-20261103/。', '本轮更新前完整快照位于F:/kangshifu-board/work/S5-progress-before-acceptance-20261103/;此前S4快照仍保留。')
- readme = originals['README.md'].decode('utf-8-sig')
- start = readme.index('当前状态(')
- end = readme.index('\n\n以下编号材料', start)
- readme = readme[:start] + '当前状态(2026年11月3日):S5 / in_progress。概念QO0uo8nisk、报告tLkz2MAoIw已保存;Codex14项数据和235项匿名电脑/手机生产浏览器检查通过,详见55号。项目3报告、1概念、7事件,未课堂完成、员工确认或正式发布,未做实物/市场/量产验证。第06步实际报告保存键较54号多了:portion-fit-project,偏差已留档;不得用规定键补跑保存。52/53/54及原包保留,不重跑。下一步可整理课堂总结和完成条件。' + readme[end:]
- readme = readme.replace('旧01/02/README完整快照在F:/kangshifu-board/work/S4-progress-before-acceptance-20261103/。', '本轮旧01/02/README完整快照在F:/kangshifu-board/work/S5-progress-before-acceptance-20261103/;S4旧快照仍保留。', 1)
- readme = readme.replace('- 51-发给王刚001-S5桌面方案草稿.txt:', '- 55-S5方案与课堂报告独立验收结果.md:当前结论;内容和生产页面通过,保存requestId偏差、计数及字段落点已校正,保持S5/in_progress,不补跑保存。\n- 51-发给王刚001-S5桌面方案草稿.txt:', 1)
- readme = readme.replace('- 52-S5桌面方案审阅与定稿.md:当前审阅结果;', '- 52-S5桌面方案审阅与定稿.md:历史审阅结果;', 1)
- readme = readme.replace('- 53-发给运维001-S5方案与报告准备.txt:当前下一步;原样落地3模板并只读核合同、归属、来源、历史、在途。', '- 53-发给运维001-S5方案与报告准备.txt:已回报落地预检完成,原单保留,不重跑准备。', 1)
- readme = readme.replace('- 54-发给王刚001-S5方案与课堂报告保存.txt:运维预检通过后在真实消息执行8步;project-progress→concept-save→workshop-save,最终S5/in_progress,不自动completed。', '- 54-发给王刚001-S5方案与课堂报告保存.txt:已执行并保存S5;第06步实际成功requestId与原单不同,见55号。保留原单不回改,不补跑规定键,不自动completed。', 1)
- readme = readme.replace('- S5桌面方案第1版-审阅稿.md:拟保存的正文审阅稿,尚未保存生产。', '- S5桌面方案第1版-审阅稿.md:已保存内容的审阅稿,生产报告tLkz2MAoIw及回读为准。', 1)
- readme = readme.replace('- ksf-S5-classroom-next-20261103.zip:3固定模板、runbook、审阅及交接;11条目、10/10清单核验。sessionId/projectId为null,不可直接提交。', '- ksf-S5-classroom-next-20261103.zip:已使用的3模板与交接原包,11条目、10/10核验;指纹未变,不再次执行。ID绑定为null,不可直接提交。', 1)
- readme = readme.replace('- 50-S4课堂桌面评估独立验收结果.md:当前结论;', '- 50-S4课堂桌面评估独立验收结果.md:S4阶段历史结论;', 1)
- readme = readme.replace('当前入口:\n\n', '当前入口:\n\n- 最新S5桌面方案:https://kangshifu.fmode.cn/report/tLkz2MAoIw\n', 1)
- readme = readme.replace('- 最新S4桌面评估:', '- S4桌面评估:', 1)
- updates = {'01-工作进展汇报.txt': progress, '02-后续待补资料.md': needs, 'README.md': readme}
- for name, text in updates.items():
- assert 'S5 / in_progress' in text
- assert 'tLkz2MAoIw' in text
- assert 'portion-fit-project:S5:save:v1' in text or name == 'README.md'
- assert '尚未保存concept或S5报告' not in readme
- assert '当前下一步;原样落地3模板' not in readme
- BACKUP.mkdir()
- for name, content in originals.items():
- with (BACKUP / name).open('xb') as stream:
- stream.write(content)
- with (BACKUP / 'manifest.json').open('x', encoding='utf-8') as stream:
- json.dump({name: sha256(content).hexdigest() for name, content in originals.items()}, stream, ensure_ascii=False, indent=2)
- with RECEIPT.open('x', encoding='utf-8') as stream:
- json.dump(receipt, stream, ensure_ascii=False, indent=2)
- with REPORT.open('x', encoding='utf-8') as stream:
- stream.write(report)
- for name, content in updates.items():
- (DOCS / name).write_text(content, encoding='utf-8')
- for path, expected in immutable.items():
- assert digest(path) == expected, path
- for name, content in originals.items():
- assert (BACKUP / name).read_bytes() == content
- print(json.dumps({'ok': True, 'businessWrites': 0, 'acceptance': str(REPORT), 'receipt': str(RECEIPT), 'backup': str(BACKUP), 'updated': list(updates), 'immutableFilesUnchanged': len(immutable)}, ensure_ascii=False, indent=2))
|