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>
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
# Template merge — `main` → `feature/techlog-ui-migration`
|
||||
|
||||
Record of how the frontend template sync on `main` was integrated into the
|
||||
TechLog UI migration branch, and of every decision taken to resolve a conflict.
|
||||
|
||||
## Why this direction first
|
||||
|
||||
The template sync landed on `main` while the UI migration ran in a worktree.
|
||||
Two orders were possible.
|
||||
|
||||
Merging the feature branch into `main` first would have resolved 15 conflicts
|
||||
directly on the integration branch: a bad resolution would already be on `main`,
|
||||
and it would have to be repaired forward, on the branch other work depends on.
|
||||
|
||||
Merging `main` into the worktree first keeps the resolution where the UI work
|
||||
lives. Every gate runs against the resolved tree before anything reaches `main`,
|
||||
and a resolution that turns out wrong is discarded by resetting one feature
|
||||
branch. `main` is then only ever fast-forwarded, so it never holds a state that
|
||||
was not already proved in the worktree.
|
||||
|
||||
That is the order used here.
|
||||
|
||||
## Starting state
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| merge base | `325a2a0` |
|
||||
| `main` | `bdee07a` — `chore: sync the frontend template from a0fbafb to 5434760`, 1 commit, 101 files, +3116/−448 |
|
||||
| `feature/techlog-ui-migration` | `0355b64` — 29 commits |
|
||||
| files changed by `main` | 101 |
|
||||
| files changed by the feature branch | 423 |
|
||||
| overlap | 23 |
|
||||
|
||||
Rollback refs were created before touching anything:
|
||||
|
||||
```
|
||||
backup/ui-before-template-merge → 0355b64
|
||||
backup/main-before-ff → bdee07a
|
||||
```
|
||||
|
||||
### Pre-merge baseline on the feature branch
|
||||
|
||||
Captured so that a pre-existing failure could not be mistaken for a merge
|
||||
regression.
|
||||
|
||||
| Gate | Result |
|
||||
| --- | --- |
|
||||
| `check:types` | pass |
|
||||
| `lint` | pass |
|
||||
| `verify:documentation` | `PASS_SCOPED` |
|
||||
| `tests/unit` + `tests/component` | 1815 passed / 101 failed |
|
||||
|
||||
The 101 failures were confined to `tests/unit/ci-artifact-contract.test.ts` (19)
|
||||
and `tests/unit/ci-workflow-generation.test.ts` (82) — sandbox subprocess gates
|
||||
that cannot run in this environment.
|
||||
|
||||
## Conflicts and how each was decided
|
||||
|
||||
15 conflicts. The rule applied throughout: **keep the template's mechanism, keep
|
||||
the product's content, and never invent a third state that neither branch
|
||||
would accept.**
|
||||
|
||||
### Deletions the UI migration made deliberately (2)
|
||||
|
||||
| Path | Decision |
|
||||
| --- | --- |
|
||||
| `src/presentation/examples/platform-overview-page.tsx` | deletion kept |
|
||||
| `tests/visual/.../platform-overview-light-chromium-visual-linux.png` | deletion kept |
|
||||
|
||||
`main` modified both; the feature branch deleted them in `c5c8b94`. Nothing on
|
||||
the branch references either, so the deletion stands.
|
||||
|
||||
### `src/features/installed-feature-contracts.ts`
|
||||
|
||||
The template introduced a product manifest: registries are composed from
|
||||
`INSTALLED_PRODUCT_FEATURES` so that narrowing the selection withdraws a
|
||||
feature's routes, operations, schemas and messages.
|
||||
|
||||
The manifest composition is kept for operations, schemas, mappers and
|
||||
invalidation. The **route registry is deliberately not composed from
|
||||
`contract.routes`**, because the reference feature still declares
|
||||
`REFERENCE_RESOURCE_*` routes whose screens this product deleted during the
|
||||
migration. Reducing over them would register paths with no component behind
|
||||
them — a typed deep link resolving to nothing.
|
||||
|
||||
The registry therefore lists the platform routes (empty on this branch) and
|
||||
TechLog's. This was caught by running the gates: the first resolution did
|
||||
compose from `contract.routes`, and `tests/unit/product-features.test.ts`
|
||||
rejected it.
|
||||
|
||||
`ROUTE_FEATURE_OWNER` was narrowed to registered routes for the same reason.
|
||||
Attributing an unregistered route to a feature claims the kill switch governs
|
||||
something no router can mount.
|
||||
|
||||
### `src/features/installed-feature-runtimes.tsx`
|
||||
|
||||
The template gates the reference feature's route codecs and components on the
|
||||
manifest. This product deleted that feature's presentation layer, so the import
|
||||
does not resolve and there is nothing to gate. The gating was removed and the
|
||||
reason recorded in the file; it belongs back the day those screens return.
|
||||
|
||||
Consequence: this file no longer references the manifest, so it was moved to the
|
||||
exempt list in `tests/unit/product-features.test.ts` with the same note.
|
||||
`tests/component/product-feature-switch.test.tsx` was rewritten to hold the
|
||||
invariant that still applies here — no registered route without a component —
|
||||
which is exactly the trap the first resolution fell into.
|
||||
|
||||
### `src/features/installed-feature-adapters.ts`
|
||||
|
||||
Both the manifest check and TechLog's input are kept. The reference feature's
|
||||
input stays `Partial` because the manifest may narrow it out; TechLog's is total
|
||||
because it is this product's own UI and is always installed. A build that
|
||||
narrows the reference feature out still ships TechLog.
|
||||
|
||||
### `src/features/installed-feature-messages.ts`
|
||||
|
||||
The template's rationale — message keys stay total so `message()` cannot become
|
||||
partial — is kept, and TechLog's catalog is merged in on the same terms.
|
||||
|
||||
### `src/presentation/routes/app-router.tsx`
|
||||
|
||||
The template looked the route definition and runtime component up by id inside
|
||||
the route element; this branch passes both as props from the grouped route
|
||||
contract. The prop-driven signature is kept.
|
||||
|
||||
The template's **feature kill switch is adopted**: a route whose owning feature
|
||||
the runtime document disabled renders the disabled surface instead of mounting.
|
||||
Withdrawing it from navigation alone would leave a working deep link. `getRoute`
|
||||
was dropped from the imports because the definition arrives as a prop.
|
||||
|
||||
### `vite.config.ts`
|
||||
|
||||
Two independent additions — TechLog route chunking and `routerBasePath` — both
|
||||
kept. A brace was lost in the first concatenation and caught by `check:types`.
|
||||
|
||||
### `scripts/build-frontend.ts`
|
||||
|
||||
Both new steps exist in the merged body, so the header comment was renumbered:
|
||||
runtime config becomes step 4, the TechLog serving boundary step 7, the release
|
||||
manifest step 8, and the inline step comments were corrected to match.
|
||||
|
||||
### `scripts/test-performance.ts`
|
||||
|
||||
The template clicked a navigation link before measuring; this product measures
|
||||
its own landing route, which `goto` already reached, and has no `targetLabel`.
|
||||
The click was dropped. The template's lesson was kept as a comment because it
|
||||
applies to the next click that lands here: Playwright matches accessible names
|
||||
by substring, so a nav entry can also match a call to action and resolve to two
|
||||
links — a strict-mode violation that produces no performance evidence at all.
|
||||
|
||||
### Derived baselines — recomputed, not chosen (3)
|
||||
|
||||
`scripts/contracts/ci-gates.ts`, `tests/unit/task3-selective-integration.test.ts`
|
||||
and `tests/unit/ci-workflow-generation.test.ts` each pin counts and a digest
|
||||
describing the gate contract. **Neither side's numbers describe the merged
|
||||
`config/ci/gates.json`**, so taking either would have been wrong. They were
|
||||
recomputed from the merged file:
|
||||
|
||||
| Value | Result |
|
||||
| --- | --- |
|
||||
| canonical gate shape SHA-256 | `5063586d799f51de94c0f0ddaf9b75e180825bba5051bc309550425013ea81ef` |
|
||||
| gates | 27 |
|
||||
| commands | 82 |
|
||||
| command references | 94 |
|
||||
| evidence artifact references | 107 |
|
||||
| artifacts | 128 (126 product + 2 from the template) |
|
||||
|
||||
### `README.md`, `docs/accessibility/manual-checklist.md`
|
||||
|
||||
The template enumerated its own example routes. Replacing them with generic
|
||||
prose broke `verify:documentation`, which requires both documents to name every
|
||||
installed route id — a rule the template sync itself introduced. Both documents
|
||||
now enumerate this product's 27 routes.
|
||||
|
||||
## Result
|
||||
|
||||
Merge commit parents: `0355b64` (UI) and `bdee07a` (template).
|
||||
|
||||
### Gates on the merged tree
|
||||
|
||||
| Gate | Result |
|
||||
| --- | --- |
|
||||
| `check:types` | pass (6 projects) |
|
||||
| `lint` | pass, `--max-warnings=0` |
|
||||
| `check:architecture` | 399 modules, 1232 dependencies, 12 fixtures pass |
|
||||
| `check:adapter-inventory` | 120 files, 5 importers of the shared abort primitive |
|
||||
| `check:remediation-ledger` | 25 dispositions, 0 open |
|
||||
| `check:diagnostics` | 8 diagnostics, 5 telemetry producers |
|
||||
| `check:i18n` | 197 keys, 4 locales |
|
||||
| `check:design-system` | 48 tokens |
|
||||
| `verify:documentation` | `PASS_SCOPED` |
|
||||
| `test:integration` | 81 passed |
|
||||
| `test:recipes` | 17 passed |
|
||||
| `test:runtime-schema` | 40 passed |
|
||||
| `tests/unit` + `tests/component` | 1851 passed / 98 failed |
|
||||
|
||||
The 98 failures are the same two sandbox files as the baseline —
|
||||
`ci-workflow-generation` (82) and `ci-artifact-contract` (16, down from 19). No
|
||||
file fails that did not fail before the merge.
|
||||
|
||||
## Mistake made during this merge
|
||||
|
||||
`git stash` was run inside the worktree while the merge was still in progress,
|
||||
to compare a gate against the pre-merge tree. That removed `MERGE_HEAD`: the
|
||||
resolved content survived, but git no longer knew a merge was underway, and
|
||||
committing then would have produced a single-parent commit — leaving `main` off
|
||||
the ancestry and breaking the fast-forward that step 2 depends on. `MERGE_HEAD`
|
||||
was restored to `bdee07a` before committing, and the resulting commit has both
|
||||
parents.
|
||||
|
||||
To compare against a pre-merge state, use a separate checkout rather than
|
||||
stashing an in-progress merge.
|
||||
|
||||
## Follow-ups this merge deliberately did not decide
|
||||
|
||||
1. **TechLog is outside the product manifest.** It is composed directly rather
|
||||
than as a `SelectableProductFeature`, so the runtime kill switch does not
|
||||
govern it. Promoting it is a product decision.
|
||||
2. **The runtime kill switch currently governs no route.** The only installed
|
||||
feature's screens were removed, so the switch still gates operations and
|
||||
schemas but has no user-visible effect. It starts asserting again as soon as
|
||||
a feature-owned route is registered.
|
||||
3. **The reference feature declares routes it cannot serve.** Either its screens
|
||||
return, or its route declarations should be withdrawn from its contract.
|
||||
Reference in New Issue
Block a user