chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
This commit is contained in:
@@ -35,7 +35,7 @@ CubeMX 重生成存活审计(check_cubemx_survival.py)
|
||||
注意
|
||||
----
|
||||
`MDK-ARM/*.uvprojx` **不参与漂移检测**(`no_baseline=True`):该文件每次
|
||||
`@build` 后被 Keil UV4 自动改写(XML 格式归一化 + `PackID` 降级到本机 DFP
|
||||
`/keil-build` 后被 Keil UV4 自动改写(XML 格式归一化 + `PackID` 降级到本机 DFP
|
||||
版本),内容每次构建都不同。若参与检测,则**每次构建后提交都会被拦**。
|
||||
本项只保留 `must_contain` 断言("源文件仍挂载")。
|
||||
"""
|
||||
@@ -242,7 +242,7 @@ CHECKS = [
|
||||
kind="generated",
|
||||
path="MDK-ARM/STM32F407-Demo.uvprojx",
|
||||
must_contain=["task_watchdog.c"],
|
||||
no_baseline=True, # 见下方说明:本文件每次 @build 后被 Keil 改写
|
||||
no_baseline=True, # 见下方说明:本文件每次 /keil-build 后被 Keil 改写
|
||||
hint="task_watchdog.c 须挂在 MDK 工程 App 组;CubeMX 重生成可能重建 .uvprojx。",
|
||||
),
|
||||
dict(
|
||||
@@ -490,7 +490,7 @@ def evaluate():
|
||||
|
||||
# ---- 基线快照 ----
|
||||
# `no_baseline=True` 的项不参与漂移检测。理由:`MDK-ARM/*.uvprojx`
|
||||
# 每次 `@build` 后被 Keil UV4 自动改写(XML 格式归一化 + PackID 降级到
|
||||
# 每次 `/keil-build` 后被 Keil UV4 自动改写(XML 格式归一化 + PackID 降级到
|
||||
# 本机 DFP 版本),内容每次构建都不同 → 若参与漂移检测,则**每次构建后
|
||||
# 提交都会被拦**。本项只保留 must_contain 断言("源文件仍挂载")。
|
||||
if not c.get("no_baseline"):
|
||||
|
||||
@@ -336,10 +336,10 @@ 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(" 先跑一次 /keil-build(keil-build skill)生成 .map,再跑本门禁。", file=sys.stderr)
|
||||
print(" 这不是代码缺陷:.map 是**每棵工作树各自的构建产物**(不入版本库),",
|
||||
file=sys.stderr)
|
||||
print(" 新开的工作树(task-worktree new)从未构建过时必然走到这里——先 @build",
|
||||
print(" 新开的工作树(task-worktree new)从未构建过时必然走到这里——先 /keil-build",
|
||||
file=sys.stderr)
|
||||
print(" 一次即可;随后按需要跑 scripts/ram_baseline.py --update 回填台账。",
|
||||
file=sys.stderr)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
要消除的盲区
|
||||
------------
|
||||
`mem-numbers` / `mem-budget` 两根闸的**事实源都是 Keil 链接产物 `.map`**
|
||||
(gitignore,只有真做过 `@build` 的机器上才有)。于是两处都是"没跑 = 通过":
|
||||
(gitignore,只有真做过 `/keil-build` 的机器上才有)。于是两处都是"没跑 = 通过":
|
||||
|
||||
· CI 侧:runner 没有 Keil,workflow 只能把这根钩子**跳过**,历史上只打一条
|
||||
`::warning::`(上一轮的处置)——"CI 全绿"仍不等于"体积守住了";
|
||||
@@ -34,7 +34,7 @@
|
||||
② 那次测量的 RAM 受管区是否 <=92%(用已提交的测量数字守红线)。
|
||||
2. 本门禁**不能**判:
|
||||
· CI 不重算绝对体积——runner 无 Keil/无 `.map`,绝对数字的可信度来自本地那次
|
||||
`@build`;本脚本把盖戳卡在"新鲜 .map + 未超线"之后,凭证不是随手可写的。
|
||||
`/keil-build`;本脚本把盖戳卡在"新鲜 .map + 未超线"之后,凭证不是随手可写的。
|
||||
· **编译器选项不在指纹范围内**:`MDK-ARM/STM32F407-Demo.uvprojx` 每次构建都被 UV4
|
||||
改写且按约定不入库(keil-build skill),把它放进指纹会让门禁在"刚刚构建完"
|
||||
的那一刻自己变红;代价是改优化等级/预处理宏不触发本门禁,这类改动必须人工
|
||||
@@ -257,7 +257,7 @@ def cmd_stamp(args):
|
||||
"""用新鲜 .map 盖同码戳;三道前置任一不过即拒绝(见模块 docstring)。"""
|
||||
map_path = pathlib.Path(args.map)
|
||||
if not map_path.exists():
|
||||
print("[FAIL] 没有 .map(%s)——先 @build 再盖戳" % map_path, file=sys.stderr)
|
||||
print("[FAIL] 没有 .map(%s)——先 /keil-build 再盖戳" % map_path, file=sys.stderr)
|
||||
return 1
|
||||
m = measure(map_path)
|
||||
if m is None:
|
||||
@@ -266,7 +266,7 @@ def cmd_stamp(args):
|
||||
map_mt = map_path.stat().st_mtime
|
||||
src_mt = newest_source_mtime()
|
||||
if src_mt > map_mt:
|
||||
print("[FAIL] .map 早于最新受管源码——这份构建不含最新改动,重新 @build",
|
||||
print("[FAIL] .map 早于最新受管源码——这份构建不含最新改动,重新 /keil-build",
|
||||
file=sys.stderr)
|
||||
print(" .map mtime=%d 最新源码 mtime=%d" % (int(map_mt), int(src_mt)),
|
||||
file=sys.stderr)
|
||||
@@ -290,7 +290,7 @@ def cmd_stamp(args):
|
||||
file=sys.stderr)
|
||||
print(" 修复:先 git add <这些文件>(凭证描述的就是你正要提交的那份),再盖戳;",
|
||||
file=sys.stderr)
|
||||
print(" 或把它们还原。@build 与盖戳都应在同一份内容上做。")
|
||||
print(" 或把它们还原。/keil-build 与盖戳都应在同一份内容上做。")
|
||||
return 1
|
||||
fp, n = tree_fingerprint()
|
||||
write_attestation({
|
||||
@@ -339,7 +339,7 @@ def _cmd_parity(args, ci_mode):
|
||||
if doc is None:
|
||||
return _report(args, ci_mode, [
|
||||
"体积同码凭证不可用:" + err,
|
||||
" 修复:本地 @build 之后跑 python scripts/ram_baseline.py --update",
|
||||
" 修复:本地 /keil-build 之后跑 python scripts/ram_baseline.py --update",
|
||||
" (或单独跑 python scripts/check_size_gate.py --stamp)",
|
||||
])
|
||||
fp, n = tree_fingerprint()
|
||||
@@ -350,14 +350,14 @@ def _cmd_parity(args, ci_mode):
|
||||
"指纹范围内有 %d 处**未 staged** 的改动/未跟踪文件,凭证覆盖不到它们" % dirt,
|
||||
" " + "、".join(samples) + ("…" if dirt > len(samples) else ""),
|
||||
" 含义:这些内容既没进索引(凭证按索引生成),也就没被那次测量算进去",
|
||||
" 修复:git add 之后 @build,再跑 python scripts/ram_baseline.py --update",
|
||||
" 修复:git add 之后 /keil-build,再跑 python scripts/ram_baseline.py --update",
|
||||
]
|
||||
if fp != doc["fingerprint"]:
|
||||
problems += [
|
||||
"本次代码(指纹 %s…,%d 文件)与体积测量(指纹 %s…,%s)不同码" % (
|
||||
fp[:12], n, str(doc["fingerprint"])[:12], doc.get("built_at", "?")),
|
||||
" 含义:这份源码没有被真实链接测量过(或测量完又改了代码/合入了别的改动)",
|
||||
" 修复:@build 0/0 之后跑 python scripts/ram_baseline.py --update 再提交",
|
||||
" 修复:/keil-build 0/0 之后跑 python scripts/ram_baseline.py --update 再提交",
|
||||
" (指纹取 git 索引 blob:同一份内容在任何工作树/runner 上算法一致,"
|
||||
"不受 CRLF/LF 检出差异影响)",
|
||||
]
|
||||
@@ -441,7 +441,7 @@ def main(argv=None):
|
||||
ap = argparse.ArgumentParser(
|
||||
description="体积门禁强制路径(同码凭证 + CI 红线复核)")
|
||||
g = ap.add_mutually_exclusive_group()
|
||||
g.add_argument("--stamp", action="store_true", help="用新鲜 .map 盖同码戳(@build 后)")
|
||||
g.add_argument("--stamp", action="store_true", help="用新鲜 .map 盖同码戳(/keil-build 后)")
|
||||
g.add_argument("--local", action="store_true", help="本地/ci_local 自查:工作树须与测量同码")
|
||||
# ⚠ help 字符串里 % 必须写成 %%(argparse 会对 help 做 %-插值;裸 % + 中文
|
||||
# 如"92% 红"在**无参/非法参的 print_help 路径**上才炸:ValueError
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@
|
||||
message 喂给钩子。
|
||||
|
||||
刻意**不含**(与 CI 同理由,见 workflow 文件头):
|
||||
- `@build`(需 Keil 授权;CI 也不跑,真编译是开发者本地红线)
|
||||
- `/keil-build`(需 Keil 授权;CI 也不跑,真编译是开发者本地红线)
|
||||
- `mem-numbers` / `mem-budget` 钩子(事实源 `.map` 是 Keil 产物——**本地若存在 .map
|
||||
就照常跑**,与 CI 的自适应逻辑一致)。⚠ 这两根闸在"无 .map"时是 **SKIP 而不是
|
||||
PASS**(等于这一趟什么都没查);那个缺口由上面的体积门禁(size)补住:同码凭证
|
||||
@@ -508,7 +508,7 @@ def run_static_gates():
|
||||
else:
|
||||
print("[INFO] 未找到 .map(未做 Keil 构建)-> 跳过 mem-numbers / "
|
||||
"mem-budget(与 CI 一致)")
|
||||
print("[INFO] :warning: 体积门禁未执行——改代码后请本地 @build + "
|
||||
print("[INFO] :warning: 体积门禁未执行——改代码后请本地 /keil-build + "
|
||||
"check_mem_numbers.py --scan + ram_baseline.py --gate 兜底")
|
||||
skip = env.get("SKIP", "")
|
||||
add = "mem-numbers,mem-budget"
|
||||
@@ -655,7 +655,7 @@ def run_size_gate():
|
||||
return 2
|
||||
rc = _run([PY, str(script), "--local"])
|
||||
if rc == 1:
|
||||
print(" (1 = 没有凭证,或代码与测量不同码:@build 0/0 之后跑 "
|
||||
print(" (1 = 没有凭证,或代码与测量不同码:/keil-build 0/0 之后跑 "
|
||||
"python scripts/ram_baseline.py --update)")
|
||||
print("[SUMMARY] size-gate: rc={}".format(rc))
|
||||
return rc
|
||||
|
||||
@@ -10,7 +10,7 @@ RAM 预算门禁 + 趋势台账(登记簿条目「缩堆迁 CCM 后半(暂
|
||||
无 `.map`(未做 Keil 构建)→ 自适应跳过(与 `mem-numbers` 同口径,
|
||||
CI 盲区口径见 `scripts/check_size_gate.py` 的 docstring)。
|
||||
`--gate` 同时校验台账受管区与 `.map` 一致(防漂移,同 `mem-numbers` 思路)。
|
||||
2. **趋势基线** `scripts/ram_baseline.tsv`(`--update` 每次 @build 后追加一行;
|
||||
2. **趋势基线** `scripts/ram_baseline.tsv`(`--update` 每次 /keil-build 后追加一行;
|
||||
`--trend` 看趋势并高亮 RW+ZI 较上一行增长 >1 KB 的对象——方案 §4.0.3
|
||||
"ZI 增量 >1KB 必须解释"的机械化)。
|
||||
3. **台账受管区**(`--write` 同步 `docs/内存管理/RAM预算台账.md` 的
|
||||
@@ -136,7 +136,7 @@ def warn_unattributed(info):
|
||||
"「应用」(台账 §2:应用 = App/ 其余),合计 %d B:" % (len(missing), total))
|
||||
for obj, nbytes in missing:
|
||||
print(" %-28s %6d B" % (_obj_base(obj), nbytes))
|
||||
print(" 修法:补 subsystem_of() 前缀 -> 重跑 @build -> --update / --write 回填台账")
|
||||
print(" 修法:补 subsystem_of() 前缀 -> 重跑 /keil-build -> --update / --write 回填台账")
|
||||
return len(missing)
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ def cmd_gate(args):
|
||||
print(" 强制兜底(无需 Keil,改代码后必然拦红):")
|
||||
print(" 本地 scripts/ci_local.py 的体积门禁步 / CI 的「体积门禁」步,")
|
||||
print(" 都跑 scripts/check_size_gate.py(同码凭证 + 92% 红线复核)。")
|
||||
print(" 改过代码请先 @build,再 python scripts/ram_baseline.py --update。")
|
||||
print(" 改过代码请先 /keil-build,再 python scripts/ram_baseline.py --update。")
|
||||
return 0
|
||||
info = collect(map_path)
|
||||
if not info["regions"]:
|
||||
@@ -284,13 +284,13 @@ def cmd_gate(args):
|
||||
ir = info["regions"].get("RW_IRAM1")
|
||||
if rows and ir and int(rows[-1]["iram1"]) != ir["size"]:
|
||||
print("[WARN] ram_baseline.tsv 末行落后于当前 .map —— "
|
||||
"@build 后请跑 `python scripts/ram_baseline.py --update`")
|
||||
"/keil-build 后请跑 `python scripts/ram_baseline.py --update`")
|
||||
else:
|
||||
print("[WARN] ram_baseline.tsv 不存在 —— 跑 "
|
||||
"`python scripts/ram_baseline.py --update` 建档")
|
||||
newest = _newest_source_mtime()
|
||||
if newest and newest > map_path.stat().st_mtime:
|
||||
print("[WARN] .map 早于最新源码——数字来自旧构建,建议先 @build 再提交")
|
||||
print("[WARN] .map 早于最新源码——数字来自旧构建,建议先 /keil-build 再提交")
|
||||
|
||||
warn_unattributed(info)
|
||||
|
||||
@@ -303,7 +303,7 @@ def cmd_gate(args):
|
||||
def cmd_write(args):
|
||||
map_path = _map_or_skip(args)
|
||||
if map_path is None:
|
||||
print("[FAIL] 找不到 .map 事实源,先 @build(.map 是每棵工作树各自的构建产物,不入版本库;",
|
||||
print("[FAIL] 找不到 .map 事实源,先 /keil-build(.map 是每棵工作树各自的构建产物,不入版本库;",
|
||||
file=sys.stderr)
|
||||
print(" 新开的 task-worktree 工作树从未构建过时必然走到这里,不是代码缺陷)",
|
||||
file=sys.stderr)
|
||||
@@ -352,7 +352,7 @@ def _top_field(top):
|
||||
def cmd_update(args):
|
||||
map_path = _map_or_skip(args)
|
||||
if map_path is None:
|
||||
print("[FAIL] 找不到 .map 事实源,先 @build(.map 是每棵工作树各自的构建产物,不入版本库;",
|
||||
print("[FAIL] 找不到 .map 事实源,先 /keil-build(.map 是每棵工作树各自的构建产物,不入版本库;",
|
||||
file=sys.stderr)
|
||||
print(" 新开的 task-worktree 工作树从未构建过时必然走到这里,不是代码缺陷)",
|
||||
file=sys.stderr)
|
||||
@@ -388,7 +388,7 @@ def cmd_update(args):
|
||||
f"({region_pct(reg['RW_CCM']):.1f}%) → {TSV_PATH.relative_to(REPO_ROOT)}")
|
||||
|
||||
# 强制路径(2026-10-25):同一次「真实测量」顺带刷新**体积同码凭证**
|
||||
# scripts/size_attestation.json——这样「每次 @build 后跑 --update」这条**既有
|
||||
# scripts/size_attestation.json——这样「每次 /keil-build 后跑 --update」这条**既有
|
||||
# 纪律**同时喂饱那条无需 Keil 也能判的强制门禁(check_size_gate --ci /
|
||||
# --local,即 ci_local 的体积门禁步与 CI 的体积门禁步),不新增人工步骤。
|
||||
# 盖戳失败只告警、不改本命令返回值:凭证新鲜度由门禁自己 fail-closed 兜,
|
||||
@@ -471,7 +471,7 @@ def main(argv=None):
|
||||
g = ap.add_mutually_exclusive_group()
|
||||
g.add_argument("--gate", action="store_true", help="92% 阈值门禁(pre-commit 入口)")
|
||||
g.add_argument("--write", action="store_true", help="同步台账受管区")
|
||||
g.add_argument("--update", action="store_true", help="追加 tsv 趋势行(@build 后跑)")
|
||||
g.add_argument("--update", action="store_true", help="追加 tsv 趋势行(/keil-build 后跑)")
|
||||
g.add_argument("--trend", action="store_true", help="打印趋势 + 高亮 >1 KB 增长")
|
||||
ap.add_argument("-n", type=int, default=10, help="--trend 显示行数")
|
||||
ap.add_argument("--map", default=str(MAP_DEFAULT), help=".map 路径")
|
||||
|
||||
@@ -240,7 +240,7 @@ def main():
|
||||
"已落盘,请接着登记注册表族并重跑 scripts/check_ids.py"
|
||||
if args.apply else "干跑结束,确认后加 --apply"))
|
||||
print("提醒:改名后必跑 check_ids.py + check_doc_links.py --all + ci_local.py;"
|
||||
"改到 test/board 或 Drivers/ 还要 @build。")
|
||||
"改到 test/board 或 Drivers/ 还要 /keil-build。")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -468,7 +468,7 @@ def main(argv=None):
|
||||
print("ℹ 引用关系图是生成物,未改写 → 重跑 & 'tools\\refgraph\\gen_refgraph.bat'")
|
||||
if old_abs.suffix.lower() in (".c", ".h") or new_abs.suffix.lower() in (".c", ".h"):
|
||||
print("ℹ 改了 .c/.h:已同步 uvprojx 与 CMakeLists;若**新增目录**还需把目录加进 "
|
||||
"uvprojx 的 <IncludePath>(见 new-source-file skill),随后跑 @build 验证")
|
||||
"uvprojx 的 <IncludePath>(见 new-source-file skill),随后跑 /keil-build 验证")
|
||||
print("ℹ 正文里的**裸文件名/昵称引用**(如「内存方案 §2.1」「app_main.c/h」)无法机械判定,"
|
||||
"已尽量改写,仍建议 grep 一遍旧名:git grep -n '<旧文件名>'")
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
真停滞嫌疑段,见下。
|
||||
|
||||
输入契约(serial-assistant 落盘日志 = 纯文本完整行;两种时钟源自动择一):
|
||||
- 首选采集相对钟:行首 [+NNNms](@test/采集管线相对时戳;相对钟回退 >5s、或出现
|
||||
- 首选采集相对钟:行首 [+NNNms](串口回归/采集管线相对时戳;相对钟回退 >5s、或出现
|
||||
reset cause 锚点行 = 新会话边界——烧录/复位/clear 都会造成不连续);
|
||||
- 无相对钟时退化为设备绝对钟:行内 [YYYY-MM-DD HH:MM:SS](墙钟未就绪的
|
||||
[----/--/-- --:--:--] 不算时戳;RTC 同步类向前跳变按会话边界处理、不计静默;
|
||||
|
||||
Reference in New Issue
Block a user