Both headers carried their own literal list of {label, path}. That made the
route contract and the header two sources for the same three facts — which
routes are navigable, what they are called, and in what order — with nothing
keeping them in step: a renamed route or a reordered menu could be right in one
place and stale in the other.
techLogNavigation(layoutGroup) derives the menu from TECH_LOG_ROUTE_REGISTRY,
where navigationOrder is what makes a route navigable. Output is byte-identical
to the previous literal lists, pinned by a new test.
Derived from TechLog's own route contract rather than from the composed
registries: .dependency-cruiser.json freezes an exact allowlist of files that
may read src/features/installed-*, explicitly so that coupling cannot spread,
and a feature header is not on it.
The studio header keeps its active-state rules — "작업본" stays highlighted
across /studio/documents/* except on the new-document screen — because that is
presentation behaviour the contract has no opinion about.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
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>