DongHyeonkaandClaude Opus 5 344a163d84 fix: 탐색 주제 필터가 slug 를 보내고, 편집기를 넓혀 두 칸이 함께 스크롤한다
주제 필터를 걸면 「조건에 맞는 공개 기록이 없습니다」만 남았다. 선택지가
`<option>{이름}</option>` 이라 값이 없어 이름이 그대로 나갔고 — `topic=OAuth/OIDC 인증
경계` — API 는 slug 로 거르므로 0건을 돌려줬다. 프로젝트 선택지는 처음부터
`value={slug}` 였고, 그래서 프로젝트만 멀쩡했다. 주제도 같은 모양으로 맞춘다.

테스트가 이 결함을 통과시킨 이유는 픽스처의 주제 이름이 `JPA`, `Authentication` 처럼
slug 와 구분되지 않는 값이어서다. 이름과 slug 가 다른 값을 쓰는 운영에서만 드러났다.
선택지의 값이 slug 인지 직접 묻는 단언을 넣는다.

`RecordFilters.topic` 은 어댑터마다 뜻이 달랐다. 정적 어댑터는 이름으로, HTTP 어댑터는
그 값을 그대로 API 에 넘겨 slug 로 걸렀다. 프로젝트가 이미 slug/제목 둘 다 받는 것과
같이 주제도 둘 다 받게 해서 두 어댑터가 같은 값을 이해하게 한다. 주제 페이지도 이름
대신 경로의 slug 로 묻는다.

「전체」를 고른 칸은 조건이 아니다. 빈 값까지 실어 보내고 있었고, URL 이 지저분해질 뿐
아니라 이 값을 그대로 API 에 넘기는 화면에서는 `topic=` 이 "slug 가 빈 문자열인 주제"로
해석되어 0건이 된다.

편집기는 미리보기를 붙박이로 두고 자체 스크롤을 줬다. 편집기를 내려도 미리보기는
제자리였고, 보려면 그 안을 따로 굴려야 했다 — 나란히 둔 이유가 둘을 같이 보는 것인데
움직임이 갈라지면 그 이점이 없다. 둘 다 페이지 스크롤을 그대로 타게 한다.

폭도 넓힌다. `.studio-main` 은 모든 Studio 화면이 1180px 를 함께 쓰는데, 본문 두 벌이
들어가야 하는 이 화면에서는 한 칸이 566px 였다. 편집기가 놓인 경우에만 1600px 로
넓히고 헤더도 같이 넓혀 좌우 끝을 맞춘다. 다른 화면은 그대로다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189NzCryfeqDzS81EWidnBx
2026-08-26 17:30:11 +09:00

Tech Log Frontend

Initialized from clean-architecture-frontend-template revision 4dc033cf33a5b6173bbf960d5eb464a406dc4c92. The exact source identity is recorded in template.lock.json.

A React/Vite TechLog application where architecture boundaries, integration behavior, release coherence, accessibility, performance, and operations are executable contracts rather than conventions.

Start locally

Requirements: the exact Node.js version in .nvmrc (currently 24.14.0) and Corepack. The repository pins pnpm in package.json.

Product source, tests, build/quality scripts, and supported tool configuration are TypeScript/TSX. allowJs is disabled. Node-side .ts scripts run directly on the pinned Node 24 runtime and are checked with NodeNext resolution plus erasable-syntax enforcement. Project-owned executable source contains no JavaScript-family files; negative architecture, security, and type-compatibility fixtures are TypeScript/TSX as well.

corepack pnpm install --frozen-lockfile
corepack pnpm dev

Runtime-public settings live in public/config.json and are validated before the product tree mounts. Client secrets are forbidden.

TechLog experience

The default build mounts the source-faithful TechLog Public and Studio experience. Public routes provide discovery, search, documents, topics, projects, releases, and profile content. /studio provides the session-scoped mock authoring workflow: create, edit, validate, preview, publish, unpublish, and immutable publication history.

The 27 canonical route definitions are divided into PUBLIC and STUDIO nested layouts. Studio authentication remains deliberately deferred; its mock gateway state lasts for one Studio shell session and resets on a full document load. The exact route, dependency, stylesheet, asset, and parity inventory is recorded in docs/operations/techlog-ui-migration-baseline.md.

corepack pnpm build emits a self-contained dist/server.mjs production boundary. corepack pnpm preview --host 127.0.0.1 --port 4174 serves known Public and Studio routes as SPA documents, preserves the in-shell Studio 404, and returns source-exact raw 404 text/plain responses for missing Public content. With the read-only source temp-copy server on 4375, run:

TECH_LOG_SOURCE_URL=http://127.0.0.1:4375 \
TECH_LOG_TARGET_URL=http://127.0.0.1:4174 \
corepack pnpm verify:tech-log-source-parity

Studio backend source

TECH_LOG_STUDIO_SOURCE (MOCK | HTTP) selects which StudioGateway adapter the composition root wires up. It defaults to MOCK — the session-scoped in-memory Studio described above — so the existing Studio workflow and its test suites are unaffected unless the switch is deliberately turned on. Setting it to HTTP wires the HTTP StudioGateway instead, which calls the canonical @tech-log/studio-contract operations against API_BASE_URL. With no backend reachable at that URL, Studio still boots and its shell renders; the specific panels that need the backend show an inline "failed to load" state rather than a blank screen or an unhandled exception.

The switch is a field on the versioned runtime config document (RuntimeConfigV2), not a build-time flag:

  • config/runtime/{local,development,staging,production}.json are the deployment profiles corepack pnpm build (via scripts/generate-runtime-config.ts) materializes into dist/config.json for a real build.
  • corepack pnpm dev does not run that step. Plain vite serves public/config.json (and public/release-manifest.json) verbatim as dev fixtures — editing config/runtime/local.json alone has no effect on pnpm dev. To exercise HTTP mode under pnpm dev, set TECH_LOG_STUDIO_SOURCE in public/config.json directly.

The dev release manifest must declare the compiled contract set

Contract-set verification runs unconditionally at boot, before any adapter is selected. It is not an HTTP-mode caveat: if public/release-manifest.json's contractSet does not match the set the build compiled, corepack pnpm dev does not start the app at all — it renders the fail-closed boot screen (CONTRACT_SET_MISMATCH / CONTRACT_SET_PACKAGE_MISSING) in the default MOCK mode too. A developer who runs pnpm dev and gets a boot error has a broken dev server, however tidy the screen looks; treat it as a defect in the fixture, never as expected behaviour.

The expectation comes from EXPECTED_CONTRACT_SET_PACKAGES (src/features/installed-contract-contributions.ts), and a real build writes it into dist/release-manifest.json from scripts/generate-contract-set.ts, so production builds are always self-consistent. Only the hand-maintained dev fixture can drift, and it drifts whenever either half moves — a regenerated contract (new package digest or version) or a contribution added to or removed from installed-contract-contributions.ts. Two things keep it honest:

  • corepack pnpm generate:tech-log-contract refreshes the fixture's contractSet block as its last step, so regenerating the contract can never leave the two out of step. corepack pnpm generate:dev-release-manifest refreshes the same block on its own, for the contribution-list case that does not go through contract generation.
  • corepack pnpm check:dev-release-manifest is the gate. It compares the fixture's setAlgorithm, setDigest and package set against the compiled set and fails on any difference. It runs in CI as part of FE-GATE-010, which is what catches the changes the generation step cannot see.

TechLog contract generation

The Studio HTTP contract is vendored from a canonical OpenAPI source, not hand-written:

  • corepack pnpm generate:tech-log-contract regenerates src/features/tech-log/contracts/studio/studio-api.openapi.yaml, generated.ts, and canonical-source.json from the canonical tech-log-design-package repository (path from TECH_LOG_DESIGN_PACKAGE, default /home/donghyeon/workspace/tech-log-design-package). It needs that repository checked out locally and network access, because type generation runs in an isolated pnpm dlx sandbox (this repo pins TypeScript 7, which has no classic compiler API for openapi-typescript to use). Run it after the canonical contract changes, then commit the regenerated files.
  • corepack pnpm check:tech-log-contract is the drift gate: it hashes the vendored yaml against the recorded digest and confirms every recorded operationId is present in both the yaml and the generated types. It needs neither the canonical repository nor the network, so it runs in CI and in this sandbox. Run it any time to confirm the vendored contract has not drifted from what was last generated.

Architecture

Dependencies point inward:

presentation -> application -> domain
adapters -----^
bootstrap composes concrete adapters
contracts own cross-cutting registries

See docs/architecture/overview.md, docs/architecture/layers.md, and docs/architecture/starter-experience.md. TechLog is one feature boundary under src/features/tech-log. Its immutable Public catalog and session-scoped Studio mock gateway are injected through the application feature input; Public and Studio presentation code shares the typed content renderer without importing concrete adapters. The retained reference feature remains a non-product platform contract fixture and can be removed without changing the TechLog route set.

Platform capability review

The starter shell is implemented, but the repository review also records the remaining work required before feature teams can use every declared contract through one end-to-end application path:

These documents distinguish repository defaults from opt-in adapters and project-owned integrations. They are target designs and review findings; a capability is not treated as implemented until its branch acceptance criteria and executable gates pass.

Verification

Common local checks:

corepack pnpm lint
corepack pnpm check:types
corepack pnpm check:types:app
corepack pnpm check:types:node
corepack pnpm check:types:test
corepack pnpm check:architecture
corepack pnpm test:all
corepack pnpm test:e2e
corepack pnpm test:a11y
corepack pnpm build
corepack pnpm check:bundle
corepack pnpm test:performance
corepack pnpm verify:compatibility
corepack pnpm verify:release
corepack pnpm check:registries
corepack pnpm drill:runbooks
corepack pnpm check:ci
corepack pnpm exec vitest run tests/features/tech-log
corepack pnpm exec playwright test tests/e2e/tech-log-public-discovery.spec.ts tests/e2e/tech-log-studio-workflow.spec.ts tests/e2e/tech-log-responsive.spec.ts tests/e2e/tech-log-accessibility.spec.ts --project=chromium
corepack pnpm test:visual

check:types는 source, Node scripts/config와 tests를 분리된 TypeScript project로 모두 검사한다. type/architecture/security/registry의 invalid fixture는 config/ci/gates.json에서 “실패해야 통과”하는 negative gate로 실행된다. 도구 호환성 결정은 VD-01에 기록돼 있다.

Application feature input은 module augmentation으로 닫힌 ID와 정확한 input shape를 제공하며, 공통 Result<Value, Failure = AppFailure>는 error registry의 failure kind만 application/presentation 경계를 통과시킨다. Architecture gate는 TypeScript/TSX의 static, dynamic, type import를 별도 정적 그래프로 분석하고 runtime/source 영역의 JavaScript 재유입도 거절한다. 해석되지 않은 import, parse failure, 금지 계층 edge와 순환 의존은 모두 fail-closed이며 전용 TypeScript/TSX negative fixture로도 검증된다.

Install the pinned Playwright browser engines before the first cross-browser run:

corepack pnpm exec playwright install --with-deps chromium firefox webkit

Two gates intentionally need external evidence:

  • review:a11y-manual needs a signed human keyboard/focus/screen-reader review for all 27 registered routes: NOT_FOUND, TECH_LOG_CASE, TECH_LOG_EXPLORE, TECH_LOG_EXPLORE_KIND, TECH_LOG_HOME, TECH_LOG_PROFILE, TECH_LOG_PROJECT, TECH_LOG_PROJECTS, TECH_LOG_PROJECT_ACTIVITY, TECH_LOG_PROJECT_DECISIONS, TECH_LOG_PROJECT_RECORDS, TECH_LOG_QUESTION, TECH_LOG_REFERENCE, TECH_LOG_RELEASE, TECH_LOG_RELEASES, TECH_LOG_SEARCH, TECH_LOG_STUDIO_DOCUMENTS, TECH_LOG_STUDIO_DOCUMENT_EDIT, TECH_LOG_STUDIO_DOCUMENT_NEW, TECH_LOG_STUDIO_DOCUMENT_PREVIEW, TECH_LOG_STUDIO_DOCUMENT_PUBLISH, TECH_LOG_STUDIO_DOCUMENT_VALIDATION, TECH_LOG_STUDIO_HOME, TECH_LOG_STUDIO_NOT_FOUND, TECH_LOG_STUDIO_PUBLICATIONS, TECH_LOG_STUDIO_PUBLICATION_PREVIEW, TECH_LOG_TOPIC. verify:documentation derives that list from the route registry and fails if this paragraph falls behind it.
  • collect:web-vitals-evidence stays FAIL_UNVERIFIED until a reviewed minimum eligible-sample threshold and 28 days of production data exist.

Live release verification additionally requires HOSTING_BASE_URL.

CI and evidence

The 26-gate registry is config/ci/gates.json; the Gitea workflow is .gitea/workflows/quality-gates.yml. It follows:

MERGE_READY -> RELEASE_READY -> PROD_PROMOTION_READY -> FIELD_SLO_READY

DOCUMENTATION_READY is independent. No gate is downgraded to a warning. Machine-readable evidence is written below artifacts/; generated evidence is ignored by Git while .gitkeep files preserve the taxonomy.

Operational details are in docs/operations/, with incident procedures in docs/runbooks/.

S
Description
No description provided
Readme
18 MiB
Languages
TypeScript 97.7%
CSS 2.2%