| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- """Freeze r9 from sealed r8 plus explicit policy application and source verification modules."""
- import argparse
- from contextlib import closing
- from datetime import datetime, timezone
- import hashlib
- import json
- from pathlib import Path
- import shutil
- import sqlite3
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- BASE = ROOT / 'artifacts/ksf-industry-monitor-20261107-r8/package'
- RELEASE = 'ksf-industry-monitor-20261107-r9'
- SOURCES = ['src/pages/view-industry.js', 'src/pages/industry-research-views.js', 'src/pages/industry-policy-view.js',
- 'src/services/industry-service.js', 'server/voc-library/industry_monitor.py', 'server/voc-library/industry_research.py',
- 'server/voc-library/industry_policy.py', 'server/voc-library/monitoring_policy.py', 'server/voc-library/test_industry_policy.py',
- 'scripts/register-industry-policy.py', 'scripts/verify-industry-monitor.py', 'scripts/verify-industry-mainline.py',
- 'scripts/verify-industry-policy-release.py', 'scripts/verify-industry-policy-runtime.py']
- DOCUMENTS = ['agent-queries.md', 'deployment.md', '主线待决策清单.md', 'r9本地实现与未完成条件.md']
- def policy_view_from_baseline():
- """Keep concurrent innovation edits out of this independently tested policy release."""
- text = (BASE/'payload/src/pages/view-industry.js').read_text('utf-8')
- changes = [
- ("import { reviewsView, policiesView } from './industry-research-views.js';", "import { reviewsView, policiesView } from './industry-research-views.js';\nimport { POLICY_APPLICATION_CSS } from './industry-policy-view.js';"),
- (" for (const [route, key] of Object.entries({ policyPage: 'page', policySegment: 'segment' }))", " for (const [route, key] of Object.entries({ policyApplication: 'application', policyAsOf: 'asOf', policyMg: 'perServingMg', policyGrams: 'servingGrams', policyReference: 'referenceMgPer100g' })) if (this.routeQuery[route]) request.policyQuery[key] = this.routeQuery[route];\n for (const [route, key] of Object.entries({ policyPage: 'page', policySegment: 'segment' }))"),
- ("q: '', policyDoc: '', policyStatus: '', policyPage: '', policySegment: ''", "q: '', policyDoc: '', policyStatus: '', policyPage: '', policySegment: '', policyApplication: '', policyAsOf: '', policyMg: '', policyGrams: '', policyReference: ''"),
- ('[VIEW_CSS, INNOVATION_CSS, DETAIL_CSS, css`', '[VIEW_CSS, INNOVATION_CSS, DETAIL_CSS, POLICY_APPLICATION_CSS, css`'),
- ]
- for old, new in changes:
- if text.count(old) != 1:raise ValueError('R8_POLICY_PATCH_BASELINE_CHANGED')
- text = text.replace(old, new)
- return text.encode('utf-8')
- def sha(raw):return hashlib.sha256(raw).hexdigest()
- def save(path, data):path.write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n','utf-8')
- def build(out):
- if out.exists():raise ValueError('RELEASE_DIRECTORY_MUST_BE_NEW')
- for line in (BASE/'FILES.sha256').read_text('utf-8').splitlines():
- digest,name=line.split(' ',1)
- if sha((BASE/name).read_bytes())!=digest:raise ValueError('FROZEN_R8_CHANGED')
- captured={name:(ROOT/name).read_bytes() for name in SOURCES}
- workspace_view = captured['src/pages/view-industry.js']
- captured['src/pages/view-industry.js'] = policy_view_from_baseline()
- shutil.copytree(BASE,out)
- for name,raw in captured.items():
- target=out/('payload/'+name if name.startswith('src/') else name)
- target.parent.mkdir(exist_ok=True,parents=True);target.write_bytes(raw)
- if any((ROOT/name).read_bytes()!=(workspace_view if name=='src/pages/view-industry.js' else raw) for name,raw in captured.items()):raise ValueError('SOURCE_CHANGED_DURING_FREEZE')
- save(out/'evidence/r9-source-capture.json',{'release':RELEASE,'sourceFilesSha256':{name:sha(raw) for name,raw in captured.items()},'viewIndustrySource':'sealed r8 plus four explicit policy changes; concurrent workspace innovation edits excluded'})
- auxiliary=ROOT/'work/industry-monitoring-20261107-r1/monitoring-registry'
- with closing(sqlite3.connect((auxiliary/'monitoring.sqlite').resolve().as_uri()+'?mode=ro',uri=True)) as source:
- with closing(sqlite3.connect(out/'data/monitoring-registry/monitoring.sqlite')) as destination:
- source.backup(destination)
- destination.execute("UPDATE pipeline_workers SET state='ARCHIVED_COPY'")
- destination.execute("UPDATE dispatch_audits SET state='ARCHIVED_COPY'");destination.commit()
- for file in (auxiliary/'assets').iterdir():
- target=out/'data/monitoring-registry/assets'/file.name
- if not target.exists():shutil.copyfile(file,target)
- release=json.loads((out/'RELEASE.json').read_bytes())
- release.update(release=RELEASE,createdAt=datetime.now(timezone.utc).isoformat(),builtFromFrozenRelease='ksf-industry-monitor-20261107-r8',
- policyApplicationWorkflows=8,officialPolicyMetadataVerified=9,officialPolicyPdfShaMatches=9,
- officialPolicyFetchFailures=16,overallComplete=False,productionDeployed=False,paidCalls=0)
- release['staticFiles'].append('src/pages/industry-policy-view.js')
- release['backendFiles'].extend(['server/voc-library/industry_policy.py','server/voc-library/monitoring_policy.py'])
- save(out/'RELEASE.json',release)
- mpath=out/'deploy/industry-ui-files.json';manifest=json.loads(mpath.read_bytes());manifest['release']=RELEASE
- manifest['files'].append({'path':'src/pages/industry-policy-view.js','allowMissing':True,'priorNormalizedSha256':[],'observedProductionSha256':None})
- for entry in manifest['files']:
- old=BASE/'payload'/entry['path']
- if old.exists():
- digest=sha(old.read_bytes().replace(b'\r\n',b'\n'))
- if digest not in entry['priorNormalizedSha256']:entry['priorNormalizedSha256'].append(digest)
- entry['sha256']=sha((out/'payload'/entry['path']).read_bytes().replace(b'\r\n',b'\n'))
- save(mpath,manifest)
- publisher=out/'scripts/publish-industry-ui.mjs';text=publisher.read_text('utf-8').replace('ksf-industry-monitor-20261107-r8',RELEASE)
- before=next(line for line in text.splitlines() if line.startswith('export const FILES='))
- publisher.write_text(text.replace(before,'export const FILES=Object.freeze('+json.dumps(release['staticFiles'])+');'),'utf-8')
- for file in (out/'deploy').iterdir():
- if file.suffix=='.example':file.write_text(file.read_text('utf-8').replace('ksf-industry-monitor-20261107-r8',RELEASE),'utf-8')
- evidence=ROOT/'work/industry-policy-verification-20261107-r1'
- target=out/'evidence/r9-policy-sources';target.mkdir()
- for file in evidence.iterdir():
- if file.is_file() and file.suffix in ('.json','.html','.png') and file.name not in ('browser-0.json','browser-1.json','application-response.json','preview-response.html'):
- shutil.copyfile(file,target/file.name)
- (out/'docs/README.md').write_text('''# 方便面行业 VOC 后台 r9 本地验收版
- 在冻结 r8 上补齐 8 类政策/膳食条款应用流程、官方状态核验附账和营养算术换算。14 个前端模块、15 个后端文件。
- 9 份标准官方元数据与 PDF 已核,PDF SHA 全部与研究归档一致;另 16 份官方页面本次访问未核成,不据此判文件失效。原研究库声明与新核验记录分开。PDF/HTML 原件保存在辅助资产区,研究库不改写。
- 按用户最新要求先保留本机,全部条件完成前不请求转发。历史 r8 文档保留为来源,旧模块数量与路径不作为 r9 执行指令。原生运行及浏览器结果见 evidence/implementation-receipt-v9.json;本地通过不等于生产已上线。
- 付费保持停止,未自动安装上游定时任务。全量可访问评价、真实全榜及重复观察、持续采集接入、详情文字覆盖和生产主机验收仍分别待证,不写为全部完成。A/B/C 与核心库合并规则保持原状。
- ''','utf-8')
- for name in DOCUMENTS:
- shutil.copyfile(ROOT/'docs/industry-monitoring-20261107'/name, out/'docs'/name)
- print(json.dumps({'ok':True,'candidate':str(out),'staticFiles':len(release['staticFiles']),'backendFiles':len(release['backendFiles']),'sealed':False}))
- def seal(out):
- receipt=json.loads((out/'evidence/implementation-receipt-v9.json').read_bytes())
- if not receipt['accepted'] or not receipt['packageVerificationPassed']:raise ValueError('ACCEPTANCE_REQUIRED')
- for name,digest in receipt['testedFilesSha256'].items():
- if sha((out/name).read_bytes())!=digest:raise ValueError('TESTED_FILE_CHANGED')
- archive=out.parent/(RELEASE+'.zip')
- if archive.exists():raise ValueError('ARCHIVE_EXISTS')
- files=sorted(file for file in out.rglob('*') if file.is_file() and file!=out/'FILES.sha256')
- (out/'FILES.sha256').write_text(''.join(sha(file.read_bytes())+' '+file.relative_to(out).as_posix()+'\n' for file in files),'utf-8')
- with zipfile.ZipFile(archive,'x',zipfile.ZIP_DEFLATED) as z:
- for file in sorted(out.rglob('*')):
- if file.is_file():z.write(file,file.relative_to(out).as_posix())
- print(json.dumps({'ok':True,'archive':str(archive),'bytes':archive.stat().st_size,'sha256':sha(archive.read_bytes()),'productionDeployed':False,'paidCalls':0}))
- if __name__=='__main__':
- parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--out',type=Path,required=True);parser.add_argument('--seal',action='store_true')
- args=parser.parse_args();(seal if args.seal else build)(args.out.resolve())
|