"""Freeze the existing-data research release; never deploy or collect upstream data.""" import argparse from concurrent.futures import ThreadPoolExecutor from datetime import datetime, timezone import difflib import hashlib import json from pathlib import Path import re import shutil from urllib.error import HTTPError from urllib.request import Request, urlopen import zipfile ROOT = Path(__file__).resolve().parents[1] WORK = ROOT / 'work/industry-existing-data-release-20261108-r1' BASELINE = WORK / 'public-baseline' PRIOR = ROOT / 'artifacts/ksf-industry-monitor-20261107-r11/package' RELEASE = 'ksf-industry-monitor-20261108-r12' PACKAGE = ROOT / 'artifacts' / RELEASE / 'package' POLICY = ROOT / 'artifacts/ksf-industry-policy-usage-20261108-r1/package' def sha(raw): return hashlib.sha256(raw).hexdigest() def normalized(raw): return raw.replace(b'\r\n', b'\n') def save_json(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 replace_one(text, before, after): assert text.count(before) == 1, 'PUBLIC_ANCHOR_CHANGED: ' + before[:80] return text.replace(before, after, 1) def shared_payloads(): """Use observed public code, adding only industry navigation integration.""" app = (BASELINE / 'src/layout/kf-app.js').read_text(encoding='utf-8') changes = [ ("import { CONFIG } from '../app.config.js';", "import { CONFIG } from '../app.config.js';\nimport { industrySection } from '../core/industry-navigation.js';"), (' const previous = this._route;', ' const previous = this._route;\n const previousQuery = this._query;'), (" const samePool = ['pool', 'mode'].includes(previous.name) && ['pool', 'mode'].includes(name);", " const samePool = ['pool', 'mode'].includes(previous.name) && ['pool', 'mode'].includes(name);\n const industrySectionChanged = previous.name === 'industry' && name === 'industry' && (previousQuery.tab || 'insights') !== (query.tab || 'insights');"), ("navigation !== 'initial' && previous.path !== path && !samePool && name !== 'mode'", "navigation !== 'initial' && ((previous.path !== path && !samePool) || industrySectionChanged) && name !== 'mode'"), (" const [title, sub] = reportType ? [reportType.name, '报告中心'] : TITLES[this._route.name] || TITLES.notfound;", " const researchPage = this._route.name === 'industry' ? industrySection(this._query.tab || 'insights') : null;\n const [title, sub] = researchPage ? [researchPage.label, '行业监测'] : reportType ? [reportType.name, '报告中心'] : TITLES[this._route.name] || TITLES.notfound;"), ('nav{flex:1;min-height:0;overflow-y:auto;align-content:start;scrollbar-width:thin} .brand,.foot{flex-shrink:0} .industry-group{min-width:0}.industry-group [hidden]{display:none!important} .industry-group button{font:inherit;cursor:pointer;touch-action:manipulation} .industry-group .nav-parent{display:flex;align-items:center;gap:12px;width:100%;min-height:44px;padding:5px 12px;border:0;border-radius:var(--r-md);background:transparent;color:var(--kf-ink-2);text-align:left;font-size:14px;font-weight:500} .industry-group.active>.nav-parent{color:var(--kf-accent-ink)} .industry-group .en{white-space:nowrap;font-size:12px} .industry-group .chevron{display:flex;flex:none}.industry-children{display:grid;gap:3px;margin:5px 0 7px 21px;padding-left:12px;border-left:1px solid var(--kf-line)} .industry-child{gap:8px;min-width:0;height:37px;padding:0 9px;font-size:12px;font-weight:400;line-height:1.6} .industry-child>svg{flex:none;width:15px;height:15px}.industry-child>span:not(.library-current-mark){flex:1} .industry-child[aria-current=page]{color:var(--kf-accent-ink);background:var(--kf-accent-soft);box-shadow:none;font-weight:600}.industry-child[aria-current=page]::before{display:none}.library-current-mark{display:flex;flex:none} .industry-group button:focus-visible,.industry-child:focus-visible{outline:2px solid var(--kf-accent);outline-offset:-2px} ''' nav = replace_one(nav, ' :host { display: block; height: 100%; }', ' :host { display: block; height: 100%; }' + css) branch = '''it.path === '/industry' ? html`
${this.#industryLinks()}
` : ''' nav = replace_one(nav, '${ITEMS.map((it) => html` ' + branch + 'html`