feat(skills): 两包资源索引机制与双向对账脚本

- product-planning:新建 references/manifest.md 作资源唯一登记处(资源/落点/引用点/口径);
  顶层 SKILL 加两条硬规矩,design-capture、stage-delivery、execution-runbook、stage-requirements
  四处把散落路径改为「落点见 references/manifest.md」
- session-mechanism:01-文档索引 新增 §四「包内资源索引」,并说明不放 manifest.md 的理由
  (那份由 install.py 本机生成、被 .gitignore 挡着)
- 新增两包共用的 scripts/check_resource_index.py(双向对账)与 product-planning/scripts/check_figures.py(图片机检),
  selftest 补对应用例,06-技能优化规程 写入写法规矩
- .gitignore:新增 _留痕/ 不入库(改造前整包快照,180K/688K,库内从无先例)
This commit is contained in:
admin committed 2026-10-18 18:07:40 +08:00
1 parent a9d2adaf72
commit acfb252bee
14 files changed
+626 -39

No files matched your search

+34 -19
View File
@@ -7920,14 +7920,16 @@ def t_term_table_single_source():
return rows
@case("🔴 06 的 16 条条目名:SKILL.md §0 ⑨ 与 06 正文/总表三方逐条同名(⛔ 写成简写=对不上)")
@case("🔴 06 条目名:SKILL.md §0 ⑨ 与 06 正文/总表三方逐条同名(⛔ 写成简写=对不上)")
def t_skill06_entries_same_source():
"""🔴 2026-10-15 立(当日实测踩到)。
"""🔴 2026-10-15 立(当日实测踩到);2026-10-16 改比对键。
`SKILL.md §0 ⑨` 是**匹配面**(点开技能就读到),`06` 正文与总表是**真源**。
实测:§0 把 C 层写成简写「职责不越界」,而正文是「步骤间职责不越界」——
两边都不报错,读者按简写去正文里搜就搜不到。
⇒ 判据落在**16 条逐条同名**上(数字 + 名字都要对上)。
⇒ 判据落在**条目名逐条同名**上。
🔴 条数**以 06 总表为准**(⛔ 不再硬编码),所以 06 增删条目时只改 06 与 §0 ⑨,
⛔ 不必回头改闸门 —— 硬编码过「应 16」,2026-10-16 实测因此漏过一整条(B#5)。
"""
import re as _re
SK = (HERE.parent / "SKILL.md").read_text(encoding="utf-8", errors="replace")
@@ -7937,28 +7939,41 @@ def t_skill06_entries_same_source():
i = SK.find("**⑨ 我要优化技能本身**")
j = SK.find("\n\n", i) if i >= 0 else -1
blk = SK[i:j] if i >= 0 and j > i else ""
a = {int(n): nm for n, nm in _re.findall(r"#(\d{1,2}) ([^(\s]+)", blk)}
b = {int(n): nm for n, nm in _re.findall(r"^(?:- )?\*\*(\d{1,2}) ([^(\s]+)\*\*", P6, _re.M)}
c = {int(n): nm for n, nm in _re.findall(
r"^\| (\d{1,2}) \| [A-D] [^|]*\| ([^|\s]+) \|", P6, _re.M)}
# 🔴 2026-10-16 改:**按条目名比,不按编号比**。
# 原写法把三处读成 `{编号: 名字}` 再逐号比对 ⇒ 编号一撞就静默丢条目:
# B 层与 C 层都用 `#5`(B#5 要求落在生成时看得见的面上/C#5 步骤顺序正确),
# 字典后写覆盖先写 ⇒ §0 ⑨ 与 06 正文都**看不到 B#5**,而 06 总表把它编成 17
# ⇒ 报成「首处不一致 #17」,真正缺的那一条反而不点名(2026-10-16 实测)。
# 名字才是判据的本意(原注释:读者按简写去正文里搜就搜不到),编号只是导航。
# 顺带把「应 16」这个硬编码去掉 ⇒ 以 **06 总表为准**,条数增减不必再改闸门。
a = [nm for _n, nm in _re.findall(r"#(\d{1,2}) ([^(\s]+)", blk)]
b = [nm for _n, nm in _re.findall(r"^(?:- )?\*\*(\d{1,2}) ([^(\s]+)\*\*", P6, _re.M)]
c = [nm for _n, nm in _re.findall(
r"^\| (\d{1,2}) \| [A-D] [^|]*\| ([^|\s]+) \|", P6, _re.M)]
# 🔴 句末标点不算差异(§0 是句子、正文是标题、「配置外置 /」后面跟分隔符)——
# 2026-10-15 实测:不清洗时 #5 会因「入仓按白名单。」多一个句号而报红,那是**假红**。
def _clean(d):
return {k: _re.sub(r"[。.,,、;;//]+$", "", v) for k, v in d.items()}
def _names(lst):
out = []
for v in lst:
v = _re.sub(r"[。.,,、;;//]+$", "", v)
if v and v not in out:
out.append(v)
return out
a, b, c = _clean(a), _clean(b), _clean(c)
A, B, C = _names(a), _names(b), _names(c)
rows = [("§0 ⑨ 读到条目名 %d 条(应 16)" % len(a), len(a) == 16),
("06 正文读到条目名 %d 条(应 16)" % len(b), len(b) == 16),
("06 总表读到条目名 %d 条(应 16)" % len(c), len(c) == 16)]
for tag, other in (("06 正文", b), ("06 总表", c)):
bad = sorted(k for k in set(a) | set(other) if a.get(k) != other.get(k))
rows = [("§0 ⑨ 读到条目名 %d 条(应 %d = 06 总表)" % (len(A), len(C)), len(A) == len(C)),
("06 正文读到条目名 %d 条(应 %d = 06 总表)" % (len(B), len(C)), len(B) == len(C))]
for tag, other in (("06 正文", B), ("06 总表", C)):
missing = [x for x in other if x not in A]
extra = [x for x in A if x not in other]
detail = ""
if bad:
k0 = bad[0]
detail = "(⛔ 首处不一致 #%d:§0=%r/%s=%r)" % (k0, a.get(k0), tag, other.get(k0))
rows.append(("§0 ⑨ 与 %s **16 条逐条同名**%s" % (tag, detail), not bad))
if missing:
detail = "(⛔ §0 ⑨ 缺:%s)" % "、".join(missing[:3])
elif extra:
detail = "(⛔ §0 ⑨ 多出:%s)" % "、".join(extra[:3])
rows.append(("§0 ⑨ 与 %s **逐条同名**%s" % (tag, detail), not missing and not extra))
return rows