Commit Graph
22 Commits
Author SHA1 Message Date
ChaiHaoandClaude Opus 5 e5cfca638f 运营中心撤掉「数据」区;流程包首页重排:卡有层次、⋯ 管编辑与删除、新建变幽灵卡
**运营 · 数据(活动记忆四层)整个撤掉** —— 记忆的放行口还没成形,摆一个空区不如
不摆;真要做时随「数据资产」一并重开。区表、面归属、shell 映射、KnowledgeHome /
MemPage、活动详情里那枚入口一并清干净;旧深链 `#/gov/memory/layers` 落运营首页
(测试跟着改口径,不留死链)。运营中心自此六区。

**流程包首页**:
  · 「编排要素:四种」这枚计数删了 —— 网关早废,它是句没人用的口号;
  · 卡片重做。从前是「头 + 芯片行 + 一大片空 + 脚注」,158 高全靠留白撑;
    现在两段:头(图标 · 名 · 标识 · 生效版 · ⋯)+ **数据板**(活动/审批/边闭合/版本
    四格等宽、竖线分隔)。段段有内容、段间有线,高矮由内容说了算 —— 实测 111 高;
  · 卡上 **⋯ 菜单**:编辑(就地改名,落在卡面那一版上)/ 删除(整条流,菜单原地翻成
    确认,不另弹窗);
  · **新建按钮变幽灵卡**:虚线卡住在货架末位,与真卡同宽同高(111/111)。

删整条流是新能力:`DELETE /api/gov/process/:id` + `process.deleteFlow`。
生效版这道闸不设(明示动作,不是手滑),在途单那道照设(PinnedVersionError → 409);
**「还有调起规则指着它」由路由层反查** —— process 是 L7,不该知道 trigger 的存在。

实测(丁子乔账号):改名往返、删除被 2 条调起规则如实挡回、幽灵卡建 `tmp-drop`
再删掉整条流,库里回到只剩 unified-quote v4,试跑数据已清。五闸绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 22:38:52 +08:00
ChaiHaoandClaude Opus 5 f631b5b8bb 版本号自己定:点编辑,整列版本进编辑态,取消/保存收尾
用户的目的是**自己改版本号**,不是让系统替他递增;顺带把上一轮那些
指示性交互全撤了 —— 这软件给专业业务设计人员用,不需要「点我改」的提示条。

界面:
  · 顶栏 编辑 → 取消 / 保存(与流程图卡「编辑 → 保存」同一套手势);
  · 行内两个字段:号(自己编)与名(各版不同),行尾 删除;
  · 改动攒在草稿里,按保存一趟落库;取消原样退出;
  · **删掉**:改名指示条、「点我改」虚下划线、`删掉 v4`/`激活 v4` 这类复述钮名的浮字。
    只留下禁用态那一句(生效版先停用 / 还不闭合),那不是指示,是钮为什么点不动。

后端 —— 号从「系统递增」放开为「设计人员自己编」,两道闸是数据本身要求的:
  · 撞号拒(VersionTakenError,409);
  · 在途钉版拒改号(那些单正按这个号找定义,搬号等于抽它们脚下的地板);
  · 已完结的单**跟着改指**:号在三处(包的列、definition、workflow.packageVersion)
    一起改 —— 号只是这一版的名牌,搬号连引用一起搬,留痕仍指同一份定义。
    先前写成「跑过单就不许改号」,实测等于任何试跑过的包永远编不了号,是过闸。
  · 名不设闸:标签不参与流转,在途钉版也能改;改名只动这一版(推翻前一日的
    「及于整条流」)—— 各版本正是靠名字互相区分,全同名等于没名字。

路由 POST /api/gov/process/:id/:version/edit 取代 …/:id/rename(单一消费者是本仓前端);
CONTRACT.md 与产品设计.md 同步,api-report 快照随本次提交。

实测(丁子乔账号,测后已 reset-runs 清运行数据):v4→v2→v4 往返,列表、画布图签、
库里三处同步;在途单存在时改号如实报 409 并原样退回。五闸绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 22:15:19 +08:00
ChaiHaoandClaude Opus 5 c9ac058bdf 三面拆分⑯:流程包详情改三栏 · 字段只声明 · 版本可删可改名
**字段只声明,不做任何要求**(用户定):编排层不再碰字段 ——
  · 激活闸只剩两面:结构闭合(dangling-node / multi-default-edge)与活动装载
    (unknown-activity);字段类的检查(同名不同型、容忍分支被必填依赖)全部退役。
  · 节点属性卡的「数据接口」从「推来源」改成**纯声明**:照活动契约列出吃什么、吐什么,
    不标必填、不推来源、不提补全 —— 缺了怎么补是**活动内实现**的事,不在这一层现形。
  · 字段类型支持数组:统一写成 `T[]`(如 `any[]`),出口口的字段也带上类型
    (原来只有字段名)。实测 A2 的 benchmark 口显示「rows any[] · needSourcing boolean」。

**详情页改三栏**:左窄轨版本(132px) | 中流程图 | 右节点属性。
  · 版本条一版一行(v几 · 状态 · 点/边计数),动作只在**看着的那一版**上展开 ——
    窄栏放不下每行一排钮,也不该让人误点别版。
  · 删掉「激活 · 双判据」整卡:没选节点时,属性栏就地列出还差哪几条(点一条定位过去);
    两面都齐时盖一枚小检验章,不再占一整张卡。
  · 删掉节点拣选行 —— 直接在图上点节点。
  · 属性卡用包壳绝对定位:行高由中栏的图定,属性长了在卡内滚,不把图拉长
    (实测卡高 467、明细内容 1039、可滚,图框不受影响)。窄轨里属性小卡改单列竖排。
  · 顺带校正:上一轮那条「图框下沿对齐左列底栏顶线」的 20px 留白,
    三栏改版后左栏已无底栏,没有对齐对象了 —— 改回四边等距(18px)。

**版本可删可改名**(新):
  · 删:生效版不可删(先停用,ActiveVersionError → 409)、在途钉版不可删
    (那些单要按它跑完,PinnedVersionError → 409);删掉正看的那版会落到剩下的最新版,
    一版不剩就回货架。
  · 改名:名字是**这条流的身份**,一次改及于所有版本 —— 版本列表上只显示 v1/v2…,
    若只改一版的名,列表上什么也看不见,人会以为没生效。
  · 5 条单测守两道闸与改名口径;实测走查:改名落库 → 改回;生效版删 409、停用版删 200。

process CONTRACT.md 与产品设计.md 同步。五闸绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 16:14:54 +08:00
ChaiHaoandClaude Opus 5 8d50ceda6a 三面拆分⑭:宽进收敛 —— 起单不卡缺项,进活动受理归一,信息不全派补全单
用户定的逻辑:「调用活动,输入数据可以不定或者为空;进入活动后受理归一,
信息不全派补全单。」把收敛点从门口挪进活动 —— 三道门口闸全撤:

  · 触发装配:缺必填**不再入死信**(AssemblyResult 的 missing 改 partial,带上已装配的);
    装到多少带多少照样起单。死信只留给真错误(勾了确认没指定人这类配置错误)。
  · process.start:撤掉调用接口缺项闸 —— 从前缺一个字段整条流都起不来,
    现实是客户来信本来就常缺客户码,拦在门外等于要求外界替系统把信息备齐。
  · task.start(替代旧 AM 12.1「缺项不得启动」):缺必填的任务落成「等人」,
    缺的字段做成**补全卡**(elicitation,标签取 schema 的 title)发到负责人的
    任务对话流,并武装 human 监听(IntakeDesk 端口;不占活动的人卡预算)。
    答复只收契约声明过的键,合并进 data;齐了或答「没法填」才转 running 开跑;
    答一半就就缺的部分再派一张卡。等人期间谱一步不走、不扣步数。

  · 测试台预检改成**信息**不是闸:灯灭不拦起单,文案直说
    「抽不出的键由受理补全单找人收齐」。

5 条受理归一单测 + 全链实测:投一封只写「想问下工控板价格」的信(零键)→
流照起 → A1 落负责人工位、等人、补全卡在对话流 → 答卡(customerCode/rfqRef)→
任务醒、合并、谱跑 3 步到验收。修一处接线错误:监听 deadline 要 ISO-8601
时长(P2D)不是时刻,传错整个补全监听静默没装上。

trigger/task/process 三份 CONTRACT.md 与产品设计.md 同步(宽进纪律成文)。
五闸绿;契约快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 13:46:13 +08:00
ChaiHaoandClaude Opus 5 f9b2c07731 三面拆分⑬:触发确认链路 + 运营中心「工作流」区 —— 设计只管设计,运营管细节
**触发确认链路**(用户口述五步的第 4/5 步):
  接收邮件 → 分类识别 → 命中 RFQ 事件 → ④(可选,运营勾)系统消息到指定人
  工作台(带邮件内容)→ ⑤ 工作台点「确认起单」,流程才起。
  · 命中不等于必起:勾了「需人工确认」,装配好的入参存成待确认单
    (sys.trigger_pending,migration 0019),ConfirmDesk 端口把带来件内容的
    系统消息送指定人(eventRef 挂票号,消息列表就地贴确认状态与那枚钮)。
  · 先认领后起单:consumed_at 空才认得到 —— 两人同时点、一人点两次,都只起一条流;
    起单失败退回认领,不留死票。确认人记进 source.by,留痕可查。
  · 登记触发不走确认(登记那一下本身就是人在确认);勾了没指定人 = 配置错误入死信。
  · 4 条单测 + 全链实测:投信 → triggersHit=1 且零直起 → AI 工作台收到确认消息
    → 确认起单 → 二次确认幂等回同一流号 → source.by = AI。

**运营中心重排**(区名即运行时:工作流是流程包的运行时,任务是活动的运行时):
  · 新「工作流」区:在途看板(从任务区迁入)+ 运行配置 —— 每包的触发器
    起单开关 / 确认打勾 / 确认人,和节点负责人(sys.node_owner)。
  · 负责人三层:显式指派 > 运营配置 > 轮值 —— 合并点在 process.start,
    复用既有派发链一行不改。
  · 任务区瘦身成活动运行时(任务/审批/需处置);知识库区改名「数据」
    (与设计中心的知识库解开同名);系统区只剩通道实例。
  · 设计中心触发器详情页摘掉启停按钮 —— 设计只管设计,启停/确认全在运营。
  · 旧深链有改嫁:tasks/board → runs/board,tasks/trg → runs/conf。

reset-runs 补 sys.trigger_pending(待确认单是运行痕 —— 和上回漏 trace_span 同类洞)。
邮件分类 Agent(第 ② 步)现由抽键正则 + S-FEEL 代位,升级路径记进 debt.md。
产品设计.md 运营章、trigger/process 两份 CONTRACT.md 同步。

五闸绿;契约快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 13:09:16 +08:00
ChaiHaoandClaude Fable 5 5fc6dfdfe1 仿真测试台(后端):节点指派 + 全链 Trace —— 起单定向到指定账号,真实任务落工作台
用户定的形态:测试台是**后端检测活动与流程**的,真实任务照旧到工作台
(不然指定账号干嘛)—— 所以测试台只管「起单 + 看穿」,人那一环还是真的。

**节点指派**(迁移 0016 workflow.assignments):
- StartWorkflowInput 增 `assignments {节点id → 员工id}`;分派改三级取人:
  ①节点指派(点名的人在岗就是他)②员工组轮值 ③全员轮值 —— 指派是加法不是替换,
  点名的人不在岗退回轮值(单不能没人接);事件里如实记 `(节点指派)`
- **顺带修一个真 bug**:`node_instance.assignee_id` 写了但 `listInstances` 从没读回来
  (审批箱收件、员工面链条、治理列表全按它认人 —— 写了不读等于没写)

**全链 Trace**(新模块 trace,L2;迁移 0017 sys.trace_span):
- span 由 **Mastra Observability 组件**产出(执行不经 Mastra 图,所以手动打点),
  一开就落库、一收就补 —— 观测面不该比被观测的事晚(不走 exporter 的批量异步路)
- 打点覆盖:工作流 root → 节点(记着派给了谁)→ 活动内每次工具调用 / 模型调用 / 人卡
- **root span 挂在 `runtime.begin`**:那是「单已建好、还没进第一个节点」那一刻 ——
  挂在 start 返回之后就晚了,起单同步走到第一个节点,节点 span 会先出生、另起孤树(实测)
- 观测尽力而为:写 span 失败吞掉,不掀翻业务
- 三样东西各管各的:trace(机器读时间轴)/ 任务记忆(人读留痕)/
  **工作区报告(活动自持的 HTML)** —— 谁也不替代谁

路由:`POST /api/gov/testbed/run`(带指派起单)、`GET /api/gov/workflows/:id/trace`。

端到端六例(真库):指派生效 / 任务真落到被指派人手上并由他答单 / 下游节点同样生效 /
全链 span 逐层挂得上且耗时齐备 / 一条流一个 traceId / 不指派照旧轮值。五闸全过。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-29 01:43:49 +08:00
ChaiHaoandClaude Fable 5 7c609e852c Mastra 化阶段1:抽 FlowRuntime 端口 —— 编排层执行面成为可替换切面,解耦完全成形
目标(用户定):完全实现编排层与活动解耦。解耦线(TaskGateway 起活动 / 终态回报)
本就存在;缺的是 CONTRACT M5-1 那句「Mastra 编译器作可替换 adapter、usecase 不动」
的端口 —— 勘察坐实它此前不存在,状态机直接写在 usecase 里揉着五端口副作用编排。

- port.ts 立 FlowRuntime 端口:begin / onTaskTerminal / applyFailure /
  onApprovalVerdict / refreshState / terminate —— 执行面的全部
- 状态机整体迁入 internal/runtime/self-hosted-runtime.ts(逐字搬迁,行为零变化):
  enter(调度+组轮值分派+实例落 assignee)、advanceFrom(条件出边选路+死路诊断+
  分支不连坐)、失败走向四选一、审批裁决(边优先、声明兜底)、三态聚合投影、终止级联
- process-service 573→248 行薄壳:定义生命周期(保存/激活/校验)+ 起单前置
  (取生效版/入参校验/工作组快照/建单留痕)+ 五方法委托;
  ProcessServiceDeps.runtime 可注入 —— Mastra 编译产物从这里换入,路由/接线零动
- index 出口 FlowRuntime 型;CONTRACT.md 记切面与已核实的语义冲突清单
  (reopen 无原语待 spike / parallel 失败连坐须 step 内自吞 / abnormal 三态 /
  node_instance 投影必留);debt M5-1 → 切面已立

等价性护栏:process 49 单测 + 八例端到端真库,提取前后原样全绿 —— 行为零变化实证。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 23:12:04 +08:00
ChaiHaoandClaude Fable 5 f145bde875 三面拆分⑲:审批点卸岗位 —— 负责人一律运营时分配,绑员工组,实例记 assignee
模型(用户定):审批点不绑岗位;节点负责人是运营时分配的,绑定到员工组。

- 契约:approval 节点删 approver(功能属性只剩驳回走向/催办/委托消息);
  CLOSURE_VIOLATION_KINDS 删 missing-owner-position(岗位不入编排,无岗可核);
  治理审批列表删 approverPosition 字段
- 运行时统一口径(活动点=审批点):工作流绑了员工组按组轮值(组员并集最闲者先),
  没绑组全员轮值;staffId 稳定排序保证确定性,组员全不在岗退回全员。
  ADR-011「绑定先于策略」随岗位退场 —— 工作组快照的成员集合成为轮值池
- 实例记 assignee(迁移 0014 node_instance.assignee_id):分派结果是流转事实 ——
  审批箱收件(i.assigneeId === me)、裁决权(同一判据)、员工面链条报实际承接人、
  治理台审批列表报人,全部按它认,不再有岗位比对
- closure 卸 positions 入参;desk/desk-approval/governance-ops 三路由改口;
  web 属性卡删「审批岗位」选择与警示、positions 管线拆除;种子 A5 卸 PM;
  导出依赖文件审批段只留编排语义

测试:closure 25、save-package 6、seed 8、graph-to-pkg 5、端到端八例真库全绿;
五闸退出码全过。库:迁移 + v4 就地同步(唯一生效版)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 21:24:41 +08:00
ChaiHaoandClaude Fable 5 a10a7b2855 三面拆分⑰:编辑即其身 —— 保存写回本版,新版本只出自「新建」
用户问:为什么编辑一个版本、保存就自动多一个副本?那不是我要的。
旧机制「保存恒新增版本号」改为:

- savePackage:pkg.version 已存在 → **就地写回该版**(号不变、状态不变);
  `asNew: true`(新建按钮)或新流程首版才铸新版本号(服务端算,不生效)
- 底线三道,一道不少:
  ① **在途钉版不可变** —— 该版有未完结工作流(按 (包,版本) 钉定义)→ 409,
    一字不落库;「就地覆盖会改掉在途正在读的编排」由这道闸守,不再靠只插不改一刀切
  ② **生效版必须始终可运行** —— 写回生效版跑闭合闸,不闭合 412 带整份报告;
    未生效版画图不检查、随手存
  ③ **陈旧编辑不复活** —— 流程存在而版本号已被清理 → 409(实测:停在已删旧版上的
    标签页发来保存,把老内容静默复活成了新版本;StaleVersionError 拦死)
- store:新增 updatePackageDefinition(只许命中现存版本)+ countPinnedInFlight
- 前端:保存按钮「保存 v{n}」;新建走 asNew;文案全线「新建才长版本」
- 测试:save-package.test 六例(写回/钉版/热改闭合/asNew/防复活/首版);
  过程中揪出双 API 进程并存(旧进程霸端口跑旧代码)——已重启归一

实测:改名保存 → 版本列表仍唯一 v4 生效中,名字已写回;八例端到端全绿;
五闸退出码全过(api:check 提交后转绿)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 20:11:41 +08:00
ChaiHaoandClaude Fable 5 7f0d25c337 三面拆分⑭:纯编排第三轮 —— 活动卸岗位、卡点废除全真边、边号 E1–En 图上可选
活动契约不带负责人岗位(用户定):
- schema:活动节点删 owner(审批点的 approver 是「卡到人的工位」的功能语义,保留)
- 运行时:活动任务默认**全员轮值**(在岗候选并集、最闲者先、staffId 稳定排序,
  owner-resolved 事件岗位记「(全员轮值)」);运营分派机制落地后让位 —— 债已记
- closure:活动点不核岗位,missing-owner-position 只出自审批点
- 员工面链条:活动点报**实际承接人**(实例→任务→人),没起的如实报「轮值待派」;
  NodeInstance 公开 taskId/approvalId(店面读回,与端口型合流)
- 画布/导出依赖文件/属性卡全线摘岗位;属性卡活动点明说「分派归运营」

卡点画法废除,顺序流一律真边(用户定):
- 种子图重画:四条回跳/直达(E4 直接定价、E6 选型后定价、E10 不通过、E12 有异议)
  全部画成折线真边,车道分层零交叉(顶 y30 / y190 / y230 / 缝隙对开 / 底 y380);
  卡点节点删净,图=恰 10 语义节点 + 14 真边一一对应(测试守着)
- graph-to-pkg 卡点认亲机制删除;pkg-to-graph 直接按 from→to 认亲
- 元素盘删卡点件;图例、属性卡卡点分支、kindLabel/kindTone 随删

边号 E1、E2…(用户定):
- 语义边 id 即边号:种子 E1–E14;合并器铸号(旧边保号、新边取下一空号,回归测试守着)
- 画布线标带号(`E3 直接寻源`,无注记的边也标 `E1`);拣选条以边号立牌,
  **条件不再上按钮** —— 条件/映射只住属性面
- 边在图上可选中:FlowDiagram 连线加宽透明命中垫(边可点时注解层提到形状层上),
  点线即切属性卡到该边(实测 E4 命中)

测试:closure 28、advance-order 6、graph-to-pkg 5(新增铸号例)、seed 8、
端到端八例真库全绿(全员轮值下照跑);五闸退出码验过。
库:purge 后重种,唯一 unified-quote@v1 生效(双面判据齐)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 19:02:07 +08:00
ChaiHaoandClaude Fable 5 281f4ef084 三面拆分⑬:流程包纯编排化 —— 编排与传递契约归包,实现方式归库,激活看双判据
模型(用户定):流程包只决定活动编排 + 活动传递的契约,不决定活动的实现方式;
节点只分活动点与审批点;图只定名称与顺序,属性在卡上填,链接好实现即可运行;
激活 = ①Mastra 式图编排闭合完善 + ②活动装载,两面判据。

schema(库经外科手术清空,无历史包袱,网关彻底出局):
- contracts:删 gateway 节点变体 / GATEWAY_KINDS / GATEWAY_KIND_LABEL /
  'gateway-deprecated' 违规项 —— 节点四种:start/end(结构件)+ activity/approval
- routing:joinReady 整个删除(多入边即异或汇合,没有「等回齐」;AND 待并行组);
  selectEdges 精简;closure 删 checkGateway;process-service 删汇合等待与网关直传,
  节点一律只收装配后载荷(verdict/outcome 判据由系统注入,不走映射)

编排面(画布 = 名称 + 顺序,其余在属性卡):
- 元素盘五件:活动点(单一,不再分标准/案例 —— 那是实现方式)/审批点/起止/卡点
- 属性卡:节点类型二分(活动点/审批点);新增「链接活动实现」选择器(装载面,
  FlowNode.act 经 merge 成为语义 activityId);范式/版本降为只读装载信息
- 边属性可填:条件(S-FEEL)+ 映射(上游输出口/起单载荷/常量三种来源;
  workflow-var/expression 归系统隐性,不进设计者面)—— FlowEdge.condition/mapping
  工作副本,pkg-to-graph 回填、graph-to-pkg 合并,清空条件即变默认兜底边
- 异常看板 → 激活双判据看板:图编排(结构+属性+契约)与活动装载(unknown-activity)
  分面亮灯,两面清零激活才亮
- pkg-to-graph 去 paradigms 绿化与网关分支;图例/文案全线「活动点/审批点」

测试:closure 27(joinReady 两组 describe 随机制摘除)、advance-order 按真图形状重写
(去 as never 捏造的网关节点)、graph-to-pkg 9(新增 act 合并 + 边契约合并/清空用例)、
seed 8、端到端八例真库全绿;五闸退出码验过(api:check 提交后转绿)。

库:purge 后重种,唯一 unified-quote@v1 生效(10 节点 14 边,双面判据齐)。
债:M5-5 清账(兼容路径连 schema 一起摘);M5-4(并行组)仍挂。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 18:29:32 +08:00
ChaiHaoandClaude Opus 5 ba5f65e685 画图不检查,激活才查完整 —— 保存 bug 的根治 + 异常看板
你存不了图的根因:保存跑闭合校验,而画图必然经过不完整的中间态
(节点没接完边、活动没安装)—— 画到一半就 412 卡死。实测同一份图:
改前 412 拒存;改后存为未生效的新版,报告随行。

## 模型改成三段

  画图 = 只创建节点和边,图上真正生效可编辑的只有**名字**(双击不再出岗位一栏;
        边上的字是辅助阅读的注记;岗位/判据/映射这些**闭环要素在节点属性里决定**;
        分配到人是运营的事,设计侧不碰)
  保存 = 不检查、不生效:恒存为新版本号,谁也伤不着 —— 因此不设确认卡,直接存;
        任何版本都可编辑(在旧版上改 = 以旧版为底另起一版)
  激活 = 完整性闸:不闭合 412 拦下并点名缺什么;过了即独占生效

## 异常看板(左,与版本卡同列同风)

画图不拦,看板替你盯着:逐条列这一版缺什么,**点一条定位到那个节点的属性**;
零异常时给空占位说「可以激活」。版本行的「激活」按钮按闭合灰显 ——
不闭合点不了,亮了才能上线。实测:v4 两条异常全列出、激活灰;闭合的 v2/v1 亮。

## 顺手修一个画布盲区

packageToGraph 以手绘存证为呈现真相,**语义里有、存证里没有的节点画布看不见**:
异常看板指着一个图上不存在的节点,点了也定位不到(实测:API 直存的包新增节点后
picker 里根本没有它)。补画:缺席节点按 60px 一档排在图纸下缘 —— 位置丑没关系,
看得见才能修。实测点异常 → 选中 A8 → 五张属性小卡弹出。

## 布局照你定的

row2 = 异常看板(1/3,与版本卡对齐)+ 节点属性卡(2/3,与流程图同宽);
属性卡内部小卡**等高 250px**,输入/输出超高的内容自己滚(实测五张全 250)。

种子改为保存后显式激活;NotClosedError 文案「不得保存」→「不得激活」。

typecheck ✓ lint ✓ depcruise ✓ 498 测试 ✓

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 15:52:45 +08:00
ChaiHaoandClaude Opus 5 710946e729 流程包台面重做:版本收进包内部,启停是运行时开关
按你定的六条做:

## ① 台面三页签:总览 · 流程包 · 触发器
「调起规则」改名触发器(键不动,深链不断)。

## ② 版本收在流程内部,不平铺台面
货架一流程一卡:名字 + 标识 + 「生效 vN / 已下线」+ 版本数。
从前一版本一张卡,三个版本占三格 —— 版本是流程的内部事,不该摊在台面上。

## ③ 详情页布局照你说的排
左 1/3 版本列表(恰一个生效,点谁看谁的图),右 2/3 流程图;
图下是节点属性(点画布或点节点条,就地填);再往下才是声明式契约细节
(边映射校验 · 调用接口 · 触发器)。属性区从右侧窄栏挪到图下整行。

## ④ 新建流程
「新建流程」→ 填名称与标识 → 建出一张「开始 → 结束」的最小图,v1 即刻生效,直接进详情。
最小图天然过闭合校验 —— 不闭合存不进来这条闸对新建同样成立。
标识收紧为小写字母开头、只许小写/数字/连字符(它进 URL 和触发器规则)。

## ⑤ 历史包袱文案清掉
「工作流不设负责人,责任只落在节点上:此处不声明岗位,起单时就解析不出任务负责人」
→「这个节点还没指定岗位 —— 不指定,起单时就不知道任务该派给谁」。
同类:「未声明 · 发布前须补齐」→「未指定 —— 保存前选一个岗位」;
「发布前须先安装并过跨包契约相容核对」→「先去活动库安装;保存时会核对契约相容」;
边映射页的「边不闭合不得发布」→「不得保存」。

## ⑥ 版本可激活/停用,一次只能激活一个,切换不动在途
运行时模型从「最新版生效」改为**显式生效指针**:
  · `setPackageState` 激活即独占(同流其余版本一并停用)
  · 停用生效版 = 整条流下线,新起单被拒(报「无生效版本 —— 去流程包里激活一个」)
  · 起单取生效版;在途工作流按启动时钉住的版本号读定义,启停对它们零影响
  · 保存新版仍即刻生效(旧生效版自动停用)—— 回退就是把生效切回旧版,不用改图
store 的 setPackageState 把列上与 definition(jsonb)里的两份 state 一起改(教训同 0012);
迁移 0013 把存量多版同 active 归一为「每条流只留最高版生效」。

实测:激活 v1 → v3 自动停用,新起单钉 v1;停用 v1(零生效)→ 起单 500 且明说原因;
在途 v1 工作流照跑;恢复激活 v3。UI:切版看图、只读提示、启停确认卡、新建一条龙全通。

typecheck ✓ lint ✓ depcruise 376 模块 ✓ 498 测试 ✓

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 14:35:54 +08:00
ChaiHaoandClaude Opus 5 b2f5f69d6a 流程包去掉发布机制 —— 严格检查 + 保存即生效,生效由运行时决定
用户要的是运行时机制:新建 + 编辑,存下去就生效,旧任务走完旧版本,
新任务调用时才加载新编排。所以「草稿 → 发布」两态整套去掉。

## 闸从「发布」挪到「保存」

`savePackage` 现在先跑闭合校验,不闭合抛 `NotClosedError`(路由 412 带报告)——
**一个字节都不落库**。存进来的每一版都能起单,所以不需要「已发布」这个标记。
`publish` 用例、路由、`publishPackage` 存储方法一并删掉。

实测:把进 A6 的边映射清空后保存 → 412,逐条点名「边 e-g3-a6 未完成载荷映射;
节点「报价议价」的必填字段未映射:rows」。

## 保存恒新增版本号,服务端说了算

这不是可选项,是「旧任务走完旧版本」的前提:工作流按 (包id, 版本) 去读定义,
就地覆盖 v1 就等于改掉所有在途 v1 任务正在读的编排 —— 它们会中途换了规则却毫不知情。

版本号由用例层算(当前最大 + 1),**不采信入参**。实测:入参故意填 version: 999,
落的是 v2。store 层因此改成**只插不改**:撞键宁可让唯一约束报错,也不静默覆盖。

起单取版从「最新已发布」改为「最新一版」(retired 除外);触发器的 requiredInputs 同口径。
实测:改完描述存成 v2 后,新起的单用 v2,早先那单仍挂 v1。

## 迁移:旧状态值不归一就是一读就炸

状态枚举从 draft/published/retired 收成 active/retired。列上和 definition 这个 jsonb 里
各存着一份 state,两处都要改 —— 不改,`processPackageSchema.parse` 读一行就抛。
这不是开发期小麻烦,任何已部署实例都会中招。加了迁移 0012。

## 界面:恒为 0 的假指标撤掉,不换个说法留着

- 编排面板:「已发布/草稿」芯片 → 「生效中/历史版本」(真正的区别是哪一版会被新起单取到);
  发布按钮撤掉,保存按钮改成「保存并生效(存为 vN)」并在确认卡里说清影响面
- 历史版本只读,但就地指路:「去 v(N-1) 改(生效中)」,不让人自己找
- 蓝图面:「草稿 N · 未闭合 M」两个计数在新机制下**结构上恒为 0**(不闭合存不进来),
  换成「共 N 个版本,保存即生效」
- 驾驶舱:「未闭合草稿」卡整张撤掉 —— 同理恒为 0。天然的第三张是「死路」
  (route-dead-end 事件),但那需要一条按事件类型聚合的路由,没有之前不摆(同死信卡的规矩)

## 顺手修:种子的幂等判断本来是坏的

比对用 `JSON.stringify` 认键序,而 jsonb 从 PG 读回来键是重排过的 —— 永远判不等,
每播一次种就多存一版一模一样的定义(实测一口气堆到 v3)。改成递归按键名排序后比对。
连播三次,只第一次存。

## 契约变更(破坏性,一步到位)

`publish` 删除、`NotPublishableError` → `NotClosedError`、`publishable` → `closed`、
状态枚举收窄。按规矩破坏性变更该走三步弃用 —— 这里一步改完,因为:
① depcruise 反查下游全在仓内(374 模块无违规),同批改完;
② 要弃用的是一个**产品决定不再存在**的机制,留弃用窗口没有意义。
CONTRACT.md 已同步,快照单独说明。

typecheck ✓ lint ✓ depcruise 374 模块 ✓ 495 测试 ✓

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 12:49:24 +08:00
ChaiHaoandClaude Opus 5 78622b8799 审批点后面补上网关 G3,并让引擎**真的走它** —— 顺带给去向旗找到落脚处
两件事,都是「图上画着、实际走不到」这一类。

## ① 审批点后面没有网关(用户提出)

A5 只有一条出边 A5→A6,驳回走向藏在 `reject` 声明里,图上只是一枚牌子。
BPMN 的常规是审批的两个去向都由网关决定 —— 补 G3「审批结果」,
`verdict = "pass"` → A6,`verdict = "reject"` → A4(回边)。

补的时候撞上真问题:`onApprovalVerdict` 只在 pass 时走 `advanceFrom`,
而且 payload 里**不含 verdict** —— 于是 G3 没有判据可读。异或分叉的每条出边都得带条件、
没有无条件兜底边,判据一缺就全不成立,流会无声停在 G3。

能让闸过的最省事办法是在 `e-a5-g3` 上喂一枚常量 `verdict: "pass"`。**没那么做** ——
那样 G3 的条件恒真、驳回边永不可达,网关就是个摆设,又是一处「画着两个去向、
其中一个结构上走不到」的谎。

改的是引擎:裁决进载荷,通过与驳回**都先看图**,只有没画驳回边的包才退回 `reject` 声明
(它 schema 必填,且是通用机制)。`advanceFrom` 因此回报「有没有真的推出去」,
并加 `quiet` 参数 —— 「先试边、不成再退回声明」这一步试空是正常分支,不是死路,
不该报 route-dead-end 假警。

`reject` 保留:schema 必填、CONTRACT.md 的不变量、且审批人界面拿它渲染人读文案。
它与 `e-g3-a4` 必须字字对齐,加了测试守。

## ② 几何重排:两带换行,不回形

原坐标逐点抄自 MVP 的 SVG,是回形走线(row 0 左→右、row 1 右→左、row 2 折回最左)——
这正是上一轮流程链读成 A1→A7→A2 的根源。重排为两带:
受理·选型·寻源(y 60..100)→ 换行车道 y=190 → 定价·审批·议价·成交(y 310..350)→
未成交闭环(y 410..450)。每带内 x 随推进次序单调递增。

实测渲染(画布 639×325):带 0 的 A1 57→G1 159→A2 223→G2 325→A3 389→G2J 491,
带 1 的 A4 38→A5 153→G3 242→A6 293→G4 395,**零框重叠**。
两枚回 A4 的卡点同一条车道、同朝向(向上=回退)。

## ③ 顺带:两枚去向旗终于有地方落

上一轮我在债务簿写「A1 过不了 G1 是诚实状态、不是缺陷」——**判断错了**。
出口的标量字段只从落地事实取,而 `needAlternative` / `needSourcing`
在任何台账对象上都没有列,活动实现了也一样取不到。

新增 `project.intake_verdict`「受理结论」**一单一行**(主键只有询价单号)。
为什么不挂在按料号分行的「统一受理记录」上:`flat` 是把这一单所有落地行 reduce 展开,
六个料号就有六行同名列、最后合并的那行赢 —— 而归并规则是「任一条命中 C 即需选型」,
last-write-wins 会静默给出错的去向。测试正反两面都守:结论表主键只能是 rfq_ref,
且**其它对象上不许出现去向旗**。

A2 也要回写这一行,所以 `rfqRef` 进了 G1→A2 的边映射,并**正式声明进 A2 的入参**——
不声明也能偷渡(闭合校验遇到未声明的目标直接跳过),但那是默许不是正当。
A2 的 contract.version 因此升 0.2.0,包版升 0.5.0。

顺手删了一个陈旧草稿 v2:它是我早前测编辑器留下的旧图(14 节点、无 G3),
而种子刚把 v1 用新图重发布 —— 草稿内容比已发布版更旧,谁发布它 G3 就没了。
删前确认零工作流引用。系统没有「丢弃草稿」的入口,这条记进债务簿。

typecheck ✓ lint ✓ depcruise 374 模块 ✓ api:check ✓ 495 测试 ✓
发布闸实测:15 节点 / 18 边,边映射闭合校验通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 12:24:40 +08:00
ChaiHaoandClaude Opus 5 22aa44c775 手工走通整条流,揪出五处编排层问题 —— 最要命的是假成功
把 `unified-quote` 从起单一路手工答到底(这是逐个开发活动之前该做的验证),
一路撞出五处,按撞到的顺序:

① 七个活动派出七张一模一样的「请处理」
   槽位骨架给的是 `card: {}`,工单标题/字段全落回默认。收卡的人不知道自己走到
   哪一步、这一步要干什么,整条流没法手工走通。→ 卡面自述:标题就是步骤标签,
   依据带「第几步 / 共几步」,并如实说「这一步的自动化还没开发,先由你来办」。

② 走到第 3 步就 `exhausted` 终态、工作流转 abnormal
   六个槽位共用 `humanCalls: 3`,而 A1 声明了 6 个人类步 —— 活动**结构上永远走不完**。
   → `budgetFor(n)` 按各槽位自己的步数给。步/项清单提成 `STEPS` 常量,
   预算与骨架共用同一个数组,数不上不会漂移。治理台申报值也同源(否则台上摆假数)。

③ 六步答完,任务卡在「等人,却没有任何东西等人答」
   `flow-runner` 写死了 `released = lastStepIsAcceptance && lastVerdictPassed` ——
   末步不是验收工单,活动就永远不 release。→ 声明的六步来自 MVP 作业指导书,
   不篡改语义,在其后**追加**一步「收口验收」。

④ 只走了 A1(一个节点实例)却报 `completed` ← **最危险的一处**
   状态聚合把「没有在跑的了」当成完成。界面上是一单已完成的报价,业务上一步没走。
   → 「完成」凭**到过终点节点**;收敛而未到终点 = 卡住 = `abnormal`,进驾驶舱异常卡。
   `aggregateWorkflowState` 收第三个入参(终点 id 集),不传退回旧口径。

⑤ 异或网关一条边都选不中时,流无声停住
   ④ 让它至少报异常了,但只说「有问题」不说「哪儿」。→ 发 `route-dead-end` 事件:
   哪个节点、各出边的条件、判据里当时有哪些键。只记键名不记值 —— 诊断够用,
   载荷不进事件明文。实测这条痕直接指出「G1 要 needAlternative,而键里没有」。

顺带:改槽位内容必须升包版(0.1.0 → 0.4.0)。「安装恒为新增、版本并存」是硬不变量,
同版重装是 no-op、manifest 一字不动 —— 我第一次改完重播种,屏幕报「已装入」而库里还是旧的。
`installSlots` 现在先问库、如实报「已装」还是「已在库,未改动 —— 要换内容请升包版」。

**走查后的诚实状态**:A1 走得完、活动 released;流**过不了 G1**,因为出口的必填去向旗
产不出来。这不是缺陷 —— 出口载荷由台账落地事实推导,槽位没实现台账写入就没有事实,
凭空捏一个就是在系统内部 mock。于是 A1 的开发完成判据也具体了:必须让
`needAlternative` / `needSourcing` 成为落地事实,否则 G1 永远判不了。债务簿已记。

另记一条闸的盲点:api:check 只快照 index/contract/port.d.ts,而 index 是 re-export 名单 ——
给纯函数加形参它看不见(本次实测)。改签名要手动同步 CONTRACT.md,别指望闸提醒。

typecheck ✓ lint ✓ depcruise 374 模块 ✓ api:check ✓ 490 测试 ✓(均以退出码验)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-28 11:11:32 +08:00
ChaiHaoandClaude Fable 5 ac85ecf2ea 基线:鉴权会话+工作组+工作台三区修复+编排工作区+产品设计文档
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 20:43:35 +08:00
ChaiHaoandClaude Opus 5 1577927214 判据 ⑨ 解锁:选择组 → 出口 → 网关判据,三处断点接通
A6 放行选了 lost,却始终走不到 A7。三处各断一截:

① 装配输出口只看**必交付口**。A6 根本没有必交付口(它的输出是选择组
   won/revise/lost),于是得到一张空表,下游连判据都读不到。改为必交付口与
   选择组的口一并装配。
② `onTaskTerminal` 只取**第一口**的载荷做路由作用域;没有必交付口时就什么都拿不到。
   改为各口载荷合并成一份。
③ 网关拿到的是**边映射装配后**的载荷,而 A6→G4 这条边没有映射,装配出来是空的 ——
   判据依据在这一步整个丢掉,异或网关一条分支都选不中,流悄无声息地停住。
   改为:网关按**到达的东西**路由(上游载荷原样穿过);活动与审批点仍只收装配后的载荷,
   它们有契约,不该被上游多余字段污染。

另立一条约定并写进注释:**选择组发射的那一口就是这次运行的 outcome**
(「恰发一口,随放行一并确认」),下游异或网关的判据读的就是它。

现在:A6 放行选 lost → G4 按 `outcome = "lost"` 判定 → **A7 情报归档起到冯兵工位**。

gates: tsc / eslint / depcruise / 399 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 05:48:04 +08:00
ChaiHaoandClaude Opus 5 9201f6b094 判据 ⑥ 解锁:容忍分支推得动汇合,汇合不再等一个永不到来的网关
三处修:

① `joinReady` 把**网关直连汇合的直通道**也算作一票要等的分支。
   网关不是责任单元、永远不会有实例,于是汇合永远等不齐,整条流卡死。
   改为:来源是网关的入边不参与等待 —— 分叉一发生它就算过了。
   (统一报价图里这条直通道就是 TYPE-A 快路。)加了回归守卫测试。

② 容忍(tolerate)只把实例标成「已消化」就收手,不往下推 ——
   `joinReady` 认 tolerated,可没人再去问一次汇合够不够齐。改为标完即续推。

③ 蒸馏钩子失败会把已落定的终态掀翻(治理台终止返回 500,任务卡在中间态)。
   蒸馏是进化管线的入口、不是终态的一部分:失败就失败,终态照落。
   失败也不再往任务记忆里追加 —— 此刻记忆已封卷,那个守卫拒得对。

结果:A1 released → G2 分叉 → A3 / A2 两个 loop 起在正确工位 →
按声明容忍收束 → **汇合回齐 → A4 成本定价起到冯兵工位**。

gates: tsc / eslint / depcruise / 397 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 05:03:58 +08:00
ChaiHaoandClaude Opus 5 9de33a5416 判据 ④⑤ 解锁:G2 并行网关起出 A3 / A2 —— 输出口发射的是落地事实
G2 之后一直不分流,根因三条,逐条修:

① 输出口载荷是空的。`outputPortsOf` 原先从**作用域**里捞字段拼载荷,
   而放行那一刻草稿层已合并并清空,捞到的只剩起单入参 —— 下游节点必然缺字段。
   改为:载荷取自**落地收敛的事实**,由 `releaseWrite` 回报合并了哪些行,
   经 release → task → app 一路带上来。这也正是「落地收敛,输出口才发射」的字面意思:
   发射的就该是已经落地的那些事实。
   `LandedFacts` 给两种读法:铺平(标量取值)与逐行(数组取值),
   输出口声明里 `type: 'array'` 的属性填逐行,标量属性填铺平值(驼峰/下划线两种写法都认)。

② 一条分支起不来,把整条终态上报连坐掀翻 —— 于是**上游任务的终态也报不上去**,
   流停在原地,一行日志都没有。改为:该分支记 `node-start-failed` 事件 + 落一个
   blocked 实例,其余分支照走,在治理台「需处置」里现形。
   (这条是先修的:正因为它,前面两条才一直查不出来。)

③ 边映射引用了不存在的路径:`G2→A3` 的 pn 取 `intake.records`(那是想象中的 LLM 出参形状)、
   `G2J→A4` 的 records 取 `initiation.records`(起单时根本还没有这些行)。
   按输出口的真实声明改正。

结果:A1 released → G2 并行分叉 → **A3 渠道寻源落到林佩萱、A2 替代选型落到王梓名**,
两个 loop 活动同时在跑,岗位解析正确。

gates: tsc / eslint / depcruise(317 模块)/ api-report / 393 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 04:52:53 +08:00
ChaiHaoandClaude Opus 5 3b9587563a M6(4/n): 工作台五要素齐活 —— 审批 / 消息 / 纪要 / 汇报
四个 desk-* feature 自 workbench 原型 ModuleWorkspace.tsx 平移(jc-* 类名原样),
共用件落 shared/desk/module-kit;mock 数据换成服务端投影:
  · desk-approval:审批点卡到审批人工位;依据快照、驳回走向(声明式)、驳回必附意见
  · desk-message:通知 + 跨任务征答**就地作答**;卡面表单由 card.ts schema 驱动,
    与后端回传校验同 schema;跨人卡不占任务清单,不开放任务对话流
  · desk-minute:纪要维护 + 决议登记起单;正文可续、决议只增
  · desk-report:四周期草案 + 度量八指标 + 确认即提交
左轨角标常驻(待批 / 未读),责任面要一眼看见什么在等我。

修一个真 bug:process.start 从不校验流程包的 callInterface,
先插工作流再由 task.start 撞活动契约 —— 失败就留下一条永远推不动的在途单。
改为**先校验后建单**(CallInterfaceViolationError),一条工作流都不留;
路由回 400 并带上缺哪个字段。已清掉这次触发出的两条孤儿单。

判据 ⑩ 纪要一环跑通:决议 → 登记触发 → 工作流 → A1 落到 BD 冯兵,
登记人即来源已记在单上;同一决议重复登记被 412 挡下。

gates: tsc / eslint / depcruise(316 模块)/ api-report / 380 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 03:53:36 +08:00
JCAI Main AgentandClaude Opus 5 a8f6eef610 M5(2/3): process 用例与流转 —— 流程引擎的确定性调度
- routing domain:选边(XOR 恰取一条 + 默认分支兜底;AND 全走)、边映射装配(四类来源 +
  S-FEEL 转换)、AND 汇合回齐(容忍分支不阻汇合)、审批点驳回走向声明式
- process-service:起单 → 调度节点 → 负责人四策略解析(结果与依据留痕)→
  活动起单 / 审批点卡工位 / 终点收束按来源回执;任务终态到达继续流转或走失败走向四选一;
  审批驳回按声明回指定节点重开新实例;状态每次变动后重算聚合投影
- adapter:流程包(id × version)、工作流实例、节点实例三表持久 —— 崩溃后可续跑

ADR 待补项登记为 debt M5-1:§6.1 的 Mastra workflow 编译器作为可替换 adapter 后补,
当前由自持确定性状态机承担,语义等价、usecase 不动

325 个测试通过,门禁五道全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 02:01:08 +08:00