prepare-S5-classroom-next.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. """Prepare S5 templates and handoffs locally. Does not call any business API."""
  2. from copy import deepcopy
  3. from pathlib import Path
  4. import hashlib
  5. import json
  6. ROOT = Path(__file__).resolve().parents[1]
  7. DOCS = Path('F:/文档/工作坊当前资料')
  8. OUT = ROOT / 'work/S5-classroom-next-20261103'
  9. assert not OUT.exists()
  10. OUT.mkdir()
  11. BASE = 'KSF-真实资料-20261102-01'
  12. S4 = json.loads((ROOT / 'work/S4-classroom-next-20261103/05-project-S4-save.template.json').read_text(encoding='utf-8'))
  13. accepted = json.loads((ROOT / 'work/live-S4-independent-acceptance-20261103.json').read_text(encoding='utf-8'))
  14. assert accepted['ok'] and accepted['artifactId'] == 'U3_W7WDliP'
  15. original = Path('C:/Users/JSHY/.codex/attachments/47f3dfe1-3de6-42a3-8709-ce63f100891c/Pasted text.txt').read_bytes()
  16. with (ROOT / 'work/S5-submitted-draft-20261103.txt').open('xb') as f:
  17. f.write(original)
  18. def put(name, value):
  19. with (OUT / name).open('x', encoding='utf-8', newline='\n') as f:
  20. f.write(value)
  21. def put_json(name, value):
  22. put(name, json.dumps(value, ensure_ascii=False, indent=2) + '\n')
  23. def para(title, text):
  24. return {'kind': 'para', 'title': title, 'text': text}
  25. def table(title, cols, rows):
  26. return {'kind': 'table', 'title': title, 'data': {'cols': cols, 'rows': rows}}
  27. pending = list(S4['pendingQuestions']) + [
  28. '三行分区呈现(净含量/面饼/件数)能否被正确理解,会不会反而更乱。',
  29. '信息呈现是否增加购买负担(货架识别、比价、货架停留)。',
  30. '包材版面、渠道素材能否执行,改动成本未知。',
  31. '“可选搭配”会不会被读成“必须加”,从而反向暗示原份量不足。',
  32. '研究中记录为何补主食的方式本身会不会影响行为,需要评估记录负担和观察者效应。',
  33. ]
  34. inheritance = ['原S4问题'] * 8 + ['承原1、4', '承原4、6', '承原7', '承原8', '承原3、6']
  35. name = '燃魂份量选择提示与搭配指引(待验证概念)'
  36. concept = {
  37. 'name': name,
  38. 'targetAudience': '拟研究把燃魂拌面用于一次用餐、需要判断一份与当次食量是否适配的人;是假定研究范围,非已证实人群。',
  39. 'usageScenario': '购买前与制备前判断份量和做法;当次目的可为正餐、加餐或其他,地点、时间、频率等未知。',
  40. 'specification': {
  41. 'designScope': '份量与制备信息布局及可选搭配指引;效果待验证,尚未确定新品实物规格。',
  42. 'packaging': '待核;未选定拟新品包装形态,不把P1图示当目标规格。',
  43. 'netWeightG': None, 'noodleWeightG': None, 'packCount': None, 'currentSku': None,
  44. 'informationLayout': ['单份含料净含量单列', '面饼重量单列', '组合件数单列'],
  45. 'preparation': '拟与后续明确的实际商品包装说明一致;本轮不设煮制时间。',
  46. 'optionalPairing': '可选搭配与规定步骤分开;不要求加菜,也不承诺搭配解决份量不足。',
  47. },
  48. 'valueProposition': '待验证:份量与做法信息集中呈现,帮助使用者判断是否适合当次用餐;不承诺更饱或购买提升。',
  49. 'flavor': '待核;燃魂是系列线索,不据此确定具体口味。',
  50. 'sellingPoints': ['待验证:份量与做法一处看明白。', '待验证:规定步骤与可选搭配清楚分开。'],
  51. 'productionSteps': [], 'pendingQuestions': pending,
  52. }
  53. design_rows = [
  54. ['份量信息', 'P1标题117g×5与参数585克算术一致;单袋图目视含料117克、面饼90克。仅是页面/图示观察。', '拟将单份含料净含量、面饼重量、组合件数分三行标示;一包、一组与一次用餐分开。', '当前及拟新品SKU/规格未知,信息理解未测试;重量不直接证明饱腹程度。'],
  55. ['煮制信息', 'P1图示6—7分钟;P2作者历史自述自己煮10分钟才软。图示与个体叙述分别保留。', '拟按后续明确商品的实际包装说明呈现制备信息,与可选搭配分开。', '没有购买路径理解测试;P2不证明包装错误或信息位置问题,不设10分钟为新标准。'],
  56. ['可选搭配', '两篇份量自述中有额外加食,但原因不全清楚;没有本轮干预或食用结果。', '搭配只作可选信息,不要求加菜;允许不搭配,保留真实用餐结果。', '会不会被理解为必须加菜或暗示原份量不足,额外负担与效果均未知。'],
  57. ['增量/双规格', '现有材料不足以支持选定增量幅度或两档规格。', '仅作有条件的备选;明确商品与用途、取得份量问题证据后再比较。', '克数、价格、袋数、配方、包装、产线及可行性未知;不声称燃魂已有或将推出加量版。'],
  58. ]
  59. spec_rows = [
  60. ['方案性质', '信息布局与可选搭配指引的设计提案', '未验证设计效果,不是已定新品'],
  61. ['净含量/面饼重量/组合件数', '目标值均待核;结构化数值为null', 'P1标示585克、含料117克、面饼90克及117g×5只作各自页面/图片参考'],
  62. ['包装形态/当前SKU/具体口味', '均待核', '页面对象不等于已取得样品,旧帖/P1/P2不合并SKU'],
  63. ['信息布局', '拟三行分别呈现净含量、面饼重量与件数', '是拟定设计,尚未验证理解效果或版面可执行性'],
  64. ['制备说明', '待明确实际商品后与其包装说明一致', '不把归档6—7分钟或作者10分钟设为拟新品标准'],
  65. ['配方/配料', '未取得;formulaItems=[]', '不造materialId、比例、用量或供应商,不以空数组认定无需配料'],
  66. ['生产工艺', '未取得;productionSteps=[]', '不造processId;用户使用、研究和待核步骤不充作生产工序'],
  67. ['成本/价格/合规', '资料未取得,保持未知', '不填0、不作正向可行性评分,不声称低成本、合规通过或可量产'],
  68. ]
  69. quote_rows = [[e['claim'], e['quotes'][0]['text'], e['sourceId']] for e in S4['report']['evidence'].values()]
  70. validation_rows = [
  71. ['信息表达', '未来用明确标为概念稿的说明布局做理解观察;目标、对象、记录方式及规则事前确定。', '出现误读、阅读或购买负担时调整版式与文案。', '资源不足或预先规则触发时暂停该设计;无差异不等于已证明无效。'],
  72. ['份量适配', '有明确商品与实际食用观察后,分别记录用途、习惯、提前加主食与吃后补食的原因。', '习惯与补偿可并存;动机混杂时继续区分,不因习惯存在便排除份量问题。', '缺对象/行为证据则不下结论;不凭未定义的“多数”判定整个份量假设无效。'],
  73. ['增量/双规格', '份量问题证据足以提出比较后,再事先确定规格、资源、方法和判断规则。', '证据或执行条件不足时调整问题,保留其他解释。', '本轮不实施;暂不推进不等于已经证明方案没有需求。'],
  74. ['企业可行性', '取得配方、工艺、成本、包材、渠道及相应合规依据后再评。', '依据冲突或条件不满足时调整方案。', '资料未取得则判断为未知,暂停可行性判断,不认定不可行或可量产。'],
  75. ]
  76. chapters = [
  77. {'id': 'scope', 'title': '方案范围与人群场景', 'stage': 'S5', 'sections': [
  78. para('方案摘要', '把一份面的份量、份数、制备方式和可选搭配集中呈现,供购买前与制备前判断是否适合当次用餐。这是目前可展开比较的设计提案,不是已验证需求;不承诺仅靠文案解决吃不饱。'),
  79. para('拟研究人群与场景', concept['targetAudience'] + concept['usageScenario'] + '年龄、购买力和购买身份未知,不补造学生夜宵或加班白领等事实画像。'),
  80. para('拟定使用路径', '购买前判断份量与做法→按后续明确商品的实际包装制备→按本次目的选择是否搭配并食用。这是设计路径,没有真实消费者执行结果。'),
  81. para('研究记录单列', '若后续开展研究,可另行记录为何补主食;记录方法、时机和负担需审阅。它不作为产品使用的必需步骤,也不默认能无干扰地观察行为。'),
  82. ]},
  83. {'id': 'design', 'title': '设计内容与候选卖点', 'stage': 'S5', 'sections': [
  84. table('设计对照', ['设计项', '已有依据及边界', '拟定设计', '仍需验证'], design_rows),
  85. para('候选文案', '待验证文案:“份量与做法,一处看明白。一包多少、一组几包、怎么煮,都写在一处。”这是待制作和验证的设计,不是现行包装或已验证卖点。'),
  86. para('提出设计的理由', 'P1提供页面参数与图示标示,P2提供历史个体制备叙述;两者均未证明“信息分散”或购买路径不清。集中呈现与可选搭配分区是设计假设,理解、购买判断、购买行为与饱腹体验分别验证。设计、包材与渠道执行成本未知。'),
  87. para('增量与承诺边界', '增量/双规格仅为条件分支,不给克数、价格或配方;不声称燃魂已有加量版。候选文案不作管饱、健康营养、成本最优或固定分钟必熟的承诺。'),
  88. ]},
  89. {'id': 'spec', 'title': '规格与执行条件', 'stage': 'S5', 'sections': [
  90. table('设计、观察与未知', ['项目', '本方案状态', '依据或约束'], spec_rows),
  91. para('结构化口径', '本轮方案结构可记录待核规格对象、候选卖点和13项未决问题。空配料与工艺数组表示未取得依据,不表示产品不需要原料或工艺。方案保存与阶段报告分别用专用命令,不创建产品、SKU、原料或工艺实体。'),
  92. ]},
  93. {'id': 'evidence', 'title': '证据与适用边界', 'stage': 'S5', 'sections': [
  94. table('主引文', ['支持范围', '逐字引文', 'sourceId'], quote_rows),
  95. para('历史个案与图示分开', 'cq-03/cq-04为2023年两个显示账号的历史自述。cq-04同文肯定味道;加蛋肠原因未知,cq-03加挂面的原因明确但其他配菜动机未知。P1是公开高清图片页观察,P2为2023年个人叙述;都未核购买身份,不拼成同SKU。来源可支持提出设计,不能证明设计有效或市场普遍需求。'),
  96. ]},
  97. {'id': 'validation', 'title': '验证路径与未决问题', 'stage': 'S5', 'sections': [
  98. table('继续、调整与暂停', ['维度', '何时继续', '何时调整', '何时暂停或不下结论'], validation_rows),
  99. table('待补问题', ['序号', '待核实问题', '承接关系'], [[str(i + 1), q, inheritance[i]] for i, q in enumerate(pending)]),
  100. para('阶段结论', '已形成可审阅的信息提示与搭配指引概念;设计效果、当前需求、实物、市场和量产验证未完成。计划保存后项目保持S5 / in_progress;本轮执行不包含completed、员工确认、企业审批或正式发布。'),
  101. ]},
  102. ]
  103. report = {
  104. 'subtitle': '份量与做法信息呈现的待验证概念,规格与效果待核',
  105. 'executive': [
  106. '基于已验收S4公开资料桌面评估形成设计概念,非试吃、市场或量产结果。',
  107. '主方案是份量选择提示与可选搭配指引;三行信息布局是设计假设,不称已经发现信息分散。',
  108. '增量或双规格仅为有条件的备选;规格、口味、配方、工艺与成本尚未确定。',
  109. '沿用4来源/4主引文;保留S4原8项缺口并增加5项设计问题,共13项。',
  110. ],
  111. 'method': '依据S4固定内容与既有来源验收进行桌面设计和证据边界审阅;本轮没有新采集、称量、试吃、招募、问卷、A/B测试或市场比例估计。候选文案与信息布局为拟定设计。',
  112. 'sources': S4['report']['sources'],
  113. 'chapters': chapters, 'evidence': deepcopy(S4['report']['evidence']),
  114. }
  115. title = '燃魂份量选择提示与搭配指引|S5桌面方案 第1版'
  116. raw_parts = [title, *report['executive']]
  117. for chapter in chapters:
  118. raw_parts.append(chapter['title'])
  119. for section in chapter['sections']:
  120. raw_parts.append(section['title'])
  121. if section['kind'] == 'para':
  122. raw_parts.append(section['text'])
  123. else:
  124. raw_parts.append(' | '.join(section['data']['cols']))
  125. raw_parts.extend(' | '.join(row) for row in section['data']['rows'])
  126. raw_text = '\n\n'.join(raw_parts)
  127. tasks = {
  128. '01-project-S5-progress.template.json': {'sessionId': None, 'projectId': None, 'toStage': 'S5', 'state': 'in_progress',
  129. 'note': 'S4公开资料桌面评估U3_W7WDliP已保存并独立验收;进入S5形成待验证的信息提示与搭配指引概念,不代表实物、市场或量产验证完成。'},
  130. '02-concept-save.template.json': {'sessionId': None, 'projectId': None, 'version': 1, 'concept': concept, 'formulaItems': []},
  131. '03-project-S5-save.template.json': {'sessionId': None, 'projectId': None, 'taskKey': BASE + ':S5', 'version': 1, 'stageCode': 'S5',
  132. 'title': title, 'artifactType': 'concept', 'category': '拌面', 'rawText': raw_text, 'sourceIds': deepcopy(S4['sourceIds']),
  133. 'pendingQuestions': pending, 'report': report},
  134. }
  135. for filename, task in tasks.items():
  136. put_json(filename, task)
  137. request_specs = [
  138. ('project-status', BASE + ':portion-fit-project:S5:precheck:v1', '00-project-status.json'),
  139. ('project-progress', BASE + ':portion-fit-project:to-S5:v1', '01-project-S5-progress.json'),
  140. ('project-status', BASE + ':portion-fit-project:read-S5:v1', '00-project-status.json'),
  141. ('concept-save', BASE + ':portion-fit-project:concept:save:v1', '02-concept-save.json'),
  142. ('project-status', BASE + ':portion-fit-project:concept:read:v1', '00-project-status.json'),
  143. ('workshop-save', BASE + ':S5:save:v1', '03-project-S5-save.json'),
  144. ('workshop-read', BASE + ':S5:read:v1', '03-project-S5.read.json'),
  145. ('project-status', BASE + ':portion-fit-project:S5:read-final:v1', '00-project-status.json'),
  146. ]
  147. runbook = {'sessionId': 'btrFqkz0yw', 'projectId': 'S_CikxZeBs', 'opportunityId': 'QE9hjtCj8E',
  148. 'priorS3ArtifactId': 'XF4h-Z7l5o', 'priorS4ArtifactId': 'U3_W7WDliP', 'taskKey': BASE + ':S5',
  149. 'steps': [{'step': i + 1, 'command': cmd, 'requestId': req, 'inputFile': inp} for i, (cmd, req, inp) in enumerate(request_specs)],
  150. 'expectedFinal': {'stageCode': 'S5', 'progressState': 'in_progress', 'projectArtifactCount': 3,
  151. 'projectEventCount': 7, 'conceptVersion': 1, 'formulaItemCount': 0, 'pendingQuestions': 13,
  152. 'newCommittedWrites': 3, 'formalReportPublished': False, 'employeeConfirmed': False, 'enterpriseApproval': False}}
  153. put_json('runbook.json', runbook)
  154. markdown = ['# ' + title, '', '待验证概念;未执行生产保存,系统阶段仍为S4 / in_progress。', '']
  155. markdown += [p + '\n' for p in report['executive']]
  156. for chapter in chapters:
  157. markdown += ['## ' + chapter['title'], '']
  158. for section in chapter['sections']:
  159. markdown += ['### ' + section['title'], '']
  160. if section['kind'] == 'para':
  161. markdown += [section['text'], '']
  162. else:
  163. data = section['data']
  164. def cell(value):
  165. return str(value).replace('|', '\\|').replace('\n', '<br>')
  166. markdown += ['| ' + ' | '.join(data['cols']) + ' |', '| ' + ' | '.join(['---'] * len(data['cols'])) + ' |']
  167. markdown += ['| ' + ' | '.join(cell(v) for v in row) + ' |' for row in data['rows']]
  168. markdown += ['']
  169. put('S5桌面方案第1版-审阅稿.md', '\n'.join(markdown) + '\n')
  170. review = '''# S5桌面方案审阅与定稿
  171. 2026年11月3日。王刚001提交的文字草稿可进入固定材料准备,无需再退回重复改稿。Codex已直接校正以下内容,定稿正文见同包《S5桌面方案第1版-审阅稿.md》,具体持久化输入以三份固定模板为准。
  172. 1. 删去“P1信息分散”这一事实断言。现有参数与图示核查没有验证信息位置或购买路径,集中呈现是设计提案。
  173. 2. P2只支持作者历史制备叙述,不能证明包装错误、说明位置问题或设计有效;“无任何证据”改为“现有材料不足以支持选定增量幅度/两档规格”,避免抹掉份量历史线索。
  174. 3. 不用未定义的“多数为习惯”判断整个份量问题停止。习惯与补偿可能并存;资料缺失时暂停相应判断,不写证明无效或不可行。
  175. 4. 将研究中的“记录为何补主食”从产品使用必经步骤中移出,另列记录负担与观察者效应待验证。
  176. 5. “四份来源原站核查与购买身份核验沿用50号”更正为“原站核查沿用既有验收;购买身份仍未独立核实”。作者、购买与普遍需求不冒称已核。
  177. S4原8条pendingQuestions逐字保留;新增5条设计问题,共13条,方案输入与报告及可见表完全对应。四段主引文直接从S4固定报告取值,P1使用真实换行,不把Markdown的<br>写进quote。所有下划线编号保留。
  178. 未来concept仅记录待验证的信息提示与搭配指引概念。规格数值null,formulaItems=[]、productionSteps=[];没有创建原料/工艺或把研究步骤当生产步骤。pendingQuestions按现网处理写入项目meta.pendingQuestions和保存响应,不要求KfConcept上有同名字段。
  179. 本轮Codex只做本地审阅准备及另行留证的生产只读预检,没有运行project-progress/concept-save/workshop-save。拟执行后为S5 / in_progress、1个concept v1、项目3份报告及7条事件,不自动completed、员工确认或正式发布。概念、报告和正文来源是三个不同编号;方案并未通过conceptRef直接写进KfArtifact,核对方式为同项目、版本、字段及事件对应,不造不存在的Pointer。
  180. 王刚001两条业务只读及身份登记调用编号、远端目录8文件/权限/退出码依据提交回报,Codex没有读取该远端文件系统。本轮未将这些回报升级为Codex独立核实。归档原稿保持原字节,系统对象精确编号按runbook.json,不按口头省略下划线的文本拼命令。
  181. 本包供53号运维落地预检,通过后按54号在王刚001真实消息中执行。沿用用户已选方向,不重新问选项、不要求先提供实物,不重导资料、不建第二套卡或项目。执行前状态或合同有新差异则停核,不能用本地模拟代替生产回读。
  182. '''
  183. put('52-S5桌面方案审阅与定稿.md', review)
  184. hash_lines = '\n'.join(f"{n}:{len((OUT/n).read_bytes())}B,sha256={hashlib.sha256((OUT/n).read_bytes()).hexdigest()}" for n in tasks)
  185. ops = f'''运维001,请按本包准备S5固定模板;本轮只落地与只读预检,不代跑业务。
  186. 先读52号审阅裁定及S5审阅稿。沿用用户选定的燃魂份量与用餐目的适配,方案为“份量选择提示与搭配指引”(待验证概念),不是已验证需求或量产新品。当前会话btrFqkz0yw、项目S_CikxZeBs,S4/in_progress;S4成果U3_W7WDliP已验收,不重做S4、不再问方向、不要求用户先交实物。
  187. 将三份template原样落地/opt/data/ksf-executor-20261103/S5-classroom-next-01/,root:root 0644,hermes实读。三份均sessionId=null/projectId=null,不可直接提交;本轮不生成绑定ID的可执行输入。
  188. 固定指纹:
  189. {hash_lines}
  190. 按FILES.sha256核包,按现网专用命令合同核project-progress / concept-save / workshop-save / project-status / workshop-read。合法转阶段命令是project-progress,不能写project-transition;artifactType=concept,reportType按workshop_auxiliary、artifactSubtype=classroom_task核,不从通用导入表字段推定命令形状。发现差异只报不改Skill/Function/schema/wrapper或放宽校验。
  191. 核同一当前真实会话/员工/Agent归属,项目仍S4/in_progress,机会QE9hjtCj8E与项目互链;S3基线XF4h-Z7l5o、S4评估U3_W7WDliP恰两份项目成果、四条事件、无concept。只读核四来源/证据对及sourceRef、原文quote、productRef=null:m1OQx9aynA/_9mUml0wmh,QdeMcyTTjX/2bFGP7axMo,PjORcvaRHn/xHGV_mRWcG,5S_Y4IgT5q/CeAEIwymTa,不重导或重新采集。
  192. 查本项目KfConcept(包括currentConceptRef未挂载的既有版本)与KSF-真实资料-20261102-01:S5系列,无v1或其它版本;查runbook.json八个requestId在KsfOperation的成功写入审计、相关对象与本地回执。读取requestId不保证入KsfOperation,不能据该表声称读命令从未运行。已有对象或回执即停报编号,不换键、删记录或升级版本绕开。
  193. 核目标/opt/data/ksf-workshop-private/S5-classroom-next-01/及54号所有将生成的输入、stdout/stderr/退出码目标均不存在、无在途;目录仅不存在才新建hermes:hermes0700且确认为空。保留S5-live-00/、S1—S4各版、原第08失败与恢复回执。不要复用或覆盖旧目录。
  194. 静态自检:pendingQuestions方案与报告恰13条且逐字相同;前8条逐字等于S4;可见待补表13行逐项对应;4来源/4主引文与S4完全相同;specification未知数值为null;formulaItems=[]、productionSteps=[];无opportunity对象,无新project-create。specification是非空JSON对象,不为满足字段临时填数字。
  195. 本轮不执行project-progress/concept-save/workshop-save,不更新任何旧业务记录或前端,不正式发布。回报模板字节/完整哈希、合同、身份归属、来源、版本/回执/在途及目标文件检查;通过后由用户转交54号王刚001真实消息执行。按原业务键一次执行,任一步失败即停。包内preflight与隔离模拟只证明本地准备,不代替现网结果。
  196. '''
  197. put('53-发给运维001-S5方案与报告准备.txt', ops)
  198. steps_text = '\n'.join(f"{i+1:02d}. {cmd};input={inp};requestId={req}" for i, (cmd, req, inp) in enumerate(request_specs))
  199. actor = f'''王刚001:本单用于已审阅的S5方案及课堂报告保存。用户此前已选“燃魂份量与用餐目的适配”,本条转交表示继续既有课堂测试,不再问选择。先确认53号落地预检通过,52号校正已纳入固定模板。沿用会话btrFqkz0yw、项目S_CikxZeBs、机会QE9hjtCj8E;不新建会话、卡或项目。
  200. 一、当前消息身份与输入
  201. 无参数运行ksfregistercurrent_employee(可信运行时正式工具名以现有接入为准),核完整agentProfileKey与员工/发送者/会话及S4归属一致。不得用旧截断键、姓名或复制身份JSON代替本条真实核验;不一致即停。跨日不自动新会话。
  202. 三模板位于/opt/data/ksf-executor-20261103/S5-classroom-next-01/;完整哈希如下:
  203. {hash_lines}
  204. 执行留存/opt/data/ksf-workshop-private/S5-classroom-next-01/必须为53号已确认的空目录0700。先核目标不存在及无历史回执/在途;每份绑定输入都从模板读进内存,只填sessionId=btrFqkz0yw、projectId=S_CikxZeBs,逐字段差异恰这两处,O_EXCL独占新建0600。所有读输入、每步stdout.json/stderr.log/exitcode也独占新建0600;已有即停查,不覆盖。复用本单只读输入是规定操作,不重建同路径。
  205. 二、八步固定顺序与requestId
  206. {steps_text}
  207. 第01步:独占新建00-project-status.json,仅含{{"sessionId":"btrFqkz0yw","projectId":"S_CikxZeBs"}};核两层ok+EXIT0、项目S4/in_progress/live/workshop、同归属,S3基线XF4h-Z7l5o与S4报告U3_W7WDliP恰两份、事件四条、concept=null、formulaItems=[]。任何变化先停,不套旧状态继续。
  208. 第02步:从01-project-S5-progress.template.json绑定两ID生成01-project-S5-progress.json,执行一次project-progress;应S5/in_progress、projectId不变、enterpriseApproval=false,取eventId。
  209. 第03步:复用00-project-status.json;应S5/in_progress、成果仍两份、事件五条,最后一条stage_changed,无concept。
  210. 第04步:从02-concept-save.template.json绑定两ID生成02-concept-save.json,执行一次concept-save;取conceptId/version=1/projectId/eventId,formulaItemIds=[]、pendingQuestions13条逐字等模板、enterpriseApproval=false。
  211. 第05步:复用00-project-status.json。核project.currentConceptRef→KfConcept/新conceptId;concept.objectId、projectRef、version1、dataMode=live、recordState=active;name/targetAudience/usageScenario/specification/valueProposition/flavor/sellingPoints/productionSteps逐项等模板,specification含null也做深比。formulaItems=[];项目meta.pendingQuestions13条逐字等模板;事件六条,末条concept_saved;项目成果仍两份。pendingQuestions不要求在KfConcept有同名字段,不能因此重存或补写。previousVersionRef可缺/null,不声称已有版本链。
  212. 第06步:从03-project-S5-save.template.json绑定两ID生成03-project-S5-save.json,执行一次workshop-save;取新artifactId/sourceId,sessionId及projectId沿用,version1/S5,opportunityId=null。方案conceptId与报告artifactId是不同对象;本报告输入没有conceptRef,不臆造直接Pointer。
  213. 第07步:根据第06实际返回的artifactId,独占新建03-project-S5.read.json,仅含{{"className":"KfArtifact","objectId":"<新artifactId>"}}。精读核下节所有字段及可见表。
  214. 第08步:复用00-project-status.json;核项目S5/in_progress,成果恰3份(旧S3、旧S4、本次S5),concept仍第04同一v1,formulaItems=[],meta.pendingQuestions13条;事件恰七条按序:原project_created、task_submitted、stage_changed、task_submitted,再本次stage_changed、concept_saved、task_submitted。最后两条分别为概念保存与S5报告提交。本轮不执行completed,不自行再转阶段。
  215. 三、命令格式与留存
  216. 写:node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs <project-progress或concept-save或workshop-save> --agent-profile <本条已核完整agentProfileKey> --input /opt/data/ksf-workshop-private/S5-classroom-next-01/<本步输入名> --request-id <本步完整requestId>
  217. 读:node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs <project-status或workshop-read> --input /opt/data/ksf-workshop-private/S5-classroom-next-01/<本步输入名> --request-id <本步完整requestId>
  218. 八步stdout/stderr/退出码按01-precheck、02-to-S5、03-status-S5、04-concept-save、05-concept-status、06-S5-save、07-S5-read、08-final-status分别留存;保存结果编号另做独占JSON记录。每步核外层ok、entryResult.ok、EXIT=0,stderr如有内容逐条解释,不以某个空字段直接宣布失败或通过。
  219. 四、S5报告精确回读
  220. 核artifactType=concept、reportType=workshop_auxiliary、artifactSubtype=classroom_task、stageCode=S5、version1、schemaVersion=ksf.v0.7、dataMode=live、dashboardState=workshop、reportSeriesKey={BASE}:S5。ownerSession/sessionRef→btrFqkz0yw,projectId/projectRef→S_CikxZeBs,Agent归属与既有一致,sourceRef→第06返回新正文sourceId,tags含拌面与课堂过程成果。
  221. rawText逐字等模板;sourceIds恰m1OQx9aynA/QdeMcyTTjX/PjORcvaRHn/5S_Y4IgT5q;evidenceCount4,四段主引文含P1换行逐字与report.evidence全块一致。report.chapters/method/sources/subtitle逐项等模板,executive仅在最前预期增加“课堂过程成果,未经员工正式确认;待验证项不作为市场结论。”,report.title/generatedAt为服务新增。
  222. meta.workshop.pendingQuestions恰13条且逐字等方案输入与项目meta.pendingQuestions;report中待补问题表13行、原8条与新增5条承接关系全等;其余设计对照/规格/主引文/验证路径表均全块比对。不要只核文字rawText而漏掉可见表。
  223. boardVisible只在保存响应核为true,不要求回读存在。正式发布/员工确认在保存响应为false,dashboardPublishedAt为null;字段缺失不等于“已正式发布”。enterpriseApproval=false,未开生产浏览器则browserVerified=false,后续Codex按新编号验收;本地预览不能替代生产浏览器。
  224. 五、停止与边界
  225. 任一步失败、超时或不确定立即停,保留原requestId、输入、所有已返回编号与完整回执;不自动重试、换键、删记录或回滚。错误不等于零写入;只读失败不因此重做任何save/progress。交Codex按编号核对后决定恢复方式。
  226. 预期新增仅1个KfConcept v1、1份KfArtifact及其正文KfSource、3条事件和3条写入审计;同一项目达到S5/in_progress。方案pendingQuestions更新项目meta,不覆盖旧报告的8条缺口。无新机会卡/项目/产品/SKU/配方原料/生产工艺,不重导来源,不改S1—S4、Skill/Function/schema/wrapper/前端,不采购、招募或试吃,不代员工确认或正式发布。当前实物、市场和量产验证仍未发生。
  227. 最终回报八步requestId、两层invocationId、EXIT及stderr,conceptId/artifactId/sourceId/eventId、逐项精确核对结果、项目阶段/成果/事件、实际留存清单和权限。不要把课堂方案存入库说成当前产品已定型或需求已证实。保留S5-live-00/及全部旧回执。
  228. '''
  229. put('54-发给王刚001-S5方案与课堂报告保存.txt', actor)
  230. put('README.txt', 'S5固定材料包:先52审阅,再53运维只读预检,通过后54由王刚001真实消息执行。\n三模板sessionId/projectId为null,不可直接提交。顺序为S4前检→project-progress转S5→concept-save并核→S5报告保存并核→最终状态。\n只记录待验证概念,配方工艺缺资料时数组为空、数值待核。不自动completed或正式发布。不含凭据、浏览器档案或完整私聊。\n')
  231. put_json('preflight.json', {'ok': True, 'at': '2026-11-03', 'localStructureOnly': True, 'packagePreparationBusinessApiCalls': 0,
  232. 'productionBusinessWrites': 0, 'saved': False, 'browserVerified': False,
  233. 'sourceIds': S4['sourceIds'], 'evidenceCount': 4, 'pendingQuestionCount': 13,
  234. 'originalS4PendingCount': 8, 'newDesignPendingCount': 5, 'rawTextCodepoints': len(raw_text),
  235. 'chapters': len(chapters), 'tables': [{'title': s['title'], 'cols': len(s['data']['cols']), 'rows': len(s['data']['rows'])}
  236. for c in chapters for s in c['sections'] if s['kind'] == 'table'],
  237. 'inputDraftSha256': hashlib.sha256(original).hexdigest(),
  238. 'scopeNote': 'Separate live read-only preflight and isolated simulation are recorded outside this initial local-structure preparation.'})
  239. print(json.dumps({'ok': True, 'prepared': str(OUT), 'templates': len(tasks), 'rawTextCodepoints': len(raw_text),
  240. 'pendingQuestions': len(pending), 'businessApiCalls': 0, 'productionBusinessWrites': 0}, ensure_ascii=True))