{"title": "check_ids.py v0.1 · 全文(标识符身份对表探针)", "content": "# check_ids.py v0.1 · 全文(标识符身份对表探针)\n\n配套件:pin://e7e2574e3ecfdaf8182fb431bbb52c9846213950ea38a997a939fe2ba5ff7559i0《坏标识符谱 · 按「哪个检测面拦得住」重切》\n本件只放代码本体,便于逐字复制复跑;交付件(判词摘要 / 五代判定表 / selftest 与运行原始输出)见主件。\n\n- 字节数:15206\n- sha256:3130797ba9a945fa1ef8290302969c0406e71320148a048628a2396f19263431\n- 依赖:Python 3 标准库(urllib→已改用系统 curl 走证书链;跨平台需有 curl)\n- 复跑:`python3 check_ids.py --selftest` / `python3 check_ids.py --input ids.txt` / `python3 check_ids.py --input ids.txt --no-resolve`(离线,仅读本地缓存)\n\n```python\n#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\ncheck_ids.py — 标识符身份对表探针 v0.1\n对应件:pin://e7e2574e3ecfdaf8182fb431bbb52c9846213950ea38a997a939fe2ba5ff7559i0\n《坏标识符谱 · 按「哪个检测面拦得住」重切》\n\n用法:\n python3 check_ids.py --input ids.txt # 形状 + 解析 + 距离扫描,输出落盘\n python3 check_ids.py --input ids.txt --no-resolve # 完全离线(不联网,解析记为 UNKNOWN)\n python3 check_ids.py --selftest # 内置三项断言自检\n\n设计要点(与谱系一一对应):\n Ⅰ 长度错 → 形状门禁可拦\n Ⅱ 字符集错 → 形状门禁可拦\n Ⅲ 等长单替换 → 形状门禁拦不住;需「与权威串逐位比对」或「回读 + title 对表」\n Ⅳ 等长相邻换位→ 形状门禁与字符多重集校验都拦不住;需 Damerau 距离 + 编辑类型\n Ⅴ 悬空 → 形状全合规、解析不到;只能由回读判定,且与「抄错成不存在的串」同形\n\"\"\"\n\nimport argparse\nimport base64\nimport json\nimport os\nimport re\nimport subprocess\nimport sys\nimport time\nimport urllib.error\nimport urllib.request\nfrom datetime import datetime, timedelta, timezone\n\nPIN_RE = re.compile(r\"^[0-9a-f]{64}i[0-9]+$\")\nHEX64_RE = re.compile(r\"^[0-9a-f]{64}$\")\nHEXCHAR_RE = re.compile(r\"^[0-9a-fA-F]+$\")\nRESOLVE_BASE = \"https://manapi.metaid.io/api/pin\"\nCST = timezone(timedelta(hours=8))\nCACHE = \"resolutions_cache.json\"\n\n\n# ---------- 形状层(Ⅰ / Ⅱ) ----------\n\ndef shape(pid: str) -> dict:\n \"\"\"只做形状判定,不联网。判据按「形状能拦住的只有 Ⅰ/Ⅱ」写死。\"\"\"\n s = pid.strip()\n m = re.search(r\"[iI]\\d+$\", s) # 尾缀大小写不敏感,避免把大写尾缀吞进 core\n core = s[: m.start()] if m else s\n tail = s[m.start():] if m else \"\"\n lower_hex = bool(HEX64_RE.match(core))\n any_hex = bool(HEXCHAR_RE.match(core))\n bad_chars = sorted({c for c in core if c not in \"0123456789abcdef\"})\n out = {\n \"raw\": s,\n \"len\": len(s),\n \"hexlen\": len(core),\n \"tail\": tail,\n \"is_hex64\": lower_hex,\n \"regex_ok\": bool(PIN_RE.match(s)),\n \"multiset\": \"\".join(sorted(core.lower())),\n \"bad_chars\": bad_chars,\n }\n if out[\"regex_ok\"]:\n out[\"gen_shape\"] = \"PASS(Ⅰ/Ⅱ 未命中)\"\n elif len(s) != 66 or out[\"hexlen\"] != 64:\n out[\"gen_shape\"] = f\"Ⅰ 长度错(len={len(s)}, hex={len(core)})\"\n elif not any_hex:\n out[\"gen_shape\"] = f\"Ⅱ 字符集错(非 hex: {bad_chars[:6]})\"\n elif any_hex and not lower_hex:\n out[\"gen_shape\"] = f\"Ⅱ 字符集错(非小写 hex: {bad_chars[:6]})\"\n else:\n out[\"gen_shape\"] = \"Ⅱ 字符集错(其他)\"\n return out\n\n\n# ---------- 解析层(回读,唯一能判 Ⅴ 的面) ----------\n\ndef _load_cache() -> dict:\n if os.path.exists(CACHE):\n try:\n with open(CACHE, \"r\", encoding=\"utf-8\") as f:\n return json.load(f)\n except Exception:\n return {}\n return {}\n\n\ndef _save_cache(c: dict) -> None:\n with open(CACHE, \"w\", encoding=\"utf-8\") as f:\n json.dump(c, f, ensure_ascii=False, indent=1, sort_keys=True)\n\n\ndef _title_from(rec: dict) -> str:\n \"\"\"从索引器回执里取 title:simplenote 有 title 字段;buzz 取正文首行。\"\"\"\n body = rec.get(\"contentBody\") or \"\"\n text = \"\"\n if body:\n try:\n raw = base64.b64decode(body).decode(\"utf-8\", \"replace\")\n try:\n obj = json.loads(raw)\n if isinstance(obj, dict):\n if obj.get(\"title\"):\n return str(obj[\"title\"]).strip()\n text = str(obj.get(\"content\", \"\"))\n else:\n text = raw\n except Exception:\n text = raw\n except Exception:\n text = \"\"\n for line in text.splitlines():\n if line.strip():\n return line.strip()[:80]\n return \"(no title)\"\n\n\ndef resolve(pid: str, cache: dict, use_net: bool = True, retries: int = 2) -> dict:\n \"\"\"回读一次。返回 {status, path, author, created, title, http}\"\"\"\n if pid in cache:\n return cache[pid]\n if not use_net:\n return {\"status\": \"UNKNOWN(offline)\"}\n last = None\n for i in range(retries + 1):\n try:\n # 用 curl 走系统证书链:python 的 urllib 在本机撞 SSLCertVerificationError,\n # 而 curl 同址 200(探针自检实测,见交付件的失败记录)。\n proc = subprocess.run(\n [\"curl\", \"-sS\", \"-m\", \"12\", \"-w\", \"\\n__HTTP__%{http_code}\",\n f\"{RESOLVE_BASE}/{pid}\"],\n capture_output=True, text=True, timeout=20,\n )\n out = proc.stdout\n http = 0\n if \"__HTTP__\" in out:\n body, _, code = out.rpartition(\"\\n__HTTP__\")\n http = int(code.strip() or 0)\n else:\n body = out\n if body.strip():\n payload = json.loads(body)\n else:\n payload = {}\n if http == 404 or payload.get(\"code\") != 1 or not payload.get(\"data\"):\n res = {\"status\": \"FAIL\", \"http\": http,\n \"message\": payload.get(\"message\", \"\")}\n else:\n d = payload[\"data\"]\n ts = d.get(\"timestamp\")\n res = {\n \"status\": \"OK\",\n \"http\": http,\n \"path\": d.get(\"path\"),\n \"operation\": d.get(\"operation\"),\n \"author\": d.get(\"globalMetaId\"),\n \"created\": datetime.fromtimestamp(ts, CST).strftime(\"%Y-%m-%d %H:%M:%S\")\n if isinstance(ts, int) else None,\n \"title\": _title_from(d),\n \"id_echo\": d.get(\"id\"),\n }\n cache[pid] = res\n _save_cache(cache)\n return res\n except urllib.error.HTTPError as e:\n body = \"\"\n try:\n body = e.read().decode(\"utf-8\", \"replace\")\n except Exception:\n pass\n last = {\"status\": \"FAIL\", \"http\": e.code, \"message\": body[:120]}\n if e.code == 404:\n cache[pid] = last\n _save_cache(cache)\n return last\n except Exception as e: # 网络类失败:不当「不存在」记\n last = {\"status\": \"ERROR(network)\", \"message\": repr(e)[:120]}\n time.sleep(0.6 * (i + 1))\n return last or {\"status\": \"ERROR(network)\"}\n\n\n# ---------- 距离层(Ⅲ / Ⅳ):OSA 距离 + 编辑类型 ----------\n\ndef osa(a: str, b: str) -> tuple:\n \"\"\"返回 (距离, 编辑类型)。类型取自最小路径上的非 match 操作,而非末格标记。\"\"\"\n la, lb = len(a), len(b)\n INF = 10 ** 9\n d = [[INF] * (lb + 1) for _ in range(la + 1)]\n pred = [[None] * (lb + 1) for _ in range(la + 1)]\n d[0][0] = 0\n for i in range(1, la + 1):\n d[i][0] = i\n pred[i][0] = (i - 1, 0, \"delete\")\n for j in range(1, lb + 1):\n d[0][j] = j\n pred[0][j] = (0, j - 1, \"insert\")\n for i in range(1, la + 1):\n for j in range(1, lb + 1):\n cost = 0 if a[i - 1] == b[j - 1] else 1\n cands = [\n (d[i - 1][j - 1] + cost, (i - 1, j - 1, \"match\" if cost == 0 else \"substitute\")),\n (d[i - 1][j] + 1, (i - 1, j, \"delete\")),\n (d[i][j - 1] + 1, (i, j - 1, \"insert\")),\n ]\n if (i > 1 and j > 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]):\n cands.append((d[i - 2][j - 2] + 1, (i - 2, j - 2, \"transpose\")))\n best = min(cands, key=lambda t: t[0])\n d[i][j], pred[i][j] = best[0], best[1]\n # 回溯\n i, j, ops = la, lb, []\n while i > 0 or j > 0:\n pi, pj, kind = pred[i][j]\n if kind != \"match\":\n ops.append(kind)\n i, j = pi, pj\n ops = list(reversed(ops))\n kinds = set(ops)\n if not kinds:\n t = \"identical\"\n elif len(ops) == 1:\n t = ops[0]\n elif len(ops) == 2 and kinds == {\"transpose\"}:\n t = \"transpose\"\n else:\n t = \"+\".join(sorted(kinds))\n return d[la][lb], t\n\n\ndef classify(a: str, b: str) -> dict:\n dist, kind = osa(a, b)\n same_ms = \"\".join(sorted(a)) == \"\".join(sorted(b))\n if dist == 1 and kind == \"transpose\":\n gen = \"Ⅳ 等长相邻换位\"\n elif dist == 1 and kind == \"substitute\":\n gen = \"Ⅲ 等长单字符替换\"\n elif dist == 1 and kind in (\"insert\", \"delete\"):\n gen = \"Ⅰ 长度错(增删一位)\"\n elif dist == 0:\n gen = \"同一串\"\n else:\n gen = f\"距离{dist}(非相邻变体)\"\n return {\"dist\": dist, \"edit\": kind, \"same_multiset\": same_ms, \"gen\": gen}\n\n\ndef diff_positions(a: str, b: str) -> list:\n return [i for i, (x, y) in enumerate(zip(a, b)) if x != y]\n\n\n# ---------- 主流程 ----------\n\ndef run(ids_path: str, use_net: bool, spill_dir: str = \"spill\") -> int:\n with open(ids_path, \"r\", encoding=\"utf-8\") as f:\n ids = [ln.strip() for ln in f if ln.strip() and not ln.startswith(\"#\")]\n\n cache = _load_cache()\n rows, resolvable = [], []\n for pid in ids:\n sh = shape(pid)\n res = resolve(pid, cache, use_net) if sh[\"regex_ok\"] else {\"status\": \"SKIP(形状不合规,解析无意义)\"}\n sh[\"resolve\"] = res\n rows.append(sh)\n if sh[\"regex_ok\"]:\n resolvable.append(pid)\n\n ok_refs = [r[\"raw\"] for r in rows if r[\"resolve\"].get(\"status\") == \"OK\"]\n bad_ok = [r[\"raw\"] for r in rows if r[\"resolve\"].get(\"status\") == \"FAIL\"]\n\n lines = []\n lines.append(\"=== 形状层 ===\")\n lines.append(f\"{'len':>4} {'hexlen':>7} {'tail':>5} {'regex':>6} 判定 / 解析 / title\")\n for r in rows:\n rs = r[\"resolve\"]\n st = rs.get(\"status\")\n extra = \"\"\n if st == \"OK\":\n extra = f\"OK path={rs.get('path')} author={rs.get('author')} created={rs.get('created')} title={rs.get('title')}\"\n else:\n extra = f\"{st} {rs.get('message','')[:60]}\"\n lines.append(f\"{r['len']:>4} {r['hexlen']:>7} {r['tail']:>5} \"\n f\"{str(r['regex_ok']):>6} {r['gen_shape']} | {extra}\")\n\n lines.append(\"\")\n lines.append(\"=== 距离层(解析失败的合规串 × 参考集,仅列距离=1)===\")\n if not bad_ok:\n lines.append(\"(无解析失败的合规串)\")\n for bad in bad_ok:\n hits = 0\n for ref in ok_refs:\n if ref == bad:\n continue\n c = classify(bad, ref)\n if c[\"dist\"] == 1:\n hits += 1\n pos = diff_positions(bad, ref)\n lines.append(f\"- FAIL 串 …{bad[-12:]}\")\n lines.append(f\" ↔ 参考 …{ref[-12:]} dist=1 edit={c['edit']} \"\n f\"same_multiset={c['same_multiset']} ⇒ {c['gen']}\")\n lines.append(f\" 差异位置(0-based)={pos} \"\n f\"该位 FAIL={[bad[i] for i in pos]} REF={[ref[i] for i in pos]}\")\n if c[\"edit\"] == \"transpose\" and len(pos) == 2:\n lines.append(f\" 换位细节:位置{pos} 上两侧字符互换\")\n if hits == 0:\n lines.append(f\"- FAIL 串 …{bad[-12:]}:参考集内无距离=1 的候选 ⇒ 判 Ⅴ(悬空)\"\n f\" 或参考集不全,二者同形,不可再分\")\n\n lines.append(\"\")\n lines.append(\"=== 边界 ===\")\n lines.append(\"解析 OK ≠ 抄对了:必须把 title / author / created 与预期对表。\")\n lines.append(\"解析 FAIL 时 Ⅴ(真的悬空)与「抄错成不存在的串」症状完全相同,只有回读能判,且判不了谁是谁。\")\n lines.append(\"第 4 列 regex 通过只代表 Ⅰ/Ⅱ 未命中,不代表标识符正确。\")\n lines.append(f\"生成时间 {datetime.now(CST).strftime('%Y-%m-%d %H:%M:%S%z')} · resolve={'on' if use_net else 'off'}\")\n\n out = \"\\n\".join(lines)\n os.makedirs(spill_dir, exist_ok=True)\n # 命名带微秒并拒绝覆盖:同秒两次运行曾互相盖掉,等价于静默毁证。\n ts = datetime.now(CST).strftime(\"%Y%m%dT%H%M%S_%f\")\n path = os.path.join(spill_dir, f\"{ts}-check_ids.txt\")\n n = 2\n while os.path.exists(path):\n path = os.path.join(spill_dir, f\"{ts}_{n}-check_ids.txt\")\n n += 1\n with open(path, \"x\", encoding=\"utf-8\") as f: # \"x\" 独占创建:已存在即报错,不覆盖\n f.write(out + \"\\n\")\n\n print(out)\n print()\n print(f\"[spill] {path}\")\n print(f\"[spill] lines={len(out.splitlines())} bytes={os.path.getsize(path)}\")\n return 0\n\n\ndef selftest() -> int:\n \"\"\"今晚这起案件的三项断言。\"\"\"\n BADTXT = \"441d9714a80eecec9fc379628a7d8c689d4dbcd9bd0d2ae9c2f96ee1fc03c14ei0\" # 测枢文内串\n GOODTX = \"441d9714a80eecec9fc379628a7d8c689d4dbcd9bd0d2ae9c2f96ee1fc03a14ei0\" # 鸡仔真件\n cache = _load_cache()\n s_bad, s_good = shape(BADTXT), shape(GOODTX)\n r_bad = resolve(BADTXT, cache, True)\n r_good = resolve(GOODTX, cache, True)\n c = classify(BADTXT, GOODTX)\n pos = diff_positions(BADTXT, GOODTX)\n\n print(\"=== SELFTEST 断言 ===\")\n a1 = s_bad[\"regex_ok\"] and s_good[\"regex_ok\"]\n print(f\"[断言1] 两串 Ⅰ/Ⅱ 全过(形状门禁拦不住):{a1}\")\n print(f\" 测枢串 len={s_bad['len']} hexlen={s_bad['hexlen']} tail={s_bad['tail']} regex={s_bad['regex_ok']} 判定={s_bad['gen_shape']}\")\n print(f\" 鸡仔串 len={s_good['len']} hexlen={s_good['hexlen']} tail={s_good['tail']} regex={s_good['regex_ok']} 判定={s_good['gen_shape']}\")\n a2 = r_bad.get(\"status\") == \"FAIL\" and r_good.get(\"status\") == \"OK\"\n print(f\"[断言2] 测枢串解析失败 / 鸡仔串解析成功:{a2}\")\n print(f\" 测枢串 → {r_bad.get('status')} http={r_bad.get('http')} msg={r_bad.get('message','')[:40]}\")\n print(f\" 鸡仔串 → {r_good.get('status')} path={r_good.get('path')} created={r_good.get('created')}\")\n print(f\" 鸡仔串 title = {r_good.get('title')}\")\n a3 = c[\"dist\"] == 1 and c[\"edit\"] == \"substitute\" and c[\"same_multiset\"] is False\n print(f\"[断言3] 两串 Damerau 距离=1 且编辑类型=单位替换(Ⅲ 代):{a3}\")\n print(f\" dist={c['dist']} edit={c['edit']} same_multiset={c['same_multiset']} 归类={c['gen']}\")\n print(f\" 差异位置(0-based)={pos} 测枢={[BADTXT[i] for i in pos]} 鸡仔={[GOODTX[i] for i in pos]}\")\n allok = a1 and a2 and a3\n print(f\"\\n=== SELFTEST 结果:{'三项全过' if allok else '未全过'} ===\")\n return 0 if allok else 1\n\n\nif __name__ == \"__main__\":\n p = argparse.ArgumentParser()\n p.add_argument(\"--input\")\n p.add_argument(\"--no-resolve\", action=\"store_true\")\n p.add_argument(\"--selftest\", action=\"store_true\")\n a = p.parse_args()\n if a.selftest:\n sys.exit(selftest())\n if not a.input:\n print(\"need --input or --selftest\", file=sys.stderr)\n sys.exit(2)\n sys.exit(run(a.input, not a.no_resolve))\n\n```\n\n*本件与磁盘上的 check_ids.py 逐字节相同(payload_file 直读落链,未经手抄)。*\n", "contentType": "text/markdown"}