docs(v2): 补齐预审闭环规格与Claude开发交接
verify / test-and-build (push) Canceled after 0s

This commit is contained in:
2026-09-29 12:09:18 +08:00
parent 47ec30a562
commit 55e2cdbba3
23 changed files with 783 additions and 34 deletions
@@ -0,0 +1,17 @@
# 事实核对与作者审阅
日期:2026-09-29。
- git status/log:基线47ec30a,当前codex/xingbang-v2-docs-20260928;仅原有无关未跟踪文件,无待覆盖业务改动。
- 独立v2目录:仓库v2/及F:/mini-OpenClaw-v2均未创建;迁移仍为计划。
- backend/services/assistant_model.py:按allowed_tools过滤read_project_snapshot/read_document/read_checks/read_method/read_experiences,反馈只形成experience_proposal。
- backend/services/assistant_experiences.py:已有draft、evaluation、published不可变版本;新卡片与闭环仍需复用设计。
- scripts/verify.py存在并使用项目venv;本轮纯文档未运行完整业务验证,不挪用旧版测试结果作为v2实现证据。
- 原型HTML/脚本本轮不改;六轴及人工情景表是既有演示,动态维度和闭环UI仅候选规格。
- Laya官方仓库 https://github.com/NandhaKishorM/laya :已查阅本地服务、上下文限制、模型差异和Honest limits;未安装/实测。Jev官方 https://docs.typesafe.ai/introduction 已查阅,未接API。
- Claude官方 https://code.claude.com/docs/en/memory 已核对@AGENTS.md导入。DeepSeek官方Claude Code指南可从官方搜索发现,正文抓取超时;文档只提供入口,不编写配置命令或固定模型名。
- Gitea恢复点的旧核验不等于最新文档备份;本轮远程结果单独记录,不改main、不部署。
作者自查:正确性——区分会议事件与标签映射、当前能力与设计;一致性——修正保留集先划分再提炼、任务AC映射和异步沙盘;数据/权限——版本、来源、出口、无密钥;验证——机械检查与真实业务验收分开。
残余:API读清单/DDL、具体代码路径、性能、生产角色、中文模型效果和真实概率均有门禁owner。独立业务/技术评审未进行;CLAUDE入口实际加载与成本改善未测试。
+12
View File
@@ -0,0 +1,12 @@
# 文档验证计划
日期:2026-09-29;范围:docs/v2 与根 CLAUDE.md,纯文档交接。
1. 检查本地Markdown链接和锚点、表格结构、编码字符。
2. 原BR/E/AC及ADR/URD编号只追加保留;44条BR、26条E、45条AC唯一连续;全部BR/E有GWT覆盖。
3. 检查任务编号T00–T13、跨文档AC引用和异常码/用户文案。
4. 核对源码事实及外部来源,列明未知/未实施事项。
5. 仅允许docs/v2、CLAUDE.md和本证据变化,git diff --check;记录业务源码未变。
6. 保存命令、base SHA、最终文档哈希及结果;不运行业务测试/模型/部署,实际交付Git状态另存delivery.json。
本检查不证明候选架构已通过独立评审,或Claude/DeepSeek实际接入成功。
@@ -0,0 +1,9 @@
# 文档检查报告
日期:2026-09-29。状态:PASS
检查 31 份Markdown、135 个本地链接;44条BR、26条E、45条AC全部唯一连续并有GWT覆盖;跨文档引用、任务T00–T13、锚点、错误码与表格检查。
命令:`backend/venv/Scripts/python.exe test-evidence/20260929-v2-handoff/check_docs.py`;cwd为仓库根。基线47ec30a,最终文档SHA256见validation.json。
git diff --check及范围检查结果见validation.json;业务源码/原型未改变。纯文档未执行verify.py或模型/CLI实测。独立业务/技术审阅未完成,不能据此进入未过门禁的实现。
@@ -0,0 +1,78 @@
from pathlib import Path
import json,re,subprocess,hashlib
root=Path.cwd(); out=root/'test-evidence/20260929-v2-handoff'
def git(*args):
return subprocess.check_output(['git',*args],encoding='utf-8')
files=[root/'AGENTS.md',root/'CLAUDE.md',*sorted((root/'docs/project-process').rglob('*.md')),*sorted((root/'docs/v2').glob('*.md'))]
issues=[]; links=0
for p in files:
s=p.read_text(encoding='utf-8')
if any(ord(c)<32 and c not in '\n\r\t' for c in s): issues.append(f'control character: {p}')
for target in re.findall(r'\[[^\]\n]*\]\(([^)]+)\)',s):
target=target.strip('<>').split('#')[0]
if not target or re.match(r'^[a-zA-Z][a-zA-Z0-9+.-]*:',target): continue
links+=1
if not (p.parent/target).exists(): issues.append(f'broken link: {p.name}: {target}')
for block in re.findall(r'(?:^\|.*(?:\n|$))+',s,re.M):
lines=block.splitlines()
if len(lines)<2 or not re.match(r'^\|[\s:|\-]+\|$',lines[1]): issues.append(f'table without header: {p.name}: {lines[0][:80]}')
s=(root/'docs/v2/01-requirements.md').read_text(encoding='utf-8')
old=git('show','47ec30a:docs/v2/01-requirements.md')
counts={}
for kind,count in [('BR',44),('E',26),('AC',45)]:
ids=re.findall(r'^\| ('+kind+r'-\d+) \|',s,re.M)
expected=[f'{kind}-{i:02}' for i in range(1,count+1)]
if ids!=expected: issues.append(f'{kind} IDs invalid: {ids}')
old_ids=set(re.findall(r'^\| ('+kind+r'-\d+) \|',old,re.M))
if not old_ids.issubset(ids): issues.append(f'{kind} baseline IDs lost')
counts[kind]=len(ids)
rows=re.findall(r'^\| AC-\d+ \|.*$',s,re.M)
for row in rows:
if not all(t in row for t in ['Given','When','Then']): issues.append(f'not GWT: {row}')
covered=set(re.findall(r'\b(?:BR|E)-\d+\b','\n'.join(rows)))
required=set(re.findall(r'^\| ((?:BR|E)-\d+) \|',s,re.M))
if required-covered: issues.append(f'uncovered: {required-covered}')
if covered-required: issues.append(f'undefined: {covered-required}')
u=(root/'docs/v2/URD.md').read_text(encoding='utf-8')
uids=re.findall(r'^\| (AC-U\d+) \|',u,re.M)
if len(uids)!=len(set(uids)) or len(uids)!=9: issues.append('URD acceptance IDs')
for row in re.findall(r'^\| MOD-.*$',u,re.M):
if not set(re.findall(r'AC-U\d+',row)).issubset(uids): issues.append(f'URD missing AC: {row}')
changed=git('diff','47ec30a','--name-only').splitlines()
if any(not (p.startswith('docs/v2/') or p == 'CLAUDE.md' or p.startswith('test-evidence/20260929-v2-handoff/')) for p in changed): issues.append('change outside documented scope')
# Cross-document stable ID references (expanded ranges validated by endpoint IDs).
valid={kind:set(re.findall(r'^\| ('+kind+r'-\d+) \|',(root/'docs/v2/01-requirements.md').read_text(encoding='utf-8'),re.M)) for kind in ['BR','E','AC']}
for p in (root/'docs/v2').glob('*.md'):
for ref in re.findall(r'\b(?:BR|E|AC)-\d+\b',p.read_text(encoding='utf-8')):
if ref not in valid[ref.split('-')[0]]: issues.append(f'undefined cross-document ID: {p.name}: {ref}')
hand=(root/'docs/v2/15-development-handoff.md').read_text(encoding='utf-8')
if re.findall(r'^\| (T\d+) ',hand,re.M) != [f'T{i:02}' for i in range(14)]: issues.append('task queue IDs')
for p in (root/'docs/v2').glob('*.md'):
# Verify local heading fragments using GitHub-style punctuation removal.
for target in re.findall(r'\[[^\]\n]*\]\(([^)]+)\)',p.read_text(encoding='utf-8')):
if '#' not in target or re.match(r'^[a-zA-Z][a-zA-Z0-9+.-]*:',target): continue
rel,frag=target.split('#',1); dest=p.parent/rel if rel else p
if dest.exists() and dest.suffix=='.md':
heads=re.findall(r'^#{1,6} (.+)$',dest.read_text(encoding='utf-8'),re.M)
slugs={re.sub(r'[^\w\- ]','',h.strip().lower()).replace(' ','-') for h in heads}
if frag not in slugs: issues.append(f'broken anchor: {p.name}: {target}')
for row in re.findall(r'^\| E-\d+ \|.*$', (root/'docs/v2/01-requirements.md').read_text(encoding='utf-8'),re.M):
if not re.search(r'\b[A-Z][A-Z_]+\b',row) or len(row.split('|'))<5: issues.append(f'error binding missing: {row}')
if not (root/'CLAUDE.md').read_text(encoding='utf-8').startswith('@AGENTS.md'): issues.append('Claude must import canonical rules')
adr=(root/'docs/v2/09-decisions.md').read_text(encoding='utf-8')
adr_ids=re.findall(r'^\| (ADR-\d+) \|',adr,re.M)
if adr_ids != [f'ADR-{i:02}' for i in range(1,15)]: issues.append('ADR IDs invalid')
old_adr=git('show','47ec30a:docs/v2/09-decisions.md')
if not set(re.findall(r'^\| (ADR-\d+) \|',old_adr,re.M)).issubset(adr_ids): issues.append('ADR baseline IDs lost')
check=subprocess.run(['git','diff','--check'],capture_output=True,text=True)
if check.returncode: issues.append(check.stdout+check.stderr)
report={'base_revision':'47ec30a562f12864b7485bb7a43b967fa8f5b755','cwd':str(root),'command':'backend/venv/Scripts/python.exe test-evidence/20260929-v2-handoff/check_docs.py','markdown_files':len(files),'local_links':links,'ids':counts,'status':'PASS' if not issues else 'FAIL','issues':issues,'runtime_tests':'Not run: documentation-only change','hashes':{str(p.relative_to(root)).replace('\\','/'):hashlib.sha256(p.read_bytes()).hexdigest() for p in files}}
(out/'validation.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({k:v for k,v in report.items() if k!='hashes'},ensure_ascii=False,indent=2))
summary = '# 文档检查报告\n\n日期:2026-09-29。状态:' + report['status'] + '\n\n'
summary += f"检查 {len(files)} 份Markdown、{links} 个本地链接;44条BR、26条E、45条AC全部唯一连续并有GWT覆盖;跨文档引用、任务T00–T13、锚点、错误码与表格检查。\n\n"
summary += '命令:`backend/venv/Scripts/python.exe test-evidence/20260929-v2-handoff/check_docs.py`;cwd为仓库根。基线47ec30a,最终文档SHA256见validation.json。\n\n'
summary += 'git diff --check及范围检查结果见validation.json;业务源码/原型未改变。纯文档未执行verify.py或模型/CLI实测。独立业务/技术审阅未完成,不能据此进入未过门禁的实现。\n'
if issues: summary += '\n问题:\n' + '\n'.join('- '+x for x in issues) + '\n'
(out/'REPORT.md').write_text(summary,encoding='utf-8')
raise SystemExit(bool(issues))
@@ -0,0 +1,48 @@
{
"base_revision": "47ec30a562f12864b7485bb7a43b967fa8f5b755",
"cwd": "F:\\mini-OpenClaw-local-stable",
"command": "backend/venv/Scripts/python.exe test-evidence/20260929-v2-handoff/check_docs.py",
"markdown_files": 31,
"local_links": 135,
"ids": {
"BR": 44,
"E": 26,
"AC": 45
},
"status": "PASS",
"issues": [],
"runtime_tests": "Not run: documentation-only change",
"hashes": {
"AGENTS.md": "51792b63ff8857f3875a03d8242fb5a2e0898f9a28a2684e5fb61e6e2f2c2271",
"CLAUDE.md": "592a82b17f292d81ba8783446e316129ad0737cf25256efe6b18af780712d083",
"docs/project-process/01-requirement.md": "0a17308dcfa5f1e0fac84507e9aec8bdf7877880f609ceee7352ea41d62ebe41",
"docs/project-process/02-architecture.md": "1ed42b1aba3e5916132138550f4f1687c0d21e33c47140a4f12f603ce04a3a83",
"docs/project-process/03-detailed-design.md": "2430148d69d96be36d34868a3b936babbf88729a4e5a82d329b4e81939267faa",
"docs/project-process/04-ui-spec.md": "c10134f8480613ef3b3089e287f099905e061c15df0016a0e9ea1767464aab6e",
"docs/project-process/05-implementation.md": "541df8d50d69771c7ddb0332838c77b231b2a37fc07e9b2586a4fcb264d7ca67",
"docs/project-process/06-validation.md": "f41f1a531c8a43a4b0cde0c0d918514d6936fbb7ae066720b278a68ea65145d8",
"docs/project-process/07-delivery.md": "1fa36286bb50605a5e59cd3d7fbe2acf32778f4801b1f49c97185cebacd14f55",
"docs/project-process/08-learning.md": "d69aac3638523ec8f90ee400a2dd79bb1bb64726de93b3817d771e8b5105f726",
"docs/project-process/README.md": "44892f59d5efa77f0ba4070d179bddf500a501902cd17de23c9c75d7290dea33",
"docs/project-process/templates/URD-Template.md": "d55ff174bf88aaecbdf0b24a1d3d70ca6b1a609fb01182acf361d348d677b46a",
"docs/project-process/XINGBANG-ADAPTATION.md": "f7f88b86dd6bdabe597ee0decac2ef381b8c1dd74bb734c6f64a0d5b89af0f43",
"docs/v2/01-requirements.md": "c969a38b2bdf3534f8026a4fb7b9ae9f9435ed7064f5e89d8faf08dbcaa28e15",
"docs/v2/02-architecture.md": "147a26c4edd754536a986078de1a4b9b1d24afa10e6b4259105b83e54fde6199",
"docs/v2/03-detailed-design.md": "496fc1086449545f7186f7057f85eca045959c2131c55afee7a12e4bbbebeaf1",
"docs/v2/04-ui-spec.md": "3a200ea2bf525f1bde9517651154710eb41f84343f56b3cb3813fcc1513ad04a",
"docs/v2/05-implementation-plan.md": "4e30b8dd39ddf653620a9af8e6d5596771f50dd4e026a7c351b6d542ee2390d8",
"docs/v2/06-validation.md": "82a693276dc1bef5fb9d83e389d743d1942b50914c569268ac376b00048c88fd",
"docs/v2/07-deployment-and-gitea.md": "c0e0f3daf46ddc8de1c90cae52abd6481eb784d7bd4031c8340775aa90d0439f",
"docs/v2/08-data-pilot-and-demo.md": "ebae5c0a1e6768e145b8740077e479b15bc2da46d290e494baab06117f3c1b7e",
"docs/v2/09-decisions.md": "14e67a46d8488f8b7d86cd370531188f1463202806135c3ac195588fdf83492e",
"docs/v2/10-delivery-record.md": "f1e2193cf63dd5969b804bb389bb76e65ca670aa72f28c571bd00dfbfd58cf5d",
"docs/v2/11-judgment-providers.md": "4366df02f1553da12ef81b75b4538356ff88973c5b66f0b4bcf9674ddd14c7f3",
"docs/v2/12-prototype.md": "4b53ab12a472743a4005839813239d0b68d317d8b317c545b10b12a7d7265ca6",
"docs/v2/13-probability-prototype.md": "d63115e0764ab5aa43189d9ce285ac8ba3084f0c3574295e60906a2247138b66",
"docs/v2/14-experience-loop.md": "c6b95ff1227597e20c8469e33793558cecb5c0a49c126872d98b7fc1d23754de",
"docs/v2/15-development-handoff.md": "5bfbbaa529e1c8789e1a3bd3dcd8770a65b60900187556c18c95d1e074541cd2",
"docs/v2/DESIGN.md": "3899bd11cf8b78cef44f3b6490b6d8a4b916bbdbeeb62ee1d02d23530497e114",
"docs/v2/README.md": "c6272aa02df5c01e6d24801458bc741d62cf9850c15e40c44bbfdc3023e2feeb",
"docs/v2/URD.md": "75bf2b68e1302a9bc344ab34cba41c44bccf880ccead38e738622013eeb1bcea"
}
}