任务:用例定位的构建候选、全树核对、人工抽查和按文档的回退
依据:集成者 10-23 的终审(docs/hub-r4-progress.md 198 行)。用例定位还有几处没在真实数据上证实:
- 构建候选从没和独立答案
scripts/real/gold/builds.ts在真实数据上比过。场景只在 gold 带候选时才比(scripts/real/accept/case-location.ts609–611 行),取的又是整份回答里的第一处候选(90、313 行)。 - 全树核对(进度 84 行)比的是产品包的用例索引分面视图(
packs/product/famx/functions/cases.ts292 行),不是定位包;定位包有自己的约定代码(packs/workflow/case-location/functions/locate.ts28 行)。火车是在脚本外直接读条目比的;树里有多少份文档不在视图里,没有写。 - 关联编号的人工抽查只是报告里的一节(
case-location.ts126 行),没有记录。找不到某份文档的行时,场景拿整份回答去比(507 行)。
每条一个提交,先写测试。
- 分支和工作树:
gold-location,目录circle-hub-gold-location(依赖已装好) - 可以改的路径:
scripts/real/accept/case-location.ts、scripts/real/gold/builds.ts、scripts/real/hub-call.ts和它们的测试,以及scripts/real/accept-circle.test.ts里 case-location 那一例(21–31 行);新文件scripts/real/accept/location-tree.ts、scripts/real/accept/id-spot-check.ts和它们的测试。scripts/real/gold/locations.ts、scripts/real/accept/location-index.ts只导入,不改。别的路径不动(定位包、产品包、hub/、contracts/都不改)。 - 规则:同前(不推送、不建 PR、不发布、不部署;不碰 7420、钥匙串、环境配置文件和任何真实系统;测试只用合成数据;
git commit -s)。另外:- 核对代码不导入
hub/和packs/; - 脚本在屏幕上只打印计数、sha256 和通过与否,不打印文档号、关联编号、构建名、版本线、火车、模块和标题;带值的明细只写进
--out指的目录(0600,不覆盖,在仓库里的路径拒绝); - 合成样例的版本线用 10.6 以后的。
- 核对代码不导入
- 验证:
npx tsc --noEmit;node --test --test-concurrency=1对scripts/real/**。
要做的
- 构建候选的独立答案收两个名称族(
scripts/real/gold/builds.ts)。- 列表页按名称族分页(
packs/product/famx/README.md113 行:页名里带名称族),一条版本线的两族构建在不同的页上。现在只读第一个--page(193 行),基础构建不在这一页时“基于”的链就停下(139 行)。 - 改成收多个
--page,几页合起来解析:跨页去重,“基于”的链在合起来的构建里解析。 - 跑完打印一行:候选 N 个,FAMX a、FAMY b、别名 c、没有制作时间 d。不打印名字。
- 测试:两份合成列表页,FAMY 页上的别名基于 FAMX 页上的构建;同一构建两页都有只留一份;打印的那行里没有构建名。
- 列表页按名称族分页(
- 场景必须比构建候选(
scripts/real/accept/case-location.ts)。- gold 里每份文档都要有
buildCandidates(第 1 条对它所在版本线的输出)。没有就在check里记一项不符,不跳过(609–611、665 行),构造场景时也不抛错(capability-scenarios.test.ts219 行不带候选构造这个场景,只调 respond)。原来“没有 buildCandidates 时行为不变”的测试(case-location.test.ts29 行)和accept-circle.test.ts21–31 行那一例改成不通过。 - 这份文档的候选只从它自己的主张读:位置写着“条目 <文档>,要求项 build_candidates”的那条(`hub/server/claims.ts` 23 行)。“另有 N 个”也只从这条陈述里取:333 行现在取整份回答里第一个“另有”,会读到关联编号的“另有”(`locate.ts` 433 行)。一行被分页切开(“接上页:第 n 行的前 m 个字符在上一页”,`hub/server/text.ts` 1251 行)先接回去。没有这条主张,或者它是未知,记不符。文档>
- 逐个比,顺序也比:候选总数、别名数;按
orderGoldCandidates(75 行)的顺序比前 12 个,每个比名称、名称族、基础构建(别名写“基于”,不是别名的不写)、制作时间(到分钟;没有时是“制作时间未知”)、发布状态(“已发布”和日期,或“未发布”);“另有”等于总数减 12。文字写法见packs/workflow/case-location/functions/candidates.ts53–58、93 行。 - 覆盖:全部文档比到的候选里,FAMX、FAMY 和别名都至少有一个,否则记不符。
- 合成的回答照真实写法(条目明细见
hub/server/text.ts690 行)。测试:两族加别名的正例;15 个候选(前 12 个加“另有 3 个”)。反例各一个:gold 缺候选、回答缺候选、名称族错、基础构建错、制作时间错、发布状态错、顺序错、拿了别的文档的候选、把关联编号的“另有”当成候选的、没有 FAMY。
- gold 里每份文档都要有
- 按文档的回退和屏幕上的真实值(同一个文件)。
- 505–507 行:找不到这份文档的条目行,就拿整份回答去比。改成只从这份文档自己的地方读:start_run 里它的条目那一行,或者结果里位置写着这份文档、要求项 location 的主张。两处都没有,记一项不符,名称写“第 k 份文档”(gold 里的序号),不再到整份回答里找。288 行关联编号的同样回退一并改。
- 511、523、536 行用
has()按字面找文档 …[^\n]*…这样带正则写法的字符串,永远不成立,删掉。 - 报告会打印到屏幕(
scripts/real/accept-circle.ts85 行)。核对的名称、期望和实际只写序号(第 k 份文档、第 j 个候选、第 i 个编号)、字段名和计数;473 行出错时不再带回答的前 120 个字;buildManualPlmSections(112 行)只写找到几个、没找到几个。 - 测试:缺了一份文档的行、另一份文档的行有同样取值时仍不通过,名称是“第 k 份文档”;不通过的报告全文里没有 gold 里的任何值。
- 全树核对(新文件
scripts/real/accept/location-tree.ts)。- 输入:
--tree(敏捷平台用例树的原件导出,和scripts/real/gold/locations.ts读的同一种),--out,可选--facets(用例索引分面视图的 JSON,location-index.ts读的那种)。 - 独立答案:
locationsGold(tree).all(locations.ts421 行)给每份文档的版本线、火车、模块,火车也在脚本里比。这不循环:locations.ts是独立答案脚本,不导入hub/和packs/(9 行),被核对的是定位包自己的约定代码。读视图可以用parseFacetView(location-index.ts83 行),它只读视图的格式。 --collect --token-file <文件> [--url <地址>] [--batch 40] [--timeout-ms <毫秒>]:- 把树里的文档按批开始定位运行:
start_run,pack: case-location、product: famx,inputs.documents是每份的agile:mindmap:<文档>; - 用
get_nodes {reference}读result.reference指的完整结果,取结构化应答里content.text的 JSON(contracts/src/tools.ts107–108 行,hub/server/tools.ts458 行); - 一批被拒:path 是
/inputs/documents/<k>的那几份记为缺、原因写错误码(hub/runs/engine/inputs.ts216 行),其余的重开一次;没有 path 的错误整批记缺; - 每次应答原样存在
--out/runs/下;不带--collect时只读这里存下的; - 调用经
callTools(scripts/real/hub-call.ts48 行)。MCP 客户端默认 60 秒超时,61 行没传选项:给callTools加可选的单次超时,不给时不变。
- 把树里的文档按批开始定位运行:
- 比较:每份文档取结果里(任何分区)它的
location主张的statement.data。版本线按 lineKey 比;火车和模块做 NFKC、去首尾空白后逐字比;null 只等于 null。 - 屏幕上只打印:
- 树里的文档数;
- 相等、不等(分版本线、火车、模块)、缺(分开始被拒、结果里没有条目、条目没有定位数据)、只在结果里,各几份;
- 有
--facets时:树里有、视图里没有的几份,视图里有、树里没有的几份,视图的truncated和skipped(contracts/src/views.ts106–107 行); - 树原件的 sha256,全部结果文件按名字排序连起来的 sha256;
- 判定:不等、缺、只在结果里都是 0 才通过,退出码 0。
- 带值的明细(文档号、字段、两边的值、缺的原因、视图里缺的文档)写
--out/details.json。 - 测试用合成的树和假的调用函数,不连网:相等;三个字段各自不等;10.6 和 10.6.0 相等;null 对有值不等;按 path 记缺、其余重开;结果里没有条目、没有定位数据;只在结果里;分批(5 份、每批 2 份);视图里缺的份数和 truncated;
runs/不覆盖;屏幕输出里没有合成的文档号和取值;源码不导入hub/和packs/。超时:在hub-call.test.ts的假服务里加一个慢工具,超时短于延迟时出错,长于延迟时通过。
- 输入:
- 人工抽查的样本和记录(新文件
scripts/real/accept/id-spot-check.ts)。sample --results <第 4 条的 --out> --count <n> [--seed <s>] --record <文件>,n 默认 10:- 从结果的
location数据里取全部requirements和defects,照定位包的写法记成plm:story:<编号>、plm:bug:<编号>(packs/workflow/case-location/README.md135 行),去重; - 按 sha256(种子加编号) 排序取前 n 个;没给种子就随机取一个;
- 记录文件里每个编号带
found: null,由集成者人工查完填; - 屏幕上只打印总体数(需求、缺陷各几个)、抽了几个、种子和记录文件的 sha256。
- 从结果的
tally --record <文件>:打印找到、没找到、没填各几个和记录的 sha256;全部填了、而且都找到才退出 0。- 测试:同一种子同一样本;n 大于总体时全取;仓库里的路径被拒;已有的记录不覆盖;tally 的计数和退出码;屏幕输出里没有编号。
集成者之后跑的(终端不跑)
列表页和树原件由集成者用 circle-hub original export 导出到仓库外,分面视图照上一轮的取法存下。
- 每条要用的版本线:
node scripts/real/gold/builds.ts --page <FAMX 列表页> --page <FAMY 列表页> --line <版本线> --out <仓库外>/builds-<n>.json。打印一行计数;挑 FAMX、FAMY、别名都不是 0 的线。 - gold 至少 5 份文档、跨几条版本线和模块,每份的
buildCandidates是它那条线的输出:HUB_REAL_TOKEN=… node scripts/real/accept-circle.ts --scenario scripts/real/accept/case-location.ts --gold <仓库外>/location-gold.json --out <仓库外的目录>。核对行只有序号、字段和通过与否,要全部相符。 - 树原件导出后马上跑:
node scripts/real/accept/location-tree.ts --tree <仓库外>/agile-tree.json --facets <仓库外>/case-facets.json --out <仓库外的目录> --collect --token-file <仓库外的令牌文件>。打印计数、sha256 和判定。 node scripts/real/accept/id-spot-check.ts sample --results <第 3 步的目录> --count 10 --record <仓库外>/spot-check.json;到缺陷系统里逐个查,把found填成 true 或 false;再跑… tally --record <仓库外>/spot-check.json。
进度里只记这些计数和 sha256。
完成后在终端里回报:提交号、测试数量、场景报告和三个脚本在合成数据上打印出来的样子、没做完或拿不准的地方。
集成者对待定问题的答复(2026-10-23)
- 抽查的编号照页面写进仓库外的记录文件,屏幕上只有计数、种子和 sha256。
- 别的场景把真实值打到屏幕上的,记为后续待办,这一件不改。
- 全树核对的批大小默认 40,什么时候跑由集成者定。
- 独立答案和定位包的约定不同:不等的条目按原因分类报告(版本线节点取法、前缀代码、其他),不改定位包。定位包 README 里五个约定问题,由集成者转给所有者。
- 做:全树核对拿运行结果 data 里的全部候选,和
builds.ts逐字段比,builds.ts的输出带上版本线。 - 做:“bug to case”后面没分类的编号也抽。
- 覆盖检查要的版本线由集成者挑,挑最新 12 个构建里就有 FAMY 别名的。
- 做:
--facets报视图里缺的份数时按原因分开(没有用例节点、不是脑图、凭据只保留、原因不明),原因取自生产记录。