package-live-S1-v2.py 7.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. """Prepare the next report input and handoff files; never writes remote data."""
  2. from pathlib import Path
  3. import hashlib
  4. import json
  5. import zipfile
  6. ROOT = Path(__file__).resolve().parents[1]
  7. WORK = ROOT / 'work'
  8. DELIVERY = Path('F:/文档/工作坊当前资料')
  9. def digest(value):
  10. return hashlib.sha256(value).hexdigest()
  11. def main():
  12. template_path = WORK / 'first-live-S1-v2-template.json'
  13. task = json.loads(template_path.read_text(encoding='utf-8'))
  14. # Store the full report body in rawText as well as the structured chapters.
  15. paragraphs = [task['title'], *task['report']['executive']]
  16. for chapter in task['report']['chapters']:
  17. paragraphs.append(chapter['title'])
  18. for section in chapter['sections']:
  19. paragraphs.append(section['title'])
  20. if section['kind'] == 'para':
  21. paragraphs.append(section['text'])
  22. elif section['kind'] == 'table':
  23. paragraphs.append(' | '.join(section['data']['cols']))
  24. paragraphs.extend(' | '.join(row) for row in section['data']['rows'])
  25. else:
  26. raise ValueError('unsupported section')
  27. for entry in task['report']['evidence'].values():
  28. paragraphs.append(entry['claim'])
  29. paragraphs.extend(q['text'] + '\n' + q['ref'] for q in entry['quotes'])
  30. task['rawText'] = '\n\n'.join(paragraphs)
  31. template_path.write_text(json.dumps(task, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
  32. operations = '''运维001:准备S1小结第2版,业务仍由王刚001在真实消息任务里保存。
  33. 先核FILES.sha256四项。将S1-save-v2.template.json安装到:
  34. /opt/data/ksf-executor-20261102/first-live-S1/S1-save-v2.template.json
  35. root:root 0644、hermes可读。保留原S1-save.template.json及所有原回执。新模板sessionId=null,不能直接提交。
  36. 现有会话btrFqkz0yw,原报告cOm6c8gg7z,第1版。原taskKey=KSF-真实资料-20261102-01:S1,新version=2、requestId=KSF-真实资料-20261102-01:S1:save:v2。核对当前发送者和Agent与原会话归属一致,并检查服务端本系列第2版、该requestId历史、在途任务及本地回执;如已有任何记录先回读,不重放、不换键。Codex打包前已只读核对,不代表以后不会发生并发变化。
  37. 业务输入由hermes生成/0600;既有/opt/data/ksf-workshop-private/S1-live-01为hermes/0700,继续留存,不清空。新文件S1-live-save-v2.json、S1-v2-save.stdout.json、S1-v2-save.stderr.txt、S1-v2-read.json及读取回执均不得覆盖已有文件。不得递归chown数据卷。
  38. 无需重启、无需改Skill或Function,不重导入产品/引文,不改原报告,不代跑业务。交王刚001按另一份说明执行,回执给Codex后再做页面验收。本次新增报告来源记录属于保存小结的正常写入,不是再次采集或重复登记原网页。
  39. '''
  40. wang = '''王刚001:继续真实流程测试,本轮只保存S1小结第2版,不再导资料。
  41. 1. 在本条真实企微消息调用无参数工具ksfregistercurrent_employee。确认当前Agent和员工与现有live会话btrFqkz0yw的归属一致,并精确回读原报告cOm6c8gg7z(version=1)。不创建新会话,不用旧身份JSON冒充本条消息上下文。
  42. 2. 等维护人确认模板安装和服务端/本地第2版历史、requestId和在途检查完成。若已有第2版或结果不明的执行回执,先回读并报告,不再提交。模板路径:
  43. /opt/data/ksf-executor-20261102/first-live-S1/S1-save-v2.template.json
  44. 固定taskKey=KSF-真实资料-20261102-01:S1、version=2、requestId=KSF-真实资料-20261102-01:S1:save:v2。
  45. 3. 以hermes(10000)、HOME=/opt/data运行。在内存读模板,仅将sessionId从null填成btrFqkz0yw;其余字段逐项保持一致。用独占创建方式(已存在则停)写/opt/data/ksf-workshop-private/S1-live-save-v2.json,0600/hermes。不打印完整输入;模板没有账号、会话键或凭据。
  46. 4. 使用已安装入口执行一次(--agent-profile填本条登记回执返回且核实的当前Agent完整键):
  47. node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs workshop-save --agent-profile <当前agentProfileKey> --input /opt/data/ksf-workshop-private/S1-live-save-v2.json --request-id KSF-真实资料-20261102-01:S1:save:v2
  48. 用独占新建的0600文件在/opt/data/ksf-workshop-private/S1-live-01/留存stdout、stderr和退出码(S1-v2-save前缀)。先确认外层ok及entryResult.ok,再取entryResult.result中sessionId/artifactId/sourceId。失败、超时或结果不确定立即停止,保留原requestId与所有编号,不自动重试、不换键、不删记录。错误不等于零写入。
  49. 5. 成功后精确回读新artifact。只读输入为{"className":"KfArtifact","objectId":"实际新artifactId"},独占写到0600的/opt/data/ksf-workshop-private/S1-live-01/S1-v2-read.json。
  50. node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs workshop-read --input /opt/data/ksf-workshop-private/S1-live-01/S1-v2-read.json --request-id KSF-真实资料-20261102-01:S1:read:v2
  51. 核对version=2、S1、live、dashboardState=workshop、sourceIds含BgUB6dnxN1/SFiKA0D7n9/McdwhD7KQq;五段逐字引文、六条待补问题和正文与模板相符。service会在摘要前加课堂声明及generatedAt,按服务合同核对。读不到私有meta不自行补造,可交Codex独立核对。
  52. 6. 回报sessionId、artifactId、生成正文sourceId、reportUrl、version、stageCode、dataMode、boardVisible、formalReportPublished、employeeConfirmed、两层invocationId及回读是否通过。不重跑K轮/查重/原导入,不新建分类、产品、机会卡或项目。旧报告保留,新版得到新链接。本轮没开浏览器就报browserVerified=false;页面验收由Codex后续独立执行。
  53. 内容口径:统一9个口味不等于9个SKU;康师傅是1条产品线、同一篇2022年媒体转载品牌通告中的3条引文,不能按条数与统一比较。现有材料不能证明当前在售或消费者偏好,滤水口不是新提出的创新方向,干面荟尚未建立产品记录。S1是课堂资料小结,不是正式市场结论。
  54. '''
  55. members = {
  56. 'S1-save-v2.template.json': template_path.read_bytes(),
  57. 'S1小结第2版-审阅稿.md': (WORK/'first-live-S1-v2-review.md').read_bytes(),
  58. '给运维001-S1第2版准备.txt': operations.encode('utf-8'),
  59. '给王刚001-S1第2版保存.txt': wang.encode('utf-8'),
  60. }
  61. manifest = ''.join(digest(body)+' '+name+'\n' for name,body in members.items()).encode('utf-8')
  62. members['FILES.sha256'] = manifest
  63. DELIVERY.mkdir(parents=True,exist_ok=True)
  64. target = DELIVERY/'ksf-live-S1-v2-20261102.zip'
  65. with zipfile.ZipFile(target,'w',compression=zipfile.ZIP_DEFLATED) as archive:
  66. for name,body in members.items():
  67. archive.writestr(name,body)
  68. with zipfile.ZipFile(target) as archive:
  69. assert archive.testzip() is None
  70. for line in archive.read('FILES.sha256').decode('utf-8').splitlines():
  71. expected,name = line.split(' ',1)
  72. assert digest(archive.read(name)) == expected
  73. (DELIVERY/'05-发给王刚001-S1第2版保存.txt').write_text(wang,encoding='utf-8')
  74. (DELIVERY/'06-发给运维001-S1第2版准备.txt').write_text(operations,encoding='utf-8')
  75. (DELIVERY/'S1小结第2版-审阅稿.md').write_bytes(members['S1小结第2版-审阅稿.md'])
  76. receipt = {'file':str(target),'bytes':target.stat().st_size,'sha256':digest(target.read_bytes()),
  77. 'templateSha256':digest(template_path.read_bytes()),'manifestChecked':'4/4',
  78. 'taskKey':task['taskKey'],'version':task['version'],'remoteWrites':0,'targetSaved':False}
  79. (WORK/'first-live-S1-v2-package-receipt.json').write_text(json.dumps(receipt,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
  80. print(json.dumps(receipt,ensure_ascii=False,indent=2))
  81. if __name__ == '__main__':
  82. main()