Integrates the frontend template sync (a0fbafb → 5434760) into the TechLog UI migration. Merged in this direction so every conflict is resolved and proved in the worktree; main is only fast-forwarded afterwards and never holds a state that was not verified here. 15 conflicts. The rule throughout: keep the template's mechanism, keep the product's content, and never invent a third state neither branch would accept. The template's product manifest and its runtime feature kill switch are adopted. The route registry is deliberately not composed from contract.routes: the reference feature still declares screens this product deleted, and reducing over them would register paths with no component behind them. ROUTE_FEATURE_OWNER is narrowed to registered routes for the same reason. The first resolution did compose from contract.routes and was rejected by product-features.test.ts. Three files pinned counts and a digest describing the gate contract. Neither side's numbers describe the merged config/ci/gates.json, so they were recomputed from it rather than chosen: 27 gates, 82 commands, 94 command references, 107 evidence references, 128 artifacts, shape sha256 5063586d. README.md and docs/accessibility/manual-checklist.md now enumerate this product's 27 routes, which the template's own verify:documentation requires. product-feature-switch.test.tsx was rewritten around the invariant that still applies here — no registered route without a component — rather than deleted with the screens it used to exercise. docs/operations/template-merge-2026-08-17.md records every decision, the gate results, and the three follow-ups this merge deliberately did not decide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { rm } from "node:fs/promises";
|
|
import process from "node:process";
|
|
|
|
import { INSTALLED_RUNTIME_CAPABILITIES } from "../src/features/installed-runtime-capabilities.ts";
|
|
|
|
/**
|
|
* §17.2.2. Deterministic two-pass build.
|
|
*
|
|
* The Service Worker needs the exact hashed asset list at compile time, so the
|
|
* order is fixed:
|
|
*
|
|
* 1. clean dist and .generated/frontend-runtime
|
|
* 2. generate contractSet and build-info source
|
|
* 3. Vite app build (emptyOutDir = true)
|
|
* 4. materialize dist/config.json from the declared APP_PROFILE
|
|
* 5. scan app dist and generate the static asset source
|
|
* 6. ACTIVE only: Vite Service Worker build (emptyOutDir = false)
|
|
* 7. generate the self-contained TechLog production serving boundary
|
|
* 8. generate Release Manifest V2 and the build manifest
|
|
*
|
|
* Steps 5 and 6 are skipped for `REMOVE_REGISTRATION`, `PURGE_OWNED_RESOURCES`
|
|
* and `null`: those modes never run an active worker build.
|
|
*
|
|
* Step 4 has to follow the Vite build and precede the asset scan. Vite copies
|
|
* `public/` verbatim, so without it every build — including a production one —
|
|
* ships the local runtime document; and the Service Worker hashes the emitted
|
|
* `config.json`, so the profile must be in place before that inventory is
|
|
* taken.
|
|
*/
|
|
|
|
const selection = INSTALLED_RUNTIME_CAPABILITIES.serviceWorker;
|
|
const buildsActiveWorker = selection?.mode === "ACTIVE";
|
|
|
|
function run(command: string, args: readonly string[]): void {
|
|
const result = spawnSync(command, [...args], {
|
|
stdio: "inherit",
|
|
env: process.env,
|
|
});
|
|
if (result.status !== 0) {
|
|
process.stderr.write(`build step failed: ${command} ${args.join(" ")}\n`);
|
|
process.exit(result.status ?? 1);
|
|
}
|
|
}
|
|
|
|
// 1. clean
|
|
await rm("dist", { recursive: true, force: true });
|
|
await rm(".generated/frontend-runtime", { recursive: true, force: true });
|
|
|
|
// 2. contract set + build info
|
|
run("node", ["scripts/generate-contract-set.ts"]);
|
|
|
|
// 3. app build
|
|
run("npx", ["vite", "build"]);
|
|
|
|
// 4. runtime config for the declared profile
|
|
run("node", ["scripts/generate-runtime-config.ts"]);
|
|
|
|
if (buildsActiveWorker) {
|
|
// 5. hashed asset inventory
|
|
run("node", ["scripts/generate-service-worker-assets.ts", "dist"]);
|
|
// 6. service worker build
|
|
run("npx", ["vite", "build", "--config", "vite.service-worker.config.ts"]);
|
|
} else {
|
|
process.stdout.write(
|
|
`service worker mode ${selection?.mode ?? "null"}: skipping worker build\n`,
|
|
);
|
|
}
|
|
|
|
// 7. production serving boundary
|
|
run("node", ["scripts/generate-tech-log-serving-artifact.ts"]);
|
|
|
|
// 8. release + build manifest
|
|
run("node", ["scripts/generate-build-manifest.ts"]);
|