A2-A7 全部补齐到验收线;顺手把几处「门拦住却不给路」的坑填了

活动:A2 选型 / A3 寻源 / A4 定价 / A6 议价 / A7 归档五件各补成真活动包 ——
契约宽进(input.required 空,归一在活动内)、台账必填声明(含行级 requiredWhen)、
入场哨兵与 L1/L2 判据一律读基座数、两份件(在办可编辑 + 办结静态快照)、
三案例常备、包内自测。e2e 各三案例 released、行动被拒 0、发口正确、提交闸每单真拦。

基座(共享面,改一处影响所有活动,故每处都带回归):
· 重发判断走在站上场之前 —— 人答完那步常已收掉,readyToWork 先抛「不能再往它上面挂活」,
  于是「重发已答过的卡 = 无事发生」这条唯一活路在最需要时够不着,A1 基线当场熔断;
· 决策卡补幂等兜底(与执行工单同一把尺):头两次无事发生,第三次起才算被拒;
· 输出没成形先就地重问一次,原话进工作日志、不再摆上卡面;
· 工具调用读数 tool_<名>_calls —— 「机检只读基座数」这条纪律第一次覆盖到工具;
· 执行卡 steps/standard 补进出参 schema(从前能到卡面全靠厂商 JSON 模式宽松);
· 件的就地更新改逐节点 morph,保存先把新值镜像进只读那份 —— 不闪、不等回声。

A1(基线,全程守住):措辞三档对齐(必填/建议/选填)、禁止拿历史价当客户目标价、
卡骨架从步骤剧本搬进说明书(压成一拍连做时剧本没摊开,会发出没有指导书的裸卡)。

契约两处**新增可选字段**(非破坏):FieldDeclaration.requiredWhen、工单 finalHtml;
api-report 同批更新。docs/adr 与 docs/archive 的 13 份文件在工作树里不见了,
本次予以恢复而非提交删除。

门禁:typecheck / lint / depcruise / test(1366)全绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-03 12:28:31 +08:00
co-authored by Claude Opus 5
parent 85466e815f
commit ade0fa5a94
90 changed files with 17969 additions and 2316 deletions
+65 -2
View File
@@ -22,8 +22,12 @@
三条硬规矩:
1. **一单一模版贯穿始终**:同一个局面,在办与归档用同一张;局面变了换张
(A1 三张:受理需求 → 料号分类 → 受理记录),不在一张里堆条件分支。
1. **每份件两态 1:1,不同工作各有各的件**(2026-08-02/03 用户两次校准):
「1:1」的落点是**每份件与它的办结快照**(在办可编辑 ↔ 回执收下那一刻存
`finalHtml`,成果页静态重放,数据/内容/布局一个字不差)—— 不是全程只许一张。
作业的件(如 A2 选型 One Pager)与收口的件(整合清单)是两件工作两份件:
「清单是所有选型完成后收口才整合的东西」。别再把两态统一误执行成一张到底
(踩过:验收工单打开还是选型页,用户当场指出)。
2. **剧本每步的 `view` 定推哪张**,写清单时定死,不归模型临场挑;放行那一步的
`view` 必须指向验收目标件 —— 最后摆上去的那张就是归档件。
3. **数据变了基座重渲**(repush):人在件上保存、在卡上回填,落草稿层后服务端拿
@@ -180,6 +184,14 @@ A1 两份报告是 1:1 平移 PRD 目标件:样式表整段搬(`prd-style.ts`
## 8. 版式纪律
- **空占位三级统一**(2026-08-02 用户定,全局口径):**必填(红)· 建议填(橘)·
选填(灰)** —— 与声明的 `required` / `expect` / 其余一一对应,胶囊文案就写这三个词。
归档静态态它们如实呈现「没有」。
- **选择列直接选**(如 A2 替代等级):三枚自绘 radio(`input[type=radio].jc-cell[data-col]`,
radio 盖满选项格可点、`:has(:checked)` 高亮),**不写 checked** —— 当前值由
`data-class-of` 投影 v-* 描虚线;基座对未选中的 radio 整列不交(「没点」=「不改」)。
收值按「input 的祖父」归行:一张卡里所有输入格的祖父必须是同一个元素。
- **铺满工作区**:去掉外层宽度上限与横向留白、卸掉外框圆角边 —— 件就是这一屏的正文,
不是嵌在里面的另一个页面(`jc-stage { max-width:none }`)。
- 必填/选填胶囊只在**在办态**摆;归档件一律 `dimempty`(那时空格的意思不再是
@@ -187,6 +199,51 @@ A1 两份报告是 1:1 平移 PRD 目标件:样式表整段搬(`prd-style.ts`
- 别摆恒常说「没有」的横幅(「本轮不报 0 行:—」),别把同一个数摆两处
(人得停下来对一遍两处一不一样);已在别处说过的结论不再复述一遍。
- 标记里不写 `**` 星号这类记号 —— 卡面与件都是纯文本/纯 HTML,记号不会变粗体。
- **概念图形是报告的第一语言**(A2 迭代周用户反复要「图形占比大一点」):
芯片小像、流光走线、引脚对照、漏斗 —— 内联线稿 SVG(stroke currentColor,
配色走样式表)把「这是件什么事」画出来;数据格是第二语言。一份让人愿意看的
报告先立视觉隐喻,再摆数。
- **收值三铁律**(件上就地改的结构约束,违者行互相覆盖或值丢失):
① 同一行所有输入格的**祖父必须是同一个元素**(collect 按祖父归行);
② 主键格必挂 `data-col`(行的钥匙,可以是隐藏格);
③ radio 不写 `checked`,「没点」=「不改」(当前值靠 `data-class-of` 投影描样式)。
## 8.5 一页汇报(slide 语言)—— 强交互全走纯 CSS
用户对 A2 选型件的要求(2026-08-02):「不够华丽,要强交互的汇报形式,
直接拿去当一页 PPT 的程度」。沙箱零脚本不变 —— **「强交互」全部由样式表承担**,
数据仍只经 `data-slot` / `data-class-of` 投影,动效是呈现,不是判断。
参考实现:`activities/quote-alternative/src/views/report.ts` 的 `ALT_WORKSHEET`。
**构图**:主视觉(hero)+ 编号小节(slide part)推进。
- **hero 深空底**:`radial-gradient` 两团光 + `linear-gradient` 深空 +
`::before` 电路点阵(radial-gradient 1px 点)+ `::after` 顶部一道光。
hero 内配色自成一组(亮字/暗丸/信标),别把 PRD 亮色变量直接用在深底上。
- **slide 小节头**(`jc-sp`):`01/02/…` 编号丸 + 题 + 一句注 + 延伸线 ——
编号是版面语言,不是数据。
- **逐段入场**(`jc-rise` + `.d1…d6` 延迟):`opacity/transform` 动画,
**全部包在 `@media (prefers-reduced-motion: no-preference)` 里**(连 `@keyframes`
一起包 —— 引用未定义的动画名等于没有动画,减弱设置下自然全静)。
- **流光走线**:两条同 path 的 SVG(`wire` 底线 + `pulse` 虚线),
`stroke-dasharray` + `stroke-dashoffset` 动画 + `drop-shadow` 发光。
- **悬停揭示**:卡片 hover 浮起(`transform + box-shadow` 过渡);
等级卡的口径段 `max-height:0 → hover/focus-within 展开`,加 `tabindex="0"`
让键盘也进得去;**触屏常开**(`@media (hover: none)` 里展开、藏掉「悬停展开」hint)。
- **空节自折**:整节只在有数据时出现 —— 包一层 wrapper,
`:not(:has(.行.v-xxx)) { display:none }`(如 nowwrap/donewrap)。
没有 NOW 行时「当前这一颗」整节(含小节头)不出,不留空壳。
**三条工程账**(都踩过):
1. **媒体查询要排在被它覆盖的基础规则之后** —— 同权重下后定义胜。
竖排断点里写了 `.jc-trace{display:none}`,而 `.jc-trace{display:flex}` 基础规则
定义在媒体块之后,于是竖排下走线照显:横着一根流光指向右边的空气。
2. **动效只动 `transform`/`opacity`/`box-shadow`**,别动布局属性;
`jc-rise` 会在每次 srcdoc 重渲后重播(保存一次播一次)—— 半秒内是 slide 感,
长了就是折磨,入场动画别超过 0.6s。
3. 队列丸/信标这类**状态灯**照旧走 `data-class-of`(`v-now` 信标呼吸、`v-done` 绿、
`v-wait` 暗)—— 「哪颗在办」是数据说的,动画只是把它说响一点。
## 9. 自测与浏览器走查
@@ -208,6 +265,9 @@ A1 两份报告是 1:1 平移 PRD 目标件:样式表整段搬(`prd-style.ts`
|---|---|---|
| 点「编辑」毫无反应,一切选择器都对 | `instanceof Element` 跨 realm 恒 false;或监听挂在 about:blank 上 | 认能力不认血统;幂等重挂(§3) |
| 保存时件「闪黑」一下 | srcDoc 换文档整页重载 | 就地换 body.innerHTML(§3) |
| 就地换了还是「闪一下」(2026-08-03 再报) | innerHTML 仍是**整树重建**:47KB 样式在 body 再解析一遍(head 那份还在,一文档两份)、SVG 全重建、CSS 动画回第 0 帧重起 | 第二层:逐节点 morph(`ViewContainer.replaceInPlace`)—— 一样的节点不碰,样式归位 head 去重,涟漪只落在真改了的格子上;任何一步栽了退回整树重建(闪但不错) |
| **保存后旧值还摆着一会儿**(真正让人以为没生效的那个) | 保存要跑个来回(POST 落草稿 → 失效 → 重取详情 → 新件推回),而闸在按下那一刻就关了 —— 这段时间屏上是只读那份的**旧值** | 按下保存**先把新值写进只读孪生件**再关闸(`mirrorTwins`),回声到达时 morph 一比已经一样;只镜像格子自己的值,红带/分布条/去向等基座算的数一律等回声 |
| 镜像/取值找不到孪生件而无声失效 | 渲染那一遍把 `data-slot` **连同槽位一起消费掉**,活 DOM 里两份都没有这个属性 | 认**位置**不认属性:`twinInputs` 把 input 紧接着只读那份写出,`previousElementSibling` 就是孪生件;**验证要在真单的 DOM 上做** —— 合成 fixture 里两份都写了 `data-slot`,照样绿 |
| 人保存了,一格都没落库 | 保存借道当下那张卡的 blocks | 按 `data-edit` 地址收,授权=台账声明(§2) |
| 改了类标,分布条/去向不动 | 汇总数是自报的没人重算;或没 repush | 分类列标 `tally` 让基座数;保存后必 repush(§1) |
| 纯色块整条灰着像没算出来 | `data-class-of` 曾以 `data-slot` 为前提 | 已修;自测钉住 |
@@ -216,3 +276,6 @@ A1 两份报告是 1:1 平移 PRD 目标件:样式表整段搬(`prd-style.ts`
| 报告与卡说的判据不一致 | 两处各算各的 | 全部读基座读数,同源(§2) |
| 归档件上还有编辑闸/胶囊 | 闸没锁在 `editable` 根类下;胶囊没分态 | §6-1、§8 |
| 版面「像又不像」目标件 | 用拼装件照着做而不是搬 | §7;截图比对 |
| 人保存后一行的值被另一行盖掉 | **主键格没挂 `data-col`** —— 收上去的行没有钥匙,服务端按「全表一致列」补键,多行对到同一行 | 主键列必挂 `data-col`(它不是编辑面,是行的钥匙);e2e 走 API 自带主键测不出,只有件上真按保存才现形(A2 走查逮过) |
| 逐行卡片克隆成套娃(第二行长进第一行里) | 渲染器按**第一个同名闭合标签**截行内容 —— `<div data-rows>` 的行模版里嵌了别的 `<div>`,行在半截被断开 | **行容器的标签名不得在行模版内部再出现**:容器换 `<section>`/`<tbody>` 这类行内不会嵌的标签(A2 选型档案卡逮过) |
| 空格胶囊比同行的数字**往下坠几像素** | 裸 inline-flex 胶囊按 baseline 挂:盒(字 + 内距 + 边框)比字的基线深,行高也随之忽高忽低 | 空格子(`:empty`)转 flex + `align-items:center` + `min-height` 撑住文字那份盒高,胶囊中线对数字中线(A1 逮过,2026-08-03) |