chore: sync the frontend template from a0fbafb to 5434760

Carries eight template commits: the provider sandbox actually running, release
admission to a named environment, the product feature manifest with its runtime
kill switch, architecture and documentation rules that match what is enforced,
the removability fixtures, and the browser, visual and performance evidence.

Product identity is unchanged. `package.json` keeps `tech-log-frontend` and the
catalog keeps the Tech Log naming; the home page was not in the delta. The
visual baselines are this product's own — the template's were excluded from the
transplant and these were regenerated here, where the only difference is the
platform overview's new product-feature section.

What this repository gains operationally: `config/runtime/{local,development,
staging,production}.json` with `FE-GATE-027` refusing an artifact whose runtime
document does not match the environment it is being admitted to, and
`FEATURE_OVERRIDES` for taking an installed feature out of service without a
rebuild.

Verified here: eight gates green, build green, visual 5/5, and 1,858 of 1,859
tests in the suites that do not need a sandbox — the one failure passes in
isolation and is a jsdom lazy-chunk timeout under parallel load. The provider
suites cannot run on this machine at all: `kernel.apparmor_restrict_unprivileged
_userns=1` makes `bwrap --unshare-net` fail, reproducible without any code from
either repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-15 21:34:19 +09:00
co-authored by Claude Opus 5
parent 325a2a0843
commit bdee07a93b
101 changed files with 3116 additions and 448 deletions
@@ -4,6 +4,8 @@ import {
type InstalledContractPackageIdentity,
} from "../contracts/external-contract-runtime.ts";
import { REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION } from "./reference-feature/contracts/reference-feature-contract-contribution.ts";
import { REFERENCE_FEATURE_ID } from "./reference-feature/contracts/reference-feature-contract.ts";
import { INSTALLED_PRODUCT_FEATURE_IDS } from "./installed-product-manifest.ts";
/**
* §4.8. Static contract selection SSOT.
@@ -13,7 +15,11 @@ import { REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION } from "./reference-feature/con
* `src/features/<feature>/contracts/<service>-contract-contribution.ts`.
*/
export const INSTALLED_CONTRACT_CONTRIBUTIONS: readonly InstalledContractContribution[] =
Object.freeze([REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION]);
Object.freeze(
INSTALLED_PRODUCT_FEATURE_IDS.includes(REFERENCE_FEATURE_ID)
? [REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION]
: [],
);
export const COMPOSED_CONTRACT_CONTRIBUTIONS = composeContractContributions(
INSTALLED_CONTRACT_CONTRIBUTIONS,
+10 -1
View File
@@ -1,14 +1,23 @@
import type { ApplicationFeatureInputs } from "../application/ports/in/application-api.ts";
import { createReferenceFeatureInstalledInput } from "./reference-feature/adapters/create-reference-feature-input.ts";
import { REFERENCE_FEATURE_ID } from "./reference-feature/contracts/reference-feature-contract.ts";
import { INSTALLED_PRODUCT_FEATURE_IDS } from "./installed-product-manifest.ts";
/**
* §3.5. Partial on purpose: a feature the manifest did not select supplies no
* driving input, so consumers have to narrow before calling one. A total type
* here would let feature code compile against an input that is not there.
*/
type InstalledFeatureInputs = Readonly<
Pick<ApplicationFeatureInputs, typeof REFERENCE_FEATURE_ID>
Partial<Pick<ApplicationFeatureInputs, typeof REFERENCE_FEATURE_ID>>
>;
export function createInstalledFeatureInputs(
context: Parameters<typeof createReferenceFeatureInstalledInput>[0],
): InstalledFeatureInputs {
if (!INSTALLED_PRODUCT_FEATURE_IDS.includes(REFERENCE_FEATURE_ID)) {
return Object.freeze({});
}
const referenceFeature = createReferenceFeatureInstalledInput(context);
return Object.freeze({
[referenceFeature.featureId]: referenceFeature.input,
+41 -12
View File
@@ -4,7 +4,9 @@ import {
composeSchemaRegistry,
PLATFORM_SCHEMA_REGISTRY,
} from "../contracts/schema-registry.ts";
import { REFERENCE_FEATURE_CONTRACT } from "./reference-feature/contracts/reference-feature-contract.ts";
import {
INSTALLED_PRODUCT_FEATURES,
} from "./installed-product-manifest.ts";
import {
composeApiOperations,
validateApiRuntimeBindings,
@@ -13,18 +15,27 @@ import { validateRestProfileBindings } from "../contracts/rest-profiles.ts";
import { composeRuntimeSchemaCodecs } from "../contracts/schema-registry.ts";
import { composeBoundaryMapperRegistry } from "../contracts/boundary-mapper.ts";
export const INSTALLED_FEATURE_CONTRACTS = Object.freeze([
REFERENCE_FEATURE_CONTRACT,
]);
/**
* §3.5. Composed from the product manifest rather than from a literal list, so
* a feature the build did not select contributes no routes, no operations, no
* schemas and no messages — and is therefore not reachable from any registry.
*/
export const INSTALLED_FEATURE_CONTRACTS = INSTALLED_PRODUCT_FEATURES;
export const ROUTE_REGISTRY = Object.freeze({
...PLATFORM_ROUTE_REGISTRY,
...REFERENCE_FEATURE_CONTRACT.routes,
});
export const ROUTE_RUNTIME_CONTRACT = Object.freeze({
...PLATFORM_ROUTE_RUNTIME_CONTRACT,
...REFERENCE_FEATURE_CONTRACT.routeRuntimeContracts,
});
export const ROUTE_REGISTRY = Object.freeze(
INSTALLED_FEATURE_CONTRACTS.reduce(
(registry, contract) => ({ ...registry, ...contract.routes }),
{ ...PLATFORM_ROUTE_REGISTRY },
),
) as typeof PLATFORM_ROUTE_REGISTRY &
(typeof INSTALLED_PRODUCT_FEATURES)[number]["routes"];
export const ROUTE_RUNTIME_CONTRACT = Object.freeze(
INSTALLED_FEATURE_CONTRACTS.reduce(
(registry, contract) => ({ ...registry, ...contract.routeRuntimeContracts }),
{ ...PLATFORM_ROUTE_RUNTIME_CONTRACT },
),
) as typeof PLATFORM_ROUTE_RUNTIME_CONTRACT &
(typeof INSTALLED_PRODUCT_FEATURES)[number]["routeRuntimeContracts"];
export const API_OPERATIONS = composeApiOperations(
INSTALLED_FEATURE_CONTRACTS.map((contract) => contract.apiOperations),
);
@@ -69,6 +80,24 @@ export const API_RUNTIME_BINDINGS_VALID = validateApiRuntimeBindings(
MAPPER_REGISTRY,
);
/**
* §3.5. Which feature owns each route.
*
* A route contributed by a feature disappears with it at build time, and has to
* be withdrawn from navigation and from the router when the runtime document
* disables that feature. Platform routes have no owner and are always present.
*/
export const ROUTE_FEATURE_OWNER: Readonly<Record<string, string>> =
Object.freeze(
Object.fromEntries(
INSTALLED_FEATURE_CONTRACTS.flatMap((contract) =>
Object.keys(contract.routes).map(
(routeId) => [routeId, contract.featureId] as const,
),
),
),
);
export const NAVIGATION_ROUTES = Object.freeze(
Object.values(ROUTE_REGISTRY)
.filter(isNavigableRoute)
+8 -2
View File
@@ -1,10 +1,16 @@
import { REFERENCE_MESSAGE_CATALOGS } from "./reference-feature/contracts/reference-message-catalog.ts";
/**
* §3.5. Messages are deliberately *not* gated on the manifest. The catalog's
* key type is what makes `message()` total, so dropping keys would turn every
* lookup partial for the sake of a few unreachable strings.
*/
const reference = REFERENCE_MESSAGE_CATALOGS;
export const INSTALLED_MESSAGE_CATALOGS = Object.freeze({
"ko-KR": Object.freeze({
...REFERENCE_MESSAGE_CATALOGS["ko-KR"],
...reference["ko-KR"],
}),
"en-US": Object.freeze({
...REFERENCE_MESSAGE_CATALOGS["en-US"],
...reference["en-US"],
}),
} as const);
+14 -2
View File
@@ -4,13 +4,25 @@ import {
REFERENCE_FEATURE_ROUTE_CODECS,
REFERENCE_FEATURE_ROUTE_RUNTIME,
} from "./reference-feature/presentation/reference-feature-runtime.tsx";
import { REFERENCE_FEATURE_ID } from "./reference-feature/contracts/reference-feature-contract.ts";
import { INSTALLED_PRODUCT_FEATURE_IDS } from "./installed-product-manifest.ts";
/**
* §3.5. A feature the manifest did not select contributes no codec and no route
* component, so the router has nothing to mount for it. The module is still
* linked — a static import cannot be undone by a value — which is why physical
* removal is FE-GATE-020's job and this is deselection, not deletion.
*/
const referenceSelected = INSTALLED_PRODUCT_FEATURE_IDS.includes(
REFERENCE_FEATURE_ID,
);
export const ROUTE_CODECS = Object.freeze({
...PLATFORM_ROUTE_CODECS,
...REFERENCE_FEATURE_ROUTE_CODECS,
...(referenceSelected ? REFERENCE_FEATURE_ROUTE_CODECS : {}),
});
export const ROUTE_RUNTIME = Object.freeze({
...PLATFORM_ROUTE_RUNTIME,
...REFERENCE_FEATURE_ROUTE_RUNTIME,
...(referenceSelected ? REFERENCE_FEATURE_ROUTE_RUNTIME : {}),
});
@@ -0,0 +1,63 @@
import {
selectCompiledProductFeatures,
type SelectableProductFeature,
} from "../contracts/product-features.ts";
import { REFERENCE_FEATURE_CONTRACT } from "./reference-feature/contracts/reference-feature-contract.ts";
/**
* §3.5. The product manifest: the single declaration of which features this
* build contains.
*
* Before this file the reference feature was spread directly into the route,
* API, schema and message registries, so the only way to ship without it was to
* edit five registries by hand and hope nothing still referred to it. The
* removability gate proved that editing worked; nothing made it a decision you
* could express.
*
* Adding an entry here is what installs a feature. `VITE_PRODUCT_FEATURES` may
* then narrow the list at build time — a comma-separated subset, `none` for an
* empty selection, absent meaning "all of them". A narrowed-out feature reaches
* no registry, so it is not routed, not navigable and not callable.
*
* It is not deleted. The import above is static, and a value cannot undo a
* static import; making the import itself conditional on configuration is the
* thing §3.5 exists to prevent. FE-GATE-020 is what proves the feature can be
* physically removed, by removing it and rebuilding the whole project.
*/
const COMPILED_PRODUCT_FEATURES = Object.freeze([
REFERENCE_FEATURE_CONTRACT,
] as const);
/**
* Every feature this source tree declares, selected or not. The platform
* overview reports on this set so an operator can tell a feature that was built
* out from one that never existed.
*/
export const COMPILED_PRODUCT_FEATURE_IDS: readonly string[] = Object.freeze(
COMPILED_PRODUCT_FEATURES.map((feature) => feature.featureId),
);
/**
* `import.meta.env` exists in a Vite build and not under Node, and this module
* is read by release scripts as well as by the app. A missing environment means
* "nothing was narrowed", which is the same answer a plain developer build
* gives.
*/
function declaredFeatureSelection(): string | undefined {
const environment = (
import.meta as unknown as {
env?: Readonly<Record<string, string | undefined>>;
}
).env;
return environment?.["VITE_PRODUCT_FEATURES"];
}
export const INSTALLED_PRODUCT_FEATURES = selectCompiledProductFeatures(
COMPILED_PRODUCT_FEATURES as readonly SelectableProductFeature[],
declaredFeatureSelection(),
) as readonly (typeof COMPILED_PRODUCT_FEATURES)[number][];
export const INSTALLED_PRODUCT_FEATURE_IDS: readonly string[] = Object.freeze(
INSTALLED_PRODUCT_FEATURES.map((feature) => feature.featureId),
);