forked from ChaiHao/BPOS-MVP
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
+570
@@ -5,6 +5,8 @@
|
||||
|
||||
| # | 债 | 原因 | 预期清理 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| M6-1 | 转人代办的答案**不经出参 Schema 校验** —— 归型失败原样交回,由活动自己面对 | 人已经把事做完了,校验不过就得把它判成「调用失败」;而人做完的事不会因为格式不合就没做过。宁可让活动拿到一个明摆着不合型的值,也不谎报「这一步没成」。是否补一道「不合型即驳回重答」,等有真活动踩到再定 | 有活动踩到 | 进行中 |
|
||||
| M6-2 | 进程重启后恢复的任务,动作级 trace span 整段丢失(`beginAction` 依赖内存里的节点父 span,重启即空)| 观测尽力而为不掀翻业务的设计取舍;恢复路径要补父 span 得把节点 span 持久化或懒重建 | 观测面强化时 | 进行中 |
|
||||
| M0-1 | `pnpm db:seed` 只打印种子计划,未真正写库 | 种子数据(五岗位 / 五员工)已在 `apps/api/src/app/seed.ts` 冻结为常量并定义 `OrgSeeder` 端口;写入实现依赖 `org` 模块的 Drizzle schema,属 M1 | M1 | 已清理(M1) |
|
||||
| M0-2 | `packages/{sfeel,activity-sdk,ui-kit}` 仅有版本常量 | M0 只立骨架,内容按里程碑序填(§15);`@contracts` 已于 M1 落首版 | M3–M5 | 进行中 |
|
||||
| M1-1 | `container.ts` 里两个占位端口:`InFlightWorkProbe`(空集)、`TriggerMatcher`(恒不命中) | 二者的真身在 task(L6,M4)与 trigger(L7,M5);`MessageFallback` 已于 M2 接真身 | M4 / M5 | 进行中 |
|
||||
@@ -217,3 +219,571 @@ intel-archive)只有契约面,入口产物是抛「未实现」的占位 ——
|
||||
指导书」;现状是运行时抛 `ToolNotFoundError`、安装时 `tool-contract-missing` 直接拒装。
|
||||
|
||||
实现活动与工具调用逻辑时,照规范第 12 章的顺序销账。
|
||||
|
||||
|
||||
## ~~A2 执行单的可编辑表渲染不出输入框~~(2026-07-31 已销)
|
||||
|
||||
**结论:不是渲染 bug,是两件事叠在一起。**
|
||||
① 我自己的排障失误 —— 用 `.closest('button,a,[role=button],li,…')` 找任务卡,匹配到了错误的祖先,
|
||||
于是屏上一直是**上一个任务**的工单(把 `editing`/`order.id` 打到 DOM 上一读就现形:
|
||||
`oid` 与 A2 的工单号对不上)。正确的是 `.closest('button.task-card')`。
|
||||
**教训:排障先把状态摆到 DOM 上读,别靠一层层猜。**
|
||||
② 真缺陷 —— A2/A3 派的执行单**只给列名不给行**(cols 4/6,rows 0)。
|
||||
可编辑表没有行就没有格子:人打开看到一排表头加一片空白,一个字都填不了。
|
||||
已在两个活动的说明书与剧本里写死:**两张表都必须带行**,没有的值给空字符串
|
||||
(空格子界面自己标红「待填写」)。A2@1.4.0 / A3@1.1.0。
|
||||
|
||||
<details><summary>原记录</summary>
|
||||
|
||||
**现象**:MVP 工作流第二轮,A2 的执行工单载荷完全正确 —— `mode: execution`、两张 `t:"table"`
|
||||
且 `editable: true`(已用 `/api/desk/tasks/:id` 核过接口出参),页签停在「待处理」、
|
||||
`disabled` 为假(提交钮在)、两张表都渲染出来了,**但一个 `<input>` 都没有** ——
|
||||
即 `WorkOrderReport` 里的 `editing` 为假。同一条代码路径在 A1 的执行单上是好的。
|
||||
|
||||
**已排除**:①载荷缺字段(接口出参已核)②`blocks` 没传到(ViewRegion:382 传的就是 `cur.blocks`)
|
||||
③`disabled` 为真(提交钮在)④`useState` 初值只算一次 —— 已改成渲染期按 `order.id` 重算,**没解决**。
|
||||
|
||||
**下一步**:在 `work-order-report.tsx` 里把 `editing` / `order.id` / `order.blocks.length`
|
||||
打到 DOM 上直接读一次,先确认它到底是不是假、以及 `order` 是不是 `cur`。
|
||||
这条挡着 MVP 工作流跨过 A2,是三轮端到端的**第一个硬阻塞**。
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## ⑨ A2 派的是一张没有表的泛化卡(2026-07-31,未销)
|
||||
|
||||
**现象**:MVP 第三轮,A2 起单后派出的执行工单标题是「需要你判断下一步」、**blocks 为 0** ——
|
||||
而剧本 `ALTERNATIVE_STEPS.T_select` 明确要求卡面放两张可编辑表(初筛清单 / 替代对标表)。
|
||||
上一轮(A2@1.1.0)它是发了两张表的(只是 rows 为 0,即 ⑧);这一轮连表都没有。
|
||||
|
||||
**「需要你判断下一步」是 `fillCard` 的兜底文案** —— 说明模型给的 `human` 行动卡面几乎是空的,
|
||||
基座用当下局面把它补全了。也就是说 Agent **没照剧本组卡**。
|
||||
|
||||
**下一步**:
|
||||
· 先看这一拍的模型出参(trace 里的 ModelCall),确认它到底给了什么;
|
||||
· 若卡面确实空,问题在提示词 —— A2 的 guide 写得比 A1 长且全是「怎么组 blocks」的细节,
|
||||
可能被单拍上下文里的其它段落淹掉;考虑把 blocks 的形状挪进 ACTION_ITEM_SCHEMA 的
|
||||
description(schema 比说明书硬),或在 guide 开头就把 JSON 骨架给全。
|
||||
· ⑧ 的修复(必须带行)因此**尚未验证** —— 它要等 A2 真的发出带表的卡才测得到。
|
||||
|
||||
## A1 视图与判据重做(2026-07-31 第三轮)—— 已销与余账
|
||||
|
||||
**已销**(六轮端到端逐轮逼出来的,每条都有实测账):
|
||||
|
||||
- 报告模版照 PRD `活动执行页/` 两份目标件重做:`INTAKE_OVERVIEW` = 来件原文 → 受理要素
|
||||
→ 指标带 → 有效 PN 清单(必填/选填胶囊 + 缺必填整行标底)→ 本轮不报;
|
||||
`INTAKE_RELEASE` = 要素 → 逐条记录(带状况标)→ 分级依据 → 分流去向 → 两类请求
|
||||
→ 结论 → **这一签下去会发生什么**。`@sdk` 新增 `letter` / `factCards` / `actionList`
|
||||
与列样式(`req`/`opt`/`num`/`pill`),`data-blank` 让空值真的留空(判断在样式里,不在填数里)。
|
||||
- **`@sdk` 五个零调用方的拼装件已删**:`kvCard` / `tableCard` / `noteCard` /
|
||||
`stagesCard` / `critList`(六个活动一个都没用过)。
|
||||
- **说明书不再重写一遍逐步做法**:它与剧本 `guide` 曾各写一份六步,分叉过 ——
|
||||
说明书里点名的 `S1_register` / `S2_ask_back` / `S3_normalize` 三个计划项案例模型里
|
||||
根本不存在,模型照它报 itemId 一报一个被拒。现由测试钉死:说明书不许点名计划项。
|
||||
- **机检判据不再两边自报**:新增基座读数 `域_对象_rows`(本单在那张表写下几行),
|
||||
`lines-present` / `route-alt` / `route-rfq` 各有一边读它;`quote-intake.classify`
|
||||
的出参带 `counts`,条数照抄不靠模型数。实测拦下的是:条数报 9 而表里躺着 10 行、
|
||||
三类条数报 7A/2B 而判定器给的是 5A/3B —— 派单数与条数同源同错,`2 = 2` 顺利过关。
|
||||
- **「问过 ≠ 答过」**:S2 的收步判据从 `asked_total`(卡发出去就为真)改成
|
||||
`answered_total`(答案已写回台账)。此前人填完提交,引擎下一拍开头就把这一步收了,
|
||||
模型没轮到把答案写回来 —— 那一格白填。
|
||||
- **归档件不再被冲掉**:收口后引擎还会跑一拍,那一拍推固定模版名 `case-overview`、
|
||||
退到「声明的第一张」,把人刚签过的放行页冲成阶段二的执行件。现按剧本 `view` 推、
|
||||
活步收完不推。
|
||||
|
||||
**余账**:
|
||||
|
||||
| # | 事项 | 现状 | 目标 |
|
||||
|---|---|---|---|
|
||||
| A1-1 | 制表符 BOM 的拆列靠模型数格子 | 已在说明书里写死「每行格子数 = 表头列数」,第六轮九行全对;但这是提示词约束,换个 BOM 仍可能错位 | 基座把附件读成**结构化行**(表头 → 对象)一并交给活动,模型只做列的语义映射,不数制表符 |
|
||||
| A1-2 | `preauthorizedActions` 仍写在六个活动清单里 | 全仓零消费者(§15 ⑨),但契约里是必填字段,删它是破坏性契约变更 | 三步弃用:先转可选、再从清单摘、最后从契约删 |
|
||||
|
||||
## Harness 可靠性:三处「门本身教模型撒谎」(2026-08-01,已销)
|
||||
|
||||
端到端连跑两条样例路径逼出来的,每一条都不是模型跑偏,是**门的形状不对**:
|
||||
|
||||
- **① 判据只接受「是」,模型就去改那个数。** `optional-asked` 原是 L2:
|
||||
`missing_minor_total = 0 or asked_total > 0` —— 两个数都由活动自己写。
|
||||
实测:一颗料只缺目标价,模型如实记 1,提交被连拒四次,第五拍把它**改回 0**,
|
||||
判据当场变绿放行,台账从此写着「缺选填 0 条」。
|
||||
**机检判据只许读基座数得出来的数**;读活动自报的数,它不是门,
|
||||
是一句「改哪个数能过」的提示。这一条已降为 L4 交人,并把缺口数摆上放行页指标带。
|
||||
- **② 裁量判据也被要求「为真」,模型就去改裁断。** 换成 L4 之后,它如实裁了
|
||||
「未满足」并写了理由,提交照样被拒 —— 判、提、拒、再判……十二轮 21 拍 187k token,
|
||||
最后一拍把裁断翻成「满足」才过去。**裁量的本义就是可以裁出「不满足」**,
|
||||
而不满足恰恰最该交给人。提交门已改:机检(L1/L2)必须为真,裁量(L3/L4)**裁过即可**,
|
||||
裁断连同理由随卡交给签字的人。
|
||||
- **③ 熔断数不出「同一堵墙」。** 一拍一串行动,同一个理由拒回来时标题里带着串位
|
||||
(「第 2 个」「第 5 个」),整条标题一比就成了 N 堵不同的墙;而叙事窗只有 20 条,
|
||||
一次撞墙连带五六条留痕,至多装得下三四次。两处叠加的结果:一单被同一条判据拒回
|
||||
**33 次**、40 万 token 烧到 `exhausted` 才停,期间界面上只是「进行中」一动不动。
|
||||
已修:比对时削掉串位前缀;熔断另开 60 条的窗(只在引擎里数,不进提示词)。
|
||||
|
||||
顺带:计划项号认唯一前缀简写(`S4` → `S4_history`),歧义为零才认 ——
|
||||
实测三轮里两次因为模型只写前半截白烧一拍。
|
||||
|
||||
**验收**:两条样例路径各三轮,全部 `released`;Itelma 10 拍 / 88-90k / 行动被拒 0,
|
||||
样例1 9-11 拍 / 71-88k / 行动被拒 0-1;数据逐轮对得上(9 行 5A/3B/1C · 1 alt + 3 rfq)。
|
||||
|
||||
## 试跑日志吞主键(2026-08-01,已销)
|
||||
|
||||
存档那一屏只渲染 `patch`,`key` 整个不显示 —— 三行受理记录屏上只剩
|
||||
「eau / lead_time」,看的人据此判定「PN 没有提取到」,而 PN 三条一条不少全在 key 里。
|
||||
实时观测面(`activity-run.tsx`)一直是对的,只有存档面走岔。两处已合并到
|
||||
`features/gov-testbed/internal/drafts.tsx` 一份实现。**没显示不等于没有,
|
||||
但屏上看起来就是没有** —— 这也算「界面不撒谎」。
|
||||
|
||||
## 画面比数据早一拍 / 满屏时刻差八小时(2026-08-01,已销)
|
||||
|
||||
用户实测报「PN 没取到,还是 —;第二阶段过不去」。查下来两件都不是抽取的事:
|
||||
|
||||
- **帧推在行动之前**。`turn-runner` 每拍推画面那一下排在行动循环**之前**,
|
||||
于是工作区那份件永远比数据早一拍:S2 那一拍把九条料写进草稿,而那一帧是写之前推的,
|
||||
清单一行都没有;偏偏这一拍以「发卡问客户」收尾,整单挂在那儿等人 ——
|
||||
人打开工作台看到一张空表,合理地判断「PN 没抽出来」。
|
||||
已改成**推在行动之后**;提交那一下由 `raiseAcceptance` 自己摆放行页(`framed` 打招呼,回合末不再盖)。
|
||||
测里钉了顺序断言(推帧时已写下几行,推早了就是 0)。
|
||||
「第二阶段过不去」是同一件事的另一面:卡在等人答那张执行卡,答完即收步(已复跑验证,11 拍 released)。
|
||||
- **满屏时刻是 UTC**。后端存 UTC ISO,前端 17 处 `at.slice(5,16).replace('T',' ')` 直接摆上去 ——
|
||||
一封 08-01 04:18 到的信,工单写「派发 07-31 20:18」、气泡写「20:18」。
|
||||
差八小时的时刻比没有时刻更坏:它看起来像一个可信的事实,人拿它核 SLA、跟客户对时间,一核一个错。
|
||||
已抽 `@web-shared/at.ts`(atMinute / atClock / atFull)统一换掉;报告里的入站时刻走
|
||||
`case:openedAt`(基座填,同样本地化)。
|
||||
**顺手撞到的坑**:批量替换把 `FlowEditor` 里一处 `text.slice(0, 16)`(截标签长度,不是时刻)
|
||||
也改成了时刻格式化 —— 已回退。正则扫时刻要认得出「哪些 slice 不是时刻」。
|
||||
|
||||
## 拆表交给基座 · 同一件事不许问第二遍(2026-08-01,已销 A1-1)
|
||||
|
||||
- **附件里的表由基座拆好**(`agent-runner` 的 `sheetRows` / `attachmentPart`)。
|
||||
数制表符是能算的事,交给模型只会数错格子:实测同一行栽过三轮 ——
|
||||
BOM 上白纸黑字写着「Антенна · 浙江嘉康 · (空) EAU」,拆出来成了
|
||||
「集成电路 · (空) · 30000」:品类错、制造商丢、年用量凭空多一个。
|
||||
现在 `attachments[].rows` 一行一个对象、键就是表头;列数对不齐就不认这张表
|
||||
(半张读歪的表比没有更坏)。
|
||||
**两处坑连着踩**:①先把 rows 塞进那段按 1800 字截断的作用域里 —— 三轮下来照错不误,
|
||||
因为它压根没进提示词;②原文与拆好的行并排放,把这一段撑了一倍。
|
||||
现在附件**单独成段、不参与截断**,拆出来就把原文换下来。
|
||||
验:同一条 BOM 连跑三轮,九行数据**一模一样且全对**(此前每轮都不一样)。
|
||||
- **同一件事不许问第二遍**。熔断管的是「连续被拒」,管不了这种:每一拍的行动都合法,
|
||||
只是问的是同一件事。实测一单里同一个缺口连问三遍,四次调人的预算耗尽落 `exhausted`。
|
||||
现在发卡前比一次:**人已经答过的同一张卡**直接拒,并把出路写进理由 ——
|
||||
「他把格子留空交回来,就是对方给不了,那也是一个答案:如实记账、往下走」。
|
||||
认的是标题(标点与全半角归一后比),**不做模糊匹配**:拦错比多问一遍更坏。
|
||||
- **待处理那张可编辑表绑台账**(`blocks[].ledger` + `fields`):
|
||||
发卡那一刻基座从草稿层把**整份清单**铺进去,人看到的是完整九行、逐格可改 ——
|
||||
与 PRD 执行页一致。此前活动自己把行塞进回复里,两处死结:回复有长度上限,
|
||||
铺长了半途截断整拍作废;那些行台账里已经有了,再抄一遍是拿 token 换一次抄错的机会
|
||||
(抄的时候把 BOM 原样的 30 填了进去,而台账里已经是 30000)。
|
||||
|
||||
## 阶段2/阶段3 两张件对 PRD 逐块核到齐(2026-08-01)
|
||||
|
||||
按 `~/coding/JCAI_PRD/docs/活动执行页/` 两份目标件逐块比对(比对脚本按块名断言,不凭印象):
|
||||
**阶段2 待处理 22/22 · 阶段3 工作成果 17/17**。这一轮补齐的三块:
|
||||
|
||||
- **归属签**「通道入站 → 冯兵 · BD」:两样都是基座知道的事实(触发来源、任务负责人),
|
||||
由 `case:origin` / `case:owner` 填 —— 让活动自报等于让被指派的人自己说自己是谁。
|
||||
两头有一头填不出就整枚不摆(`:has(b:empty)`)。
|
||||
- **落款位置签**:写「这份件在流里是什么位置」(A1 受理归一 → 交下游),
|
||||
**不写状态**。在办与归档是同一张模版,写「已放行」进了档案才成立,在办时就是谎。
|
||||
- **成交迷你柱**:`market.price_history` 有 customer_code / quoted_at / unit_price 三列,
|
||||
但**这张表一行数据都没有**(种子不写、CRM 那件工具是桩)。所以不画六根编出来的柱子,
|
||||
留原型自己的**空轨**(虚线)并在旁写清为什么空 —— 空得有出处,才不是组件坏了。
|
||||
真要画,先得有人往那张表里写成交(见余账)。
|
||||
|
||||
仍**有意不做**的两处,理由写在 `views/report.ts` 的文件头:
|
||||
① G2 命中行高亮 —— 命中在编排层求值,这份件画个对勾就是替它宣布结果;
|
||||
② 报告里的编辑态与按钮 —— 改数据走执行工单(那张表已绑台账、整份九行逐格可改)。
|
||||
|
||||
**余账**:`customer` 维度的成交台账没有写入方 —— A4 定价要取历史锚点、A1 要画成交柱,
|
||||
两处都指着它。定了谁写(A6 议价成交回写?A7 归档?)再画那条柱子。
|
||||
|
||||
## 去向由分类表算出、验收页可就地改(2026-08-01,用户定的口径)
|
||||
|
||||
用户口径两句:**阶段3 要按验收目标件做,而且可编辑;最后走哪条边是分类表算出来的,
|
||||
不是人编辑或选出来的**。照此改了三处:
|
||||
|
||||
- **出口谓词进契约**:`output.selective[].ports[].when`(S-FEEL,按序求值首个命中即出)。
|
||||
A1 的三口写成 `type_c_total > 0` / `type_c_total = 0 and type_b_total > 0` /
|
||||
`type_c_total = 0 and type_b_total = 0` —— 与报告上那张 G2 规则表逐条对应。
|
||||
发放行卡时基座自己判,卡上只如实告诉人**走哪一口、凭哪一条**(`routedPort`),
|
||||
不摆选择题;算不出来的组(每口都得写 `when`)才退回「人选一口」。
|
||||
`revisableAtRelease` 里那两枚去向旗随之撤掉 —— **去向不是人该改的东西**。
|
||||
- **验收页可就地改**:必交付物声明 `editableAtRelease: [列名…]`,基座在放行卡上铺一张
|
||||
绑台账的表(整份行都在、逐格可改);人提交那一刻**基座写回草稿层**,AI 不经手。
|
||||
执行卡上那张表同理(`blocks[].ledger` + `fields`)。
|
||||
- **人手改的值与 AI 写的值过同一道归型门**。第一版直接调 `draftWrite` 绕过了
|
||||
`coerceToColumn`,于是一句「客户已确认」落进 `double precision` 列,
|
||||
到 `releaseWrite` 才被 PG 掀桌 —— **人签完字之后**,整单落 `blocked`(实测)。
|
||||
现在归得了就归、空值不写、归不了**跳过那一格**并留痕(别的格照落):
|
||||
人填错一格不该把他改对的另外八格一起丢掉。
|
||||
|
||||
**又踩了一次「链上最后一处丢值」**:`blocks` 契约声明了、submit 传了、release 转了,
|
||||
到 `container.ts` 那个显式对象里被抹平 —— 编译不报、运行时静默丢,卡面 blocks 恒空。
|
||||
`fields` 当年就是这么丢的,注释还在旁边。**装配层的显式对象是这条链的最后一关**,
|
||||
加字段时那一处必须一起改。
|
||||
|
||||
## 交代不复读:一字不差拦不住换个说法(2026-08-01)
|
||||
|
||||
用户指着对话区那一段问「不觉得啰嗦吗」——连着五条「阶段结论」说的是同一件事:
|
||||
「9颗料都打上标了:7颗A…」「9颗料都打标完了:5颗A…」「这单9颗料都打标完了…」
|
||||
字面各不相同,读到的却是同一句被复读了五遍。原来的去重只认**一字不差**。
|
||||
现改成看重合度:两句归一(去标点空白)后,短的那句八成以上的字出现在长的那句里就算同一句。
|
||||
阈值不敢再低 —— 近义但不同的交代重合度远低于此,**拦错一句比多说一句更坏**。
|
||||
|
||||
## 件是投影,不是快照 —— 改完数据要重渲(2026-08-01,一半修完一半列账)
|
||||
|
||||
用户:「我编辑了表格,点了保存,实际表单变了,但是保存后 A、B、C 的图标没变;
|
||||
我全修改成 A,按理说底下的流向应该是直通 A4 直接定价,也没有受影响。」
|
||||
|
||||
三层原因,前两层已修、第三层**未修**:
|
||||
|
||||
**① 保存压根没落库。** `onEdits` 只把值塞进前端缓冲,等提交那一下才带走。
|
||||
新增 `POST /api/desk/tasks/:taskId/table`(复用 `applyBoundTableEdits`),保存即写回草稿层,
|
||||
工单不办结、放行门的位置没动。
|
||||
|
||||
**② 落了库也不会重渲。** `putView` 只存渲染好的 HTML —— 那一帧就是快照。
|
||||
补迁移 0028 存下**模版名**,加 `views.repush(taskId)`:按当下数据、用同一张模版再渲一遍。
|
||||
验证:人把九条全改成 A,**类徽跟着全变 A** ✓。
|
||||
|
||||
**③ 汇总列没人重算 —— 这一条没修。**
|
||||
分布条与结论横幅那条去向读的是 `verdict` 的 `type_a_total / type_b_total / type_c_total`,
|
||||
那是**活动写的汇总数**;人改的是 `record.type_tag` 逐行。逐行变了,汇总列不会自己跟着变 ——
|
||||
于是重渲之后仍是「类徽全 A、分布条 5/3/1、去向 to-alternative」,**自相矛盾**。
|
||||
|
||||
要治得给台账一个「派生列」维度(声明 `type_a_total = count(record where type_tag='A')`,
|
||||
基座按声明现算),与 `required` → `域_对象_missing` 是同一个路子:
|
||||
**能由数据算出来的,别让活动自报**。那是新能力,不是小修,故列在此。
|
||||
在它落地之前,人在放行页改类标只改得动逐行那一层,汇总与去向仍以 AI 写的数为准。
|
||||
|
||||
顺带修了一处真缺口:`editableAtRelease` **原本不含 `type_tag`** ——
|
||||
写回按这份名单摆格子,不在名单里的列压根不进载荷,所以人改了九格、一格也没落库。
|
||||
三分类是 P-03 算的,但算得对不对该由人说了算:改一颗料的类就是推翻一次判定,那是他的职权。
|
||||
|
||||
## 判据的第三态,与「看」跟「改」分家(2026-08-01)
|
||||
|
||||
用户两问:「①『选填缺口经过人』这个未达标为啥还在?②报告里两个可编辑表格默认锁定,
|
||||
右上角安置一个按键,编辑按钮点击了才进入编辑态,取消或保存。」
|
||||
|
||||
### ① 那条红叉不是「活没干完」,是 AI 办不到、交你定 —— 而卡上说不出这个区别
|
||||
|
||||
`acceptanceCriterionCheckSchema` 只有 `{label, level, pass}` —— **裁断与理由传不到卡面**。
|
||||
于是「裁了但裁成不满足」与「压根没裁」长得一模一样:都是一个红叉「未达标」。
|
||||
人看到一条永远不绿的项,既不知道 AI 有没有办过、也不知道它为什么办不到,只能凭感觉签。
|
||||
|
||||
而提交门早就允许「裁成不满足照样能提交」(裁量的本义就是可以裁出不满足),
|
||||
更早的注释里也写着「裁成不满足的,连着理由一起进验收卡:那正是签字的人要看的东西」——
|
||||
**那句话描述的行为从来没有实现过**,因为契约里没有那两个字段。
|
||||
|
||||
补齐:`settled`(裁过没有)+ `rationale`(凭什么)一路带到卡上;
|
||||
裁量项在卡面变成**三态** —— 达标(绿)/ **AI 判为不满足 · 交你定**(琥珀,附理由)/ 未达标(红)。
|
||||
两处判据表(对话区的卡、工单报告)统一走同一个组件,免得三态只在一处生效。
|
||||
|
||||
### ② 看与改是两件事:表格默认锁着,按「编辑」才开
|
||||
|
||||
从前进待处理那一屏,整份件就是编辑态:A1 那份 54 个输入框一起亮着 ——
|
||||
人还没决定要改什么,屏上已经是一张待填的表。**件首先是给人看的。**
|
||||
|
||||
每张可改的表各带一道闸(`[data-edit-for="域.对象"]`):锁着 → 「编辑」;
|
||||
开了 → 「取消 / 保存」。取消还原成按下「编辑」那一刻的值,保存才把这张表交给外层缓冲
|
||||
(真正写回草稿层仍在提交那一下)。一屏两张表各管各的。
|
||||
**iframe 里依然一行脚本都没有** —— 按钮由父文档代理(`allow-same-origin`)。
|
||||
|
||||
**踩了一个跨 realm 的经典陷阱**:handler 里写 `event.target instanceof Element` ——
|
||||
`Element` 是**父文档**的构造器,而 target 来自 iframe 的 realm,原型链不同,
|
||||
`instanceof` 恒为 false。症状:点「编辑」毫无反应,而 class、CSS、选择器逐个查都对,
|
||||
监听也确实挂上了、事件也确实收到了 —— 每次都在 handler 第一行 return。
|
||||
**认能力不认血统**:有 `closest` 就当它是元素。
|
||||
|
||||
顺带把监听的挂载改成幂等的:`srcDoc` 换文档的时机抓不准,与其判断「挂过没有」,
|
||||
不如每次有机会(effect / onLoad / measure)就挂一遍 —— 同一个 (type, listener) 重复 add 无副作用。
|
||||
从前那个 `bound` 去重 ref 恰恰让「第一次挂在了空白文档上」变成了**永远没有第二次机会**。
|
||||
|
||||
## Harness 的五条死胡同:门拦住了,却没给改正的手段(2026-08-01)
|
||||
|
||||
**账**(每一轮都是真起单、真跑、真放行,`e2e-a1.mjs` 连跑):
|
||||
|
||||
| 批次 | 版本 | released | 熔断 | 备注 |
|
||||
|---|---|---|---|---|
|
||||
| 基线 | v15 | 4/5 | 1 | 那一轮 13 次拒回、23 拍、234k token |
|
||||
| c | v16(+drop) | 4/6 | 2 | |
|
||||
| d | v17(+名单/Completed) | 6/8 | 1 | 另 1 轮是模型侧变慢(4 拍 605 秒),非缺陷 |
|
||||
| e | v18(+同源/指路) | 7/8 | 1 | |
|
||||
| f | v18(+JSON 名单) | 7/10 | 3 | **三次里两次是同一条**:`必填齐` |
|
||||
| **g** | **v19(+required)** | **10/10** | **0** | 全程 0 拒回,10-11 拍 / 99-112k token |
|
||||
|
||||
|
||||
|
||||
连跑 A1 摸底:**5 轮里 4 轮干净、1 轮熔断**;再跑 6 轮又熔断 2 轮。
|
||||
挨个看失败形态,三条全是同一个病 —— **判据正确地拦住了一件办错的事,而模型没有一个动作能把它办对**。
|
||||
门本身没错,错在门后没有路。
|
||||
|
||||
### ① 写错一行没有退路 → 加 `drop`
|
||||
|
||||
`XTSD04GLGEAQG-I` 是客户明说本轮跳过的那颗料,被写进了有效清单。
|
||||
数报得全对(9 有效 / 1 跳过),行却有 10 条,判据 `record_rows = line_total` 当场拦住。
|
||||
而模型**改不动**:它唯一能改的是 `line_total`(改成 10,`skipped_total` 又不对)。
|
||||
13 次拒回、23 拍、234k token 才熔断 —— 而它从写错那一刻起就已经无解。
|
||||
|
||||
行动型里只有 `write` 没有删:草稿层本来就是这一单的过程事实,过程里写错一行是常事。
|
||||
加 `drop`(`ledger.draftDrop` → `DraftStore.drop`):**只撤本单草稿层那一笔**,
|
||||
正式版一个字都动不了,也不抛事件 —— 不是第四种写,放行门的位置没变。
|
||||
撤不到如实回 `false`(主键写法对不上是常见原因):静默成功会让模型以为撤掉了,
|
||||
下一拍判据照旧不过,它又去撤一次。
|
||||
|
||||
### ② 拒回只给数字,不给「差在哪几行」→ 加主键名单
|
||||
|
||||
行数类判据拒回时只说「10 ≠ 9」。模型看得见对不上、看不见多的是哪一行,
|
||||
于是只能猜:改数、重写、再提。三种失败形态全是这一类 ——
|
||||
记录多一行、分类表 0 行、替代请求 1 张而 C 类 0 条。
|
||||
|
||||
基座数得出来的事实,基座给:`rowKeyList` 把每张表本单写下的行的**主键**摆进读数
|
||||
(封顶 12 行,超出的如实说还有几行)。多的那个 PN 就在名单里对着 —— 一句话能办的事。
|
||||
**不替模型判断谁多谁少**:名单是事实,判断留给它。
|
||||
|
||||
### ③ 拒回的话把模型指上死路 → 按状态分开说
|
||||
|
||||
「`S3_confirm` 标了人工激活,要先 start 再干活 —— 当下是 **Completed**」。
|
||||
模型照做去 start,而 `Completed → Active` 是非法迁移,又被拒;回头再撞第一堵墙。
|
||||
两堵墙对着撞,连拒五拍熔断。
|
||||
**拒回的话是模型下一拍的行动指南;指错方向比不给指引更糟。**
|
||||
现在收掉/停用的项说「这一步已经办完收掉了,挂到当下这一步去(可选:…)」并列出可为的项;
|
||||
`start` 一个已收的项也改成无事发生(与「已在进行中」同款)——收掉的项不重新打开,但不必为按一下报错。
|
||||
|
||||
### 顺带:两处收步判据比验收判据松
|
||||
|
||||
- `S5` 只看自报的 `untyped_total = 0` —— 数归零就收,而验收比的是
|
||||
`type_table_rows = record_rows`(基座数的行)。数对了表没写全,**收了步却过不了验收**,
|
||||
模型得回头补写一张已经收掉的表(c3 那一单正死于此)。
|
||||
- `S6` 只看「写过 `alt_request_total` 这个数」—— 记完账就收,而单还一张没开。
|
||||
|
||||
两处都改成与验收判据同源。**收步比验收松,松出来的那一截就是返工。**
|
||||
S6 那条要额外挂一句 `untyped_total = 0` 当前置:两个 `_rows` 由基座铺 0,
|
||||
而 `type_c_total` 要模型写过才有 —— 开局两边都是「没写过」,S-FEEL 里相等即真,
|
||||
这一步会开局即收(包内自测那条「doneWhen 开局就为真」当场拦下)。
|
||||
|
||||
### ④ 名单给了,主键却抄不对 → 摆成能原样粘的 JSON
|
||||
|
||||
装上 `drop` 之后再跑,模型**用了**它 —— 撤 `{"rfq_ref":"RFQ-MD14.1","pn":"XTSD…"}`,
|
||||
连撤三次都撤不到。而名单里那一行是 `RFQ MD14.1/XTSD…`:**单号里是空格,不是横杠**。
|
||||
名单用 `/` 拼串,模型得自己猜第一段是哪一列;列名它猜对了,值却按更常见的写法转写了一次。
|
||||
|
||||
**主键是认行的钥匙,一个字符都不能差 —— 那就别让它转写。**
|
||||
名单改摆 JSON(`{"rfq_ref":"RFQ MD14.1","pn":"XTSD…"}`),拒回里明说「原样粘进 keys 就行」。
|
||||
|
||||
### ⑤ 最后一条自报的机检判据 → 台账声明加 `required`,基座自己数
|
||||
|
||||
十轮里三轮死在同一条:**「必填齐(PN / 年用量)」[L1] `missing_key_total = 1`**,连拒五拍熔断。
|
||||
这条是 L1 机检,读的却是**活动自己写的数**。人在 S3 卡上补了年用量、基座已经把值写回草稿层,
|
||||
而那个汇总列还要模型自己重算 —— 它漏算一次,这条判据就永远绿不了,而数据本身完全合规。
|
||||
(其中一轮它还想拿 `drop` 撤掉一行有效料来「解决」这条判据 —— 手段有了,可用错了地方,
|
||||
因为门指的方向本来就是错的。)
|
||||
|
||||
这正是自己立过的那条纪律的漏网之鱼:
|
||||
> 机检判据只许读基座数得出来的数;读活动自报的数,它就不是门,是一句「改哪个数能过」的提示。
|
||||
|
||||
台账字段声明缺一维:**没有「必填」**,所以基座数不出来。补 `fields[].required`(可选,
|
||||
不建 NOT NULL —— 草稿层的本分就是过程中允许缺),基座据此出两个读数:
|
||||
`域_对象_missing`(缺必填的行数)与 `_missing_keys`(缺在哪几行、缺哪一列)。
|
||||
判据改读前者,S4 的入场前提也一并改。**模型改不动这个数,也不必改 —— 它只要把那一格补上。**
|
||||
|
||||
### 还删了一个孤儿
|
||||
|
||||
`intake-gaps` 模版:7 处 `view:` 引用没有一个指向它,声明着、渲染着、随包走,
|
||||
而没有一步会推它 —— 缺口那件事早已由 `intake-overview` 的红胶囊与题头计数说清楚了。
|
||||
|
||||
## 对话区太密集:把「一拍一句」改成「一轮一句」(2026-08-01)
|
||||
|
||||
用户贴了一段对话区,五条气泡里四条长这样:
|
||||
|
||||
> 「我这就把审定结论落账,收掉这一步,往下走查历史价。」
|
||||
> 「我去查这三颗料的历史价,拿到判据就写进分类表。」
|
||||
> 「…我这就把标写进分类表和受理记录,收掉这一步。」
|
||||
> 「…我这就把两张请求单和去向旗写好,收掉这一步。」
|
||||
|
||||
原话:「太密集了」。**那些不是给人的话,是工序播报** —— 写哪张表、收哪一步,
|
||||
人既不用知道也帮不上忙;那一段本该由「AI 工作中」的办事条覆盖。
|
||||
|
||||
用户定的形状:**设定目标 →(分析现状 → 规划 → 执行)× N 轮 → 输出一个聊天气泡**,
|
||||
气泡可以是仅结论,也可以是**结论 + 功能卡(录入 / 执行 / 验收)两部分**。
|
||||
|
||||
三处改到位:
|
||||
|
||||
1. **哪一拍才出声**(`turn-runner` 的 `handsOff`):一轮的收口 = 控制权要交出去 ——
|
||||
发卡问人(ask)、派工单(human)、摆岔路(decide)、立监听等外部(listen)、
|
||||
提交放行(submit)。别的拍照旧留痕进工作日志(`internal`),只是不进对话区。
|
||||
按「打算做什么」判而不是「做成了什么」:交代必须赶在动手之前说出口,人要先看见
|
||||
「我要去办什么」再看见卡。代价是模型说要发卡却被拒的那一拍会白说一句 ——
|
||||
那种拍本来也该让人看见。
|
||||
2. **模型知道这条规矩**(`agent-runner` 的提示词与 say 字段说明):
|
||||
否则它照旧每拍写一句播报,只是被我们默默丢掉 —— 白烧 token,还写不好那一句总结。
|
||||
现在明写:say 只在交出控制权那一拍才摆到人面前,写这一轮的结论与为什么现在需要他。
|
||||
3. **结论与卡并成一个气泡**(`chat-projection`):它们本是同一轮的两半。
|
||||
从前各占一条,每轮就是「一条话 + 一条只有标题加个按钮的卡」,而卡的标题多半是
|
||||
那句话的缩写。顺带把收尾那三条引擎回声(裁决:pass / 落地收敛,发射输出口 / 终态)
|
||||
收成一条 —— 裁决是人自己刚按下的,发了哪几个口在工作日志里。
|
||||
|
||||
**并卡的判断一度写错**:挡住并卡的条件写成 `tag !== 'conclusion'`,而交代本身常常
|
||||
就是结论口吻(「判据我都裁过了,交你签」),模型给它标 conclusion 完全正当 ——
|
||||
于是第一轮并对了、收口那一轮没并,同一屏两种样子。改成按**来路**认(只有 terminal
|
||||
那一条不许认领卡),不按语气签认。
|
||||
|
||||
账:同一条 11 拍的 A1,对话区从 **9 条降到 5 条**(系统开场 1 + AI 结论带卡 2 +
|
||||
办结回执 1 + 收尾 1);被压下的 8 条交代一条不少地留在工作日志里。
|
||||
|
||||
## 放行卡上那两条不该在的判据(2026-08-01)
|
||||
|
||||
用户看着放行卡的判据表:「最后的验收 L4 选题口不应该是收口验收的指标;
|
||||
L5 当前卡片就是放行卡,也不该显示在这里 —— **这里是判据**。」
|
||||
|
||||
两条都对,而且是两种不同的错:
|
||||
|
||||
**① L4「去向判断合乎业务常理」—— 判据得能指向一个可做的动作。**
|
||||
去向早已不是判断了:三个出口各写了 `when`,放行那一刻基座按序求值、首个命中即出。
|
||||
再挂一条 L4 让模型裁「这个去向合不合理」,是让它给一个**自己没做的决定**盖章 ——
|
||||
裁「满足」是废话,裁「不满足」也改不动任何东西(能改的是三类条数,而那已经由
|
||||
route-alt / route-rfq 两条机检看着了)。这种判据只是一道要人按一下的手续。已删。
|
||||
|
||||
**② L5「BD 放行」—— 判据是签字前要核的东西,不是签字这件事自己。**
|
||||
那张卡本身就是这道关。把它列进判据表,等于在放行卡上摆一条恒为「未达标」的项:
|
||||
人一路核到底,最后一行永远是红的,而消掉它的动作就是按下同一张卡上的「确认通过」;
|
||||
「n/m 达标」那个数也因此永远差一条,看着像还有活没干完。
|
||||
**声明留着,呈现去掉** —— L5 说的是「这份交付物必须由人放行」,由 `T_release` 的
|
||||
人工激活 + 终局唯放行落实,提交门与预检本来就把它排除在外;少的只是卡面这一行。
|
||||
滤在基座组卡处(`engines.ts`),六个活动一起生效,不是 A1 单独打的补丁。
|
||||
|
||||
验收:端到端 11 拍 released、被拒 0;卡面九条(L1×3 / L2×4 / L3×1 / L4×1),
|
||||
浏览器里数过 —— 没有 L5,也没有那条去向裁量。
|
||||
|
||||
## 一份报告里嵌着另一份报告(2026-08-01)
|
||||
|
||||
用户:「现在你用你自己的报告格式**嵌套了一层**我要的报告格式」。
|
||||
|
||||
工单报告组件(`WorkOrderReport`)自带一整套外壳 —— 题头、编号/所属/编制/派发带、
|
||||
依据指标带、章节头(`Part`)、判据表、落款动作 —— 然后把活动推的那一屏
|
||||
`<ViewContainer>` 塞进自己身体里的一个「数据与分析」章节。两套版面语言叠在一起,
|
||||
外面那套还先声夺人。搬 PRD 目标件那几轮全在改里面那一层,而人一眼看见的是外面那层。
|
||||
|
||||
**分家口径(用户定)**:
|
||||
|
||||
| 区 | 放什么 |
|
||||
|---|---|
|
||||
| 工作区 · 待处理 | **只放那份件**,可就地改 |
|
||||
| 工作区 · 工作成果 | **只放那份件**,不加编辑态 |
|
||||
| 对话区的卡 | 作业指导书 · 依据 · 判据 · 填报表单 · **提交与裁决** |
|
||||
|
||||
就地改的做法:可改的格子挂 `data-col="台账列名"`、行容器挂 `data-edit="域.对象"`,
|
||||
渲染时每格多渲一个 `<input class="jc-cell">` 孪生件,**只读那份与输入那份由样式表二选一**
|
||||
(根节点带不带 `editing`)。容器是 `sandbox="allow-same-origin"` 的无脚本 iframe:
|
||||
外层读得到也改得动里面的 DOM,开编辑就是加个 class,收值就是把 input 读出来 ——
|
||||
**iframe 里始终一行脚本都没有**。为什么两份都渲:同一份件既在待处理给人改、
|
||||
又在工作成果里定稿归档,**两处是同一份**;渲两套等于存两份,存两份就会分叉。
|
||||
|
||||
### 两个只在「真人点」那条路上现形的洞
|
||||
|
||||
跑测脚本一直好好的,浏览器里一走就露:
|
||||
|
||||
1. **监听挂在 `about:blank` 上**。`srcDoc` 是异步换文档的,渲染那一刻
|
||||
`contentDocument` 还是空白页 —— 监听挂上去,srcdoc 一到就随旧文档一起没了。
|
||||
症状:人在件上改了两格,提交时载荷里一个字都没有(和从前「表格改动只活在组件局部
|
||||
state」是同一种病的第二种得法)。修:回调放 ref、在 `onLoad` 里再挂一次、
|
||||
记住挂过哪一份文档。
|
||||
2. **算出来的去向没交回去**。`routedPort`(基座按判据算出的那一口)在卡上只作展示,
|
||||
`selectedPorts` 恒空;人按「确认通过」→ 放行落地判「选择组未选出口」→ 整单 `blocked`。
|
||||
**台账已经合并进正式表了,单子却死在最后一步。**
|
||||
试跑脚本自己带着 `port`,所以这条一直被盖着。修:有 `routedPort` 就把它当作选中的那一口
|
||||
—— 人按下的「确认」,确认的正是基座算出来的那一口。
|
||||
|
||||
验收:浏览器里走完两道人环 —— 件上改两格(DAG 年用量 0→30000、UMW 制造商补上)→ 提交
|
||||
→ 落草稿层 → 放行 → **released**,九行数据落进正式表;工作成果那一屏零输入框、零外壳。
|
||||
|
||||
## 阶段2 整个没发生:把「人的门」写成了「缺东西时的分支」(2026-08-01)
|
||||
|
||||
用户:「为啥步骤发生了变化?阶段2没有了?**我都没确认需求 它就让我验收了**」。
|
||||
|
||||
查下来不是步骤被删了,是这道门从一开始就**不是一道门**。「受理需求经过人」写在归一那一步的
|
||||
收步条件里,是半句话:
|
||||
|
||||
```
|
||||
doneWhen: line_total > 0 and missing_key_total = 0 and (missing_minor_total = 0 or answered_total > 0)
|
||||
└────────── 只在缺东西的时候才叫人 ──────────┘
|
||||
```
|
||||
|
||||
于是一封字段齐全的询价进来,这半句在**拆完那一刻**就为真 —— 引擎在下一拍开头把归一自己收了,
|
||||
一张卡都没发,人下一次被叫到已经是终局的验收放行。缺字段那条路上看着「正常」,
|
||||
所以前面几十轮跑测都没露出来:样例 BOM 恰好总缺那么两格。
|
||||
|
||||
**教训:判据是「这件事办完了没有」,不是「要不要办」。**
|
||||
要不要经过人是模型结构的事;写进判据,就成了「不缺就不必经过人」。
|
||||
而 PRD 的目标件早就把答案写在页脚了 —— `A1-询价受理需求.html` 的落款是
|
||||
「AI 拟稿,你审定;可就地修改后再确认」,按钮是【修改】【确认受理记录】:
|
||||
**它从来就是一张人签的页**,不是缺东西时才弹的追问。
|
||||
|
||||
处置:`S3_confirm` 独立成项,与放行同款 —— **人工激活 + 不给 doneWhen**
|
||||
(触达世界的项一律不给 doneWhen,给了就是给引擎一把替人签字的钥匙);
|
||||
S4 的入场前提改读 `confirmed_total` —— 一个只有「人把卡交回来」才产生的数。
|
||||
规范 §7.1 立为纪律 3.5。
|
||||
|
||||
### 同一次跑测挖出的零条死锁:**没写过 ≠ 写了 0 行**
|
||||
|
||||
拿一封「全齐、无 C 类」的询价复跑,卡在了另一处:验收判据
|
||||
`quote_intake_alt_request_rows = type_c_total`(没有 C 类 → 0 = 0,本该成立)
|
||||
连拒五拍熔断。原因是行数读数**只数写过的表** —— 一行没写,那个键整个不存在,
|
||||
而 S-FEEL 里未绑定的名字参与比较求值为假。数据完全合规,门却永远打不开。
|
||||
|
||||
已修:按 `declarations.ledger` 把每张**声明过**的表先铺成 0,再让实际行数盖上去。
|
||||
声明面就是授权面,不在声明里的表本来也数不着。
|
||||
|
||||
验收:常路 11 拍 released / 被拒 0;全齐无 C 类那条 10 拍 released、去向 `to-sourcing`
|
||||
(那 2 次被拒是模型真忘了给 B 类开询价请求,门拦住它并被它改对了 —— 门在做它该做的事)。
|
||||
|
||||
## 版面重做:块齐了不等于长得像(2026-08-01)
|
||||
|
||||
用户第三次指出「HTML 报告跟我给你的两份完全不一样」。我之前两轮的「25 块到位」
|
||||
是**结构断言**(某个 class 在不在),而结构断言对**版面一点约束都没有** ——
|
||||
块可以全在,而版面是另一套语言。截图并排一比,差的是这些:
|
||||
|
||||
| | PRD 目标件 | 我的(重做前) |
|
||||
|---|---|---|
|
||||
| 题头下 | 直接进内容 | 多一段灰色 summary |
|
||||
| 元信息 | 没有 | 多一条「单号/客户码/主题」带 |
|
||||
| 章节头 | 小号加粗字 + 细线 + 右端 mono 小签 | 彩色方块编号 + 大标题 |
|
||||
| 指标带 | 没有 | 插了 5-7 格 |
|
||||
| 字号 | 固定 11/12/18px | clamp 响应式,整体偏大偏松 |
|
||||
|
||||
**教训:视觉目标件不能用 DOM 断言验收,只能看。** 这一轮把 PRD 的 CSS 数值逐条抄过来
|
||||
(题头 16/20/13、章节头 11px/800/.12em、正文 12px、表头 10.5px、落款 13/20/15),
|
||||
`section()` 去掉编号参数(六个活动一并迁移),删掉 summary / meta / statsBand ——
|
||||
数字改进**章节右签**(目标件就是这么摆的:数挨着它说明的那一段站着)。
|
||||
|
||||
顺带修掉截图里才看得见的两处:
|
||||
- **刻度条格子里冒出数字**(`meter` 同时带了 `data-slot`,九格排着「0 0 0 … 1」)——
|
||||
只保留 `data-class-of` 上色,格子不填字;
|
||||
- **空成交轨像坏掉的进度条** —— 没数据就别画那一格,把「为什么没有」写在卡脚。
|
||||
画一个空壳比不画更像故障。
|
||||
|
||||
### 后续:抄数值仍然不够,最后是**整份搬过来**(同日)
|
||||
|
||||
上面这一轮把 CSS 数值逐条抄了过来,用户看完仍然说不一样 ——「你直接把两份报告
|
||||
copy 过来替换掉你的 UI,重新适配功能层」。于是不再用 `@sdk` 那套外壳复述:
|
||||
`PRD_CSS` / `PRD_SPRITE` 是**机械抽取**的整段样式表与图标精灵(Python 抽,不手敲,
|
||||
只把反引号换成 `'` 好活过 TS 模板串),标记逐块搬进 `report.ts`,只换掉四样东西
|
||||
(写死的行→`data-rows`、JS 算的数→汇总列、JS 插的胶囊→`:empty::after`、
|
||||
JS 拼的 class 与 `style="flex:N"`→`data-class-of`)。规范 §10.3.1 已立此法。
|
||||
|
||||
搬的过程中挖出**渲染器一个静默失色的洞**:`data-class-of` 从前只在**带 `data-slot`
|
||||
的元素**上生效(两者写在同一条正则里)。于是分布条那种纯色块
|
||||
(`<i data-class-of="type_tag"></i>`,格里不写字、只按值上色)整条不着色 ——
|
||||
属性还留在成品上,一个 class 都没长出来,屏上是一排灰格子。
|
||||
**而灰格子看着像「还没算出来」,不像「没上色」**,查了两遍才认出来;
|
||||
老用例每一条都同时写了 `data-slot`,所以谁都没拦住它。
|
||||
现在拆成两遍:取值填字是一件事,按值上色是另一件事,后者不以前者为条件(补了 4 条用例)。
|
||||
|
||||
同批做实的三件:
|
||||
- **去向注入视图**(`case:routeKey/routePort/routeTo/routeWhy`)—— 判定表上命中哪一行
|
||||
由基座按契约的出口谓词求值后注入,与放行时发口是**同一次判定**;报告不自己算、也不抄判据。
|
||||
- **分布条按条数分宽** —— 原型是 JS 写 `style="flex:N"`;改由 `data-class-of` 把
|
||||
**条数本身**投影成 `v-5`,样式表备一把 `v-1…v-20` 的梯子取宽。模版仍然不许算。
|
||||
- **`contact_name` 落进 `quote_intake.verdict`** —— 联系人卡上那个人。它本在
|
||||
`customer.contact`(主数据)里,可老客户不重写主数据、本单草稿里根本没有那一行;
|
||||
顶层裸键又与 `customer.profile.name` 撞名(后写的赢)。
|
||||
**一张写着公司名的联系人卡比空着更坏**,所以跟 `customer_name` 同一笔写下。
|
||||
|
||||
Reference in New Issue
Block a user