fix(门禁脚本): 缺 .map 时说明"本树从未构建",并写进工作树文档

票面:task-20 交付后的回报——全新工作树没有 .map,依赖它的门禁必然红,而现场只看到一句
"找不到 .map 事实源",容易误判成代码问题。(Lead 定的两头修法都做,本笔独立于 UI 改动。)

- scripts/check_mem_numbers.py:FAIL 后补三行原因与动作——.map 是**每棵工作树各自的**构建产物、
  不入版本库,新开的工作树(task-worktree new)从未构建过时必然走到这里,先 @build 一次即可,
  再按需要 ram_baseline.py --update 回填台账。保持 FAIL 语义不变,只把提示词写对。
- scripts/ram_baseline.py:两处(--update / --write 路径)同样的补充说明。
- docs/环境与部署/并行任务工作树方案.md:生命周期表加一行「新空间首次:@build 一次生成 .map」,
  表下补一段说明点名 mem-numbers / mem-budget / L0c 三个消费者。
- .dsh/skills/task-worktree/SKILL.md:陷阱区加同一条,并与既有的「done 的门禁跑在 @build 之前
  → .map 是旧的」明确区分——一条是"有但旧",一条是"根本没有",处置动作不同。

只改提示文案与文档,不改判定逻辑与阈值;两个脚本 py_compile 通过并实测打印。
跳过的门禁(三条同族):mem-numbers / mem-budget / size-gate(L0c)——本笔恰恰是在解释其中两条
为什么会红,数值本身仍等 Lead 回填。
This commit is contained in:
2026-10-25 15:33:45 +08:00
parent 3dc7f4a088
commit a8de47ca3f
4 changed files with 27 additions and 3 deletions
+5
View File
@@ -87,6 +87,11 @@ whenToUse: 要同时推进多个任务、担心同分支同文档互相冲突;
**处置**:门禁失败且只差这两项时,按提示在主工作树跑
`python scripts/ram_baseline.py --write` + `python scripts/check_mem_numbers.py --write`
后追加提交,再重跑 `done`(它会检测到「已合入」直接清场)。
- **⚠ 新建工作树从未构建过 → 体积类门禁必然红**(2026-10-25 实测):`.map` 是每棵树各自的
构建产物、不入版本库,所以 `new` 出来的空间里 `mem-numbers` / `mem-budget` / `ci_local.py`
的 L0c 都拿不到事实源,报 `[FAIL] 找不到 .map 事实源`(脚本提示已写明原因,不是代码缺陷)。
**处置**:新空间第一次提交前先 `@build` 一次(`build` 锁自动排队,与别的树串行)。与上一条
的区别要分清:上一条是「有 `.map` 但是旧的」,本条是「根本没有 `.map`」,处置动作不同。
- **⚠ 增量构建可能不重链 → `.map` 数字瞬态不一致**(2026-10-12 实测):
`@build` 若判定无需重链,`.map` 保持旧值,此时 `--write` 生成的快照是**瞬态值**
(实测同一份源码两次读到 223,560 与 223,724,差 164 B)。**以主工作树(集成点)
@@ -270,10 +270,17 @@ D:/Code/DTU Code/
| ------- | ------------------------------------ | ------------------------------ |
| 任务期 | `scripts/ci_local.py`(L0 + L0.5,可并行) | 每个任务各自跑 |
| 任务期 | `@build`(0 错 0 警,**持 `build` 锁串行**) | 每个任务各自跑 |
| **新空间首次** | `@build` 一次,生成 `.map` | 见下表下注释:不先构建,体积类门禁必然红 |
| 任务期(可选) | `@test` / 板级套件(持 `bench` 锁) | 需真机验证的任务 |
| 回收期 | 重生成产物 → `ci_local.py` → `@build` | `done` 内强制(`--skip-gates` 可跳过) |
| 人工 | push 许可 | 回收后 |
> ⚠ **新开工作树后先 `@build` 一次再谈门禁**(实测 2026-10-25):`.map` 是每棵树各自的构建产物、
> 不入版本库,所以依赖它的事实源的门禁——`mem-numbers`、`mem-budget`、以及 `ci_local.py` 的
> `L0c`——在从未构建过的新空间里**必然红**。这不是代码缺陷,脚本此时也会把这句原因打出来
> (`check_mem_numbers.py` / `ram_baseline.py` 的提示文案)。顺带一条同源提醒:`done` 的门禁
> 排在 `@build` 之前(见下文陷阱条目),同一族误报在回收期也会重现一次。
**`done` 的完成判据**:主干上 `git merge --no-ff` 成功 + `ci_local.py` 全绿 + `@build` 0 错 0 警
- 残留审计无死规则/重复条目 + 工作树已移除。
+7 -1
View File
@@ -336,7 +336,13 @@ def main(argv):
map_path = pathlib.Path(args.map)
if not map_path.exists():
print(f"[FAIL] 找不到 .map 事实源:{map_path}", file=sys.stderr)
print(" 先跑一次 @build(keil-build skill)生成 .map。", file=sys.stderr)
print(" 先跑一次 @build(keil-build skill)生成 .map,再跑本门禁。", file=sys.stderr)
print(" 这不是代码缺陷:.map 是**每棵工作树各自的构建产物**(不入版本库),",
file=sys.stderr)
print(" 新开的工作树(task-worktree new)从未构建过时必然走到这里——先 @build",
file=sys.stderr)
print(" 一次即可;随后按需要跑 scripts/ram_baseline.py --update 回填台账。",
file=sys.stderr)
return 1
data = parse_map(map_path)
+8 -2
View File
@@ -247,7 +247,10 @@ def cmd_gate(args):
def cmd_write(args):
map_path = _map_or_skip(args)
if map_path is None:
print("[FAIL] 找不到 .map 事实源,先 @build", file=sys.stderr)
print("[FAIL] 找不到 .map 事实源,先 @build(.map 是每棵工作树各自的构建产物,不入版本库;",
file=sys.stderr)
print(" 新开的 task-worktree 工作树从未构建过时必然走到这里,不是代码缺陷)",
file=sys.stderr)
return 1
info = collect(map_path)
if not LEDGER.exists():
@@ -293,7 +296,10 @@ def _top_field(top):
def cmd_update(args):
map_path = _map_or_skip(args)
if map_path is None:
print("[FAIL] 找不到 .map 事实源,先 @build", file=sys.stderr)
print("[FAIL] 找不到 .map 事实源,先 @build(.map 是每棵工作树各自的构建产物,不入版本库;",
file=sys.stderr)
print(" 新开的 task-worktree 工作树从未构建过时必然走到这里,不是代码缺陷)",
file=sys.stderr)
return 1
info = collect(map_path)
gt = ram_report.parse_map(str(map_path))["grand_totals"]