Files
DongHyeonkaandClaude Opus 5 7d1ccccbcd Merge branch 'main' into feature/techlog-ui-migration
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>
2026-08-17 18:38:08 +09:00

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"]);