export const LAUNCH_GATE_SEEDS = [ ["sealed-e2e", "P0", "security", "sealed 命令与附件端到端实证"], ["expiry-boundary", "P0", "entitlement", "到期后的操作边界与重放测试"], ["tenant-isolation", "P0", "infrastructure", "租户隔离、备份与删除验证"], ["billing-idempotency", "PAID", "billing", "订单到权益的幂等与对账"], ["pairing-claim-security", "P0", "security", "主机配对认领、限速与尝试预算"], ["public-auth", "P0", "identity", "国内个人用户登录、恢复与管理员身份"], ["legal-entity", "P0", "compliance", "免费公测主体、域名与备案路径"], ["payment-provider", "PAID", "billing", "支付商户准入、验签、退款与对账"], ["tax-invoice", "PAID", "compliance", "税务、含税口径与数电发票"], ["privacy-retention", "P0", "privacy", "数据清单、保存、删除与跨境路径"], ["terms-consumer", "PAID", "compliance", "付费服务条款、取消、退款与消费者规则"], ["backup-restore", "P1", "operations", "备份与恢复演练"], ["capacity-economics", "P1", "operations", "容量、成本与支持工时实测"], ["beta-policy", "P1", "product", "公测结束、通知、宽限与反滥用限制"], ["build-toolchain-audit", "P1", "security", "构建工具链残余公告与上游替换"], ["account-lifecycle", "P1", "privacy", "导出、注销、保留例外与备份擦除"], ["host-lifecycle-recovery", "P1", "operations", "主机恢复、换绑、停用、重装与凭据轮换"], ["incident-response", "P1", "security", "安全事件分级、值守、通知与服务流程"], ["release-provenance", "P1", "security", "PWA 构建来源、CSP、依赖与回滚 provenance"], ] as const; export const REQUIRED_PUBLIC_BETA_P0_KEYS = LAUNCH_GATE_SEEDS .filter(([, priority]) => priority === "P0") .map(([key]) => key); export type LaunchGateEvidence = { key: string; priority: string; status: string; owner: string | null; notes: string; evidence_url: string | null; }; export function hasPassingGateEvidence(gate: LaunchGateEvidence): boolean { if ( gate.status !== "passed" || !gate.owner?.trim() || !gate.notes.trim() || !gate.evidence_url ) { return false; } try { return new URL(gate.evidence_url).protocol === "https:"; } catch { return false; } } export function countBlockedPublicBetaP0(gates: readonly LaunchGateEvidence[]): number { const required = new Set(REQUIRED_PUBLIC_BETA_P0_KEYS); const presentRequired = new Set( gates .filter((gate) => gate.priority === "P0" && required.has(gate.key)) .map((gate) => gate.key), ); const missingRequired = REQUIRED_PUBLIC_BETA_P0_KEYS.length - presentRequired.size; const invalidP0 = gates.filter( (gate) => gate.priority === "P0" && !hasPassingGateEvidence(gate), ).length; return missingRequired + invalidP0; } const requiredP0SqlList = REQUIRED_PUBLIC_BETA_P0_KEYS .map((key) => `'${key.replaceAll("'", "''")}'`) .join(", "); /** * Fail closed when a required P0 row is missing or any P0 row lacks complete * passing evidence. This fragment is embedded only in repository-owned SQL. */ export const PUBLIC_BETA_GATE_READY_SQL = ` (SELECT COUNT(*) FROM launch_gates WHERE priority = 'P0' AND key IN (${requiredP0SqlList})) = ${REQUIRED_PUBLIC_BETA_P0_KEYS.length} AND NOT EXISTS ( SELECT 1 FROM launch_gates WHERE priority = 'P0' AND ( status != 'passed' OR trim(COALESCE(owner, '')) = '' OR trim(COALESCE(notes, '')) = '' OR evidence_url IS NULL OR lower(evidence_url) NOT LIKE 'https://%' ) )`;