初始化提交:contentm_agent 工作区全量快照

内容分四块:
1、产品规划产出 —— MCN 短视频整合营销工作台的①段五份(1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景)、②段两份(2a 功能/2b 布局)、③段界面(DESIGN.md 契约与令牌表 + mcn-workbench.html 原型 + 实测/会诊/审查三份 + 23 张闸门截图)。
2、开源竞品调研 —— 5 个内容工作台项目的取证原始件与 1b 系列分析文档。
3、参考资料 —— 竞品视频抽帧 1145 张 + 2 个源视频 + 功能点截图。
4、机制侧 —— 协作脚本与状态台账、工作区记忆日志、抽帧/OCR 脚本。

.gitignore 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
This commit is contained in:
WorkBuddy committed 2026-10-08 08:13:02 +08:00
commit df56c2c137
1773 files changed
+205840

No files matched your search

@@ -0,0 +1,93 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""骨架单一可信源闸门:`prd/2b-界面布局.md` §二 ↔ 原型里的 FROZEN 表。
为什么单拎出来:页面里的自检只能证明「渲染出来的板块 == FROZEN 表」,
证明不了「FROZEN 表 == ②段原文」。两张表各改一边就没人发现,所以这条闸门
直接把②段那份 Markdown 当权威读,逐页逐位比。
判据:板块**数量**必须相等,**顺序**必须相等,名字**互为前缀**(②段的名字带
「(可点入)」这类注解,不是给界面抄的标题,⛔ 不逐字比)。
用法:python _tools/check_skeleton.py
"""
import argparse
import os, re, sys
HERE = os.path.dirname(os.path.abspath(__file__))
UI = os.path.abspath(os.path.join(HERE, ".."))
GOAL = os.path.abspath(os.path.join(UI, ".."))
DOC = os.path.join(GOAL, "prd", "2b-界面布局.md")
PROTO = os.path.join(UI, "mcn-workbench.html")
Z2 = "\u3000\u3000" # 全角空格:注释切到右边界用
def from_doc(path=None):
s = open(path or DOC, encoding="utf-8").read()
if "## 二、八页骨架" not in s or "## 三、页面流转" not in s:
raise SystemExit("⛔ 读不到②段的 §二 八页骨架 —— 文件被改结构了?")
seg = s[s.index("## 二、八页骨架"):s.index("## 三、页面流转")]
out, cur = {}, None
for line in seg.split("\n"):
m = re.match(r"^### (P\d) ", line)
if m:
cur = m.group(1).lower()
out[cur] = []
continue
m2 = re.match(r"^\s*(\d+)\.\s*(.+)$", line)
if cur and m2:
out[cur].append(m2.group(2).split("——")[0].strip())
return out
def from_proto(path=None):
s = open(path or PROTO, encoding="utf-8").read()
m = re.search(r"^const FROZEN=\{(.*?)^\};", s, re.S | re.M)
if not m:
raise SystemExit("⛔ 原型里找不到 const FROZEN 表")
out = {}
# ⚠️ 逗号必须可选:表里最后一项后面没有逗号 —— 早前写成必选,p8 被静默漏掉
# (表现为「页面集合不同」,不是静默通过,但那也是漏;已修)
for k, body in re.findall(r"^\s*(p\d):\[(.*?)\],?\s*$", m.group(1), re.M):
items = re.findall(r"'([^']*)'", body)
out[k] = items[1:] # 第 0 项是页面标题
if len(out) != 8:
raise SystemExit("⛔ FROZEN 表只解析出 %d 页(应为 8):%s" % (len(out), sorted(out)))
return out
def norm(x):
return x.split("(")[0].strip()
def main():
ap = argparse.ArgumentParser(description="骨架单一可信源闸门(②段原文 ↔ 原型 FROZEN 表)")
ap.add_argument("--doc", default=None, help="②段界面布局文档(默认 prd/2b-界面布局.md)")
ap.add_argument("--proto", default=None, help="原型 HTML(默认 ui/mcn-workbench.html)")
a = ap.parse_args()
doc, proto = from_doc(a.doc), from_proto(a.proto)
bad = []
if set(doc) != set(proto):
bad.append("页面集合不同:②段 %s / 原型 %s" % (sorted(doc), sorted(proto)))
for p in sorted(set(doc) & set(proto)):
a, b = doc[p], proto[p]
if len(a) != len(b):
bad.append("%s 板块数 ②段 %d ≠ 原型 %d(②段 %s / 原型 %s)" % (p, len(a), len(b), a, b))
continue
for i, (x, y) in enumerate(zip(a, b)):
nx, ny = norm(x), norm(y)
if not (nx.startswith(ny) or ny.startswith(nx)):
bad.append("%s 第 %d 块 ②段「%s」≠ 原型「%s」" % (p, i + 1, x, y))
n = sum(len(v) for v in doc.values())
if bad:
print("FAIL 骨架与②段不一致(②段 %d 个板块):" % n)
for x in bad:
print(" · " + x)
return 1
print("PASS 骨架冻结:八页 %d 个板块,②段 2b-界面布局.md §二 与原型 FROZEN 表逐页逐位一致" % n)
return 0
if __name__ == "__main__":
sys.exit(main())