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
+3 -2
View File
@@ -2108,7 +2108,7 @@
.proto-root .jc-rp-vals dd { margin: 0; padding: 8px 12px; font-size: 12px; color: var(--ink); line-height: 1.65; background: #fff; }
/* 落款 */
.proto-root .jc-rp-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; padding: 13px 20px 15px; border-top: 2px solid var(--line); background: #fbfcfd; }
.proto-root .jc-rp-foot { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; padding: 13px 20px 15px; border-top: 2px solid var(--line); background: #fbfcfd; }
.proto-root .jc-rp-sign { display: flex; align-items: center; gap: 9px; flex: 1 1 200px; min-width: 0; }
.proto-root .jc-rp-ava { flex: none; width: 30px; height: 30px; border-radius: 999px; display: grid; place-items: center; font-size: 12px; font-weight: 700; color: #fff; background: var(--brand); }
.proto-root .jc-rp-sign b { display: block; font-size: 12px; color: var(--ink-2); }
@@ -2301,7 +2301,8 @@
.proto-root .jc-fc-h em.bad { color: var(--danger); }
/* 卡体与动作栏 */
.proto-root .jc-fc-b { padding: 11px 13px; display: flex; flex-direction: column; gap: 10px; }
.proto-root .jc-fc-ft { display: flex; align-items: center; gap: 8px; padding: 9px 13px; border-top: 1px solid var(--line-soft); background: #fbfcfe; }
/* 动作靠右(2026-08-03 用户:「提交按钮放左侧太奇怪了」)—— 验收卡的「重做|通过」有 jc-fc-sp 撑开,不受影响 */
.proto-root .jc-fc-ft { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 9px 13px; border-top: 1px solid var(--line-soft); background: #fbfcfe; }
/*
* **交不了的时候,说清差在哪** —— 只摆一个灰按钮等于让人猜自己哪儿做错了。
* 底色用警示色而不是错误红:这不是出错,是活还没干完。
@@ -81,6 +81,13 @@ function collectEdits(edits: ViewEdits, order: WorkOrder, onField: (k: string, v
export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: ViewRegionProps) {
const [view, setView] = useState<Tab>('open');
const [page, setPage] = useState(0);
/*
* **件内翻页**(2026-08-02 用户:「一个执行单可以派多个 HTML 待执行报告,
* 合理利用右上角的翻页和待处理的数字气泡」):件里带 `data-page` 的可见段
* 各算一页(A2 = 一颗待选型芯片一页)。单卡多页时,右上角那对箭头翻的是页。
*/
const [docPage, setDocPage] = useState(0);
const [docPages, setDocPages] = useState(1);
/** 工作成果:列表里点开的那份定稿报告 */
const [doneSel, setDoneSel] = useState<string | null>(null);
const [openFile, setOpenFile] = useState<FileDoc | undefined>(undefined);
@@ -279,29 +286,56 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
<Ico name="chevron-left" s={14} /> 返回列表
</button>
) : view === 'open' ? (
<span className="jc-tnav-pg" aria-label="翻页">
<button
type="button"
className="jc-tnav-btn"
disabled={pos === 0}
onClick={() => setPage(pos - 1)}
aria-label="上一件"
>
<Ico name="chevron-left" s={15} />
</button>
<b className="jc-tnav-n jc-mono">
{openList.length === 0 ? 0 : pos + 1}/{openList.length}
</b>
<button
type="button"
className="jc-tnav-btn"
disabled={pos >= openList.length - 1}
onClick={() => setPage(pos + 1)}
aria-label="下一件"
>
<Ico name="chevron-right" s={15} />
</button>
</span>
/* 单卡多页时这对箭头翻**件里的页**(一颗芯片一页);多卡时照旧翻件 */
docPages > 1 && openList.length <= 1 ? (
<span className="jc-tnav-pg" aria-label="翻页">
<button
type="button"
className="jc-tnav-btn"
disabled={docPage === 0}
onClick={() => setDocPage(docPage - 1)}
aria-label="上一页"
>
<Ico name="chevron-left" s={15} />
</button>
<b className="jc-tnav-n jc-mono">
{docPage + 1}/{docPages}
</b>
<button
type="button"
className="jc-tnav-btn"
disabled={docPage >= docPages - 1}
onClick={() => setDocPage(docPage + 1)}
aria-label="下一页"
>
<Ico name="chevron-right" s={15} />
</button>
</span>
) : (
<span className="jc-tnav-pg" aria-label="翻页">
<button
type="button"
className="jc-tnav-btn"
disabled={pos === 0}
onClick={() => setPage(pos - 1)}
aria-label="上一件"
>
<Ico name="chevron-left" s={15} />
</button>
<b className="jc-tnav-n jc-mono">
{openList.length === 0 ? 0 : pos + 1}/{openList.length}
</b>
<button
type="button"
className="jc-tnav-btn"
disabled={pos >= openList.length - 1}
onClick={() => setPage(pos + 1)}
aria-label="下一件"
>
<Ico name="chevron-right" s={15} />
</button>
</span>
)
) : null}
</div>
@@ -416,12 +450,21 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
) : view === 'done' ? (
doneItem !== undefined ? (
/*
* 定稿归档 = **同一份件,不加编辑态**(§9.3 第 2 条:在办与归档同一张模版)。
* 与待处理那一屏一样,这里也只放件本身 —— 判据留在对话区那张办结卡上,
* 归档之后「凭什么算过」比当时更常被问起,所以它跟着卡走,不跟着件走。
* **定稿归档 = 办结那一刻的件快照,1:1 静态重放**(2026-08-02 用户:
* 「HTML UI、数据、内容、布局都要 1:1 地从待处理状态转换为静态报告
* 放到工作成果」—— 反复强调过)。
*
* 从前两条路都不对:验收单渲**当下**的任务视图(随后续推进还会变),
* 执行单干脆退回 WorkOrderReport 卡壳(题头/编号带/blocks)——
* 没有一样是人办结时看到的那份。现在办结那一刻基座把渲好的件
* 存进工单(`finalHtml`),这里原样静态渲它;老单没有快照的,
* 退最近一帧任务视图,再没有才退卡壳(历史兼容)。
*/
doneItem.mode === 'acceptance' && detail.viewHtml !== undefined ? (
<ViewContainer html={detail.viewHtml} title={`${detail.card.activity} · 定稿报告`} />
doneItem.finalHtml !== undefined || detail.viewHtml !== undefined ? (
<ViewContainer
html={doneItem.finalHtml ?? detail.viewHtml ?? ''}
title={`${detail.card.activity} · 定稿报告`}
/>
) : (
<article className="jc-mst-doc">
<WorkOrderReport
@@ -504,6 +547,8 @@ export function ViewRegion({ detail, onReplyOrder, values, onField, locate }: Vi
<ViewContainer
html={detail.viewHtml}
title={`${detail.card.activity} · 汇报报告`}
page={docPage}
onPages={setDocPages}
editable
onEdits={(save) => {
/*