| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- """Prepare a database-only supplement for the already installed Nov 6 query skills."""
- import argparse
- import hashlib
- import importlib.util
- import json
- from pathlib import Path
- import shutil
- import sys
- import tempfile
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- WORK = ROOT / 'work/voc-database-delivery-20261106'
- OUT = ROOT / 'artifacts/研究数据库补装-20261106'
- NAME = 'ksf-voc-database-20261106-r1'
- RELEASE = OUT / NAME
- DIST = Path('F:/文档/康师傅skill')
- SKILL = ROOT / 'skills/ksf-voc-library'
- EXAMPLES = ROOT / 'artifacts/数据库精准检索规则-20261106/ksf-agent-query-rules-20261106-r1/examples'
- def digest(path):
- with Path(path).open('rb') as stream:
- return hashlib.file_digest(stream, 'sha256').hexdigest()
- def write(path, data):
- path.parent.mkdir(parents=True, exist_ok=True)
- path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
- def manifest(folder):
- entries = {p.relative_to(folder).as_posix(): digest(p) for p in sorted(folder.rglob('*')) if p.is_file() and p.name != 'FILES.sha256'}
- (folder / 'FILES.sha256').write_text(''.join(sha + ' ' + name + '\n' for name, sha in entries.items()), encoding='utf-8', newline='\n')
- def load_module(name, path):
- spec = importlib.util.spec_from_file_location(name, path)
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module)
- return module
- def prepare():
- if RELEASE.exists():
- raise ValueError('FROZEN_RELEASE_ALREADY_EXISTS')
- sys.path.insert(0, str(ROOT / 'server/voc-library'))
- import voc_db
- registry = ROOT / 'work/library-preview/registry'
- pointer = json.loads((registry / 'current.json').read_text(encoding='utf-8'))
- source = voc_db.registry_database(registry)
- if digest(source) != pointer['databaseSha256']:
- raise ValueError('LOCAL_BOARD_POINTER_HASH_MISMATCH')
- with voc_db.readonly(source) as con:
- meta = dict(con.execute('SELECT key,value FROM meta'))
- entity_counts = voc_db.table_counts(con)
- counts = {**entity_counts, 'retrieval_documents': con.execute('SELECT count(*) FROM retrieval_documents').fetchone()[0]}
- database = dict(schema=meta['schema'], generation=meta['generation'], bytes=source.stat().st_size, sha256=digest(source),
- originalArchiveSha256=meta['archive_sha256'], entityCounts=entity_counts, counts=counts)
- destination = RELEASE / 'library/library.sqlite'
- destination.parent.mkdir(parents=True)
- shutil.copyfile(source, destination)
- if digest(destination) != pointer['databaseSha256']:
- raise ValueError('COPIED_DATABASE_HASH_MISMATCH')
- required_skill = digest(SKILL / 'FILES.sha256')
- if required_skill != '4da410bb882dc73e277c740d13407918d9c9d5da2196ac9211f6ff7c009692fb':
- raise ValueError('INSTALLED_QUERY_RELEASE_NO_LONGER_MATCHES')
- write(RELEASE / 'DATABASE.json', dict(release=NAME, database=database,
- requiredResearchSkillManifestSha256=required_skill, source='Codex local board current database snapshot',
- matchesLocalBoardDatabaseBytes=True, storageMode='static-snapshot', automaticUpdatesConfigured=False,
- productionInstalled=False, productionBoardAligned=False, realMessageVerified=False,
- skillIncluded=False, skillInstallationRequired=False, defaultDatabasePath='/opt/data/ksf-voc-library/library.sqlite'))
- for name in ('install-voc-database.py', 'verify-installed-voc-database.py'):
- shutil.copyfile(ROOT / 'scripts' / name, RELEASE / name)
- shutil.copytree(EXAMPLES, RELEASE / 'examples')
- shutil.copytree(ROOT / 'docs/voc-database-delivery-20261106', RELEASE / 'docs')
- manifest(RELEASE)
- print(json.dumps(dict(prepared=str(RELEASE), database=database), ensure_ascii=False, indent=2))
- def validate():
- WORK.mkdir(parents=True, exist_ok=True)
- installer = load_module('seed_install', RELEASE / 'install-voc-database.py')
- verifier = load_module('seed_verify', RELEASE / 'verify-installed-voc-database.py')
- with tempfile.TemporaryDirectory(prefix='simulate-', dir=WORK) as temporary:
- root = Path(temporary)
- target = root / 'ksf-voc-library'
- plan = installer.install(RELEASE, target)
- if target.exists() or not plan['readOnly']:
- raise ValueError('DRY_PLAN_WROTE_TARGET')
- applied = installer.install(RELEASE, target, True)
- landed = target / 'library.sqlite'
- before = (digest(landed), landed.stat().st_mtime_ns)
- repeated = installer.install(RELEASE, target, True)
- if repeated['applied'] or (digest(landed), landed.stat().st_mtime_ns) != before:
- raise ValueError('REPEAT_CHANGED_DATABASE')
- verification = verifier.verify(RELEASE, SKILL, landed)
- if (digest(landed), landed.stat().st_mtime_ns) != before:
- raise ValueError('READBACK_CHANGED_DATABASE')
- result = dict(ok=True, package=NAME, installPlan=plan, installSimulation=applied,
- exactRepeatVerified=True, installedReadback=verification,
- skillFilesChanged=False, productionInstalled=False, realMessageVerified=False)
- write(WORK / 'local-validation.json', result)
- print(json.dumps(dict(ok=True, generation=verification['generation'], scenarios=len(verification['scenarios']),
- sourceChecks=len(verification['citations']), installedDefaultPathSimulated=True, exactRepeatVerified=True), ensure_ascii=False, indent=2))
- def finalize():
- validation = json.loads((WORK / 'local-validation.json').read_text(encoding='utf-8'))
- if not validation['ok']:
- raise ValueError('VALIDATION_REQUIRED')
- package_meta = json.loads((RELEASE / 'DATABASE.json').read_text(encoding='utf-8'))
- if digest(RELEASE / 'library/library.sqlite') != validation['installedReadback']['databaseSha256'] or package_meta['database']['generation'] != validation['installedReadback']['generation']:
- raise ValueError('DATABASE_CHANGED_AFTER_VALIDATION')
- archive = OUT / (NAME + '.zip')
- if archive.exists():
- raise ValueError('ZIP_ALREADY_FROZEN')
- shutil.copyfile(WORK / 'local-validation.json', RELEASE / 'local-validation.json')
- manifest(RELEASE)
- with zipfile.ZipFile(archive, 'x', zipfile.ZIP_DEFLATED) as z:
- for path in sorted(RELEASE.rglob('*')):
- if path.is_file():
- z.write(path, path.relative_to(RELEASE).as_posix())
- with zipfile.ZipFile(archive) as z:
- entries = z.read('FILES.sha256').decode('utf-8').splitlines()
- for line in entries:
- expected, name = line.split(' ', 1)
- if hashlib.sha256(z.read(name)).hexdigest() != expected:
- raise ValueError('ZIP_READBACK_MISMATCH')
- receipt = dict(release=NAME, filename=archive.name, bytes=archive.stat().st_size, sha256=digest(archive),
- entries=len(entries)+1, rootManifestVerified=str(len(entries)) + '/' + str(len(entries)),
- database=json.loads((RELEASE / 'DATABASE.json').read_text(encoding='utf-8'))['database'],
- localValidated=True, databaseIncluded=True, skillIncluded=False, productionInstalled=False,
- realMessageVerified=False, productionBoardAligned=False)
- write(OUT / 'delivery-receipt.json', receipt)
- shutil.copyfile(archive, DIST / archive.name)
- shutil.copyfile(OUT / 'delivery-receipt.json', DIST / '研究数据库补包校验-20261106.json')
- note = '''
- ## 研究库补装 · 2026-11-06
- 已收到运维与王刚001回报:本目录11月6日两份Skill已安装且实际加载;研究数据库此前未安装,查询停在DATABASE_NOT_INSTALLED。对此不需要重装或回退Skill。
- 请只转交 **ksf-voc-database-20261106-r1.zip** 与其包内docs/发给运维001-补装研究数据库.md;校验见研究数据库补包校验-20261106.json。该包只含数据库本体、安装/验收工具和样例,不含Skill,装到既定默认路径,无须新凭据或改网关。
- 补包取自Codex本地看板当前快照;尚未安装到远端,尚未对齐远端生产看板或配置后续自动同步。数据库装好再由王刚001继续原五步真实消息验收。原r2整包中的旧Skill安装器不要再执行。
- '''
- readme = (DIST / 'README.md').read_text(encoding='utf-8')
- readme = readme.replace('不含数据库,沿用已安装v2研究库。缺库时需先用原完整r2资料包建立授权库;不能只复制Skill就声称能查到资料。',
- '查询规则包不含数据库。缺库时用本目录新增的数据库专用补包落地,不再执行原r2整包中的旧Skill安装器。')
- readme = readme.replace('本机准备和验证完成,未在王刚001生产实例安装/加载/真实消息验收。请将当前完整ZIP与包内docs/发给运维001.md一起转运维;安装后将docs/发给王刚001.md交本人真实消息验证。',
- '已收到运维001报告两份Skill安装完成,王刚001确认真实加载;数据库此前未安装,真实数据查询尚未验收。请按下方数据库补装说明继续,不重复安装Skill。')
- (DIST / 'README.md').write_text(readme + note, encoding='utf-8')
- version_file = DIST / '版本与校验.json'
- version = json.loads(version_file.read_text(encoding='utf-8'))
- version['deploymentReport20261106'] = dict(source='user forwarded Operations001 and WangGang001 reports',
- skillsInstalledReported=True, skillsLoadedReported=True, databaseInstalledReported=False,
- realMessageQueryVerified=False, interpretation='historical report before this database supplement; package preparation is not remote installation')
- version['databaseSupplement'] = dict(zip=receipt, installInstructions=str(ROOT / 'docs/voc-database-delivery-20261106/发给运维001-补装研究数据库.md'))
- write(version_file, version)
- handoff = Path('F:/文档/新数字人交接')
- (handoff / '11-研究数据库补装-20261106.md').write_text('''# 研究数据库补装 · 2026-11-06
- 11月6日查询规则包仅含Skill,研究数据库须独立落地。两份Skill已由运维报告安装并经王刚001实际加载,查询因DATABASE_NOT_INSTALLED未开始。
- 当前补装包:F:/文档/康师傅skill/ksf-voc-database-20261106-r1.zip;核验文件:同目录研究数据库补包校验-20261106.json。
- 只补数据库到默认路径,不重装旧r2 Skill,不改身份、会话、网关或生产看板。按包内运维单安装/回读后,再由用户将王刚001续验单转入真实企微消息。数据库源于本地看板同字节快照,远端共享存储和后续自动同步尚未接通。
- ''', encoding='utf-8')
- print(json.dumps({key: value for key, value in receipt.items() if key != 'database'}, ensure_ascii=False, indent=2))
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('phase', choices=['prepare', 'validate', 'finalize'])
- args = parser.parse_args()
- globals()[args.phase]()
|