feat: establish versioned UI translation ledger foundation - #929
feat: establish versioned UI translation ledger foundation#929seonghobae wants to merge 194 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough8개 로케일 번역 원장과 읽기 경로를 추가했습니다. 인증된 HTTP 조회, 캐시 검증과 폴백, 게시 후 변경 차단, TRUNCATE 가드, 롤백 경로, 기준선 문서와 검증 테스트도 갱신했습니다. ChangesUI 번역 원장
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🔵 Low · up to This change adds a versioned translation ledger and authenticated read API. Runtime behavior is covered by the supplied contract evidence, but release-status documentation checks can still miss contradictory status text and should be tightened before relying on them for readiness claims. Sequence Diagram(s)sequenceDiagram
participant AuthenticatedClient
participant FastAPI
participant translation_ledger
participant PostgreSQL
participant Valkey
AuthenticatedClient->>FastAPI: GET /api/translations/{screen_key}
FastAPI->>translation_ledger: read_ui_translations(...)
translation_ledger->>Valkey: exact-version cache candidate
Valkey-->>translation_ledger: cache payload or miss
translation_ledger->>PostgreSQL: validate digest / fetch projection
PostgreSQL-->>translation_ledger: TranslationScreen
translation_ledger-->>FastAPI: immutable translations
FastAPI-->>AuthenticatedClient: 200 / 404 / 409 / 422
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
seonghobae
left a comment
There was a problem hiding this comment.
Valid finding: the explicit-version Valkey fast path can make a structurally valid but incomplete cache payload authoritative. _decode_cached_screen() validates identity and nonblank values, but it has no independent knowledge of the published resource's required key set. A payload for the exact product/screen/version/locale containing only title therefore bypasses PostgreSQL and returns an incomplete screen, contradicting this PR's fail-closed completeness contract and its statement that PostgreSQL remains authoritative. Add a realistic RED for a correct-identity/partial-key cache payload, then make cache acceptance depend on authoritative published key-set evidence (without cross-locale fallback).
|
Review finding on current head |
|
Review finding on |
seonghobae
left a comment
There was a problem hiding this comment.
Application/DB identity semantics are not actually aligned yet. ADR 0362 says leading/trailing-whitespace product_key and screen_key forms are rejected consistently at PostgreSQL and the application boundary, and migration 0246 rejects them with btrim equality. _validate_identity_segment() instead strips caller input and accepts it, so lineageweave aliases lineageweave before DB/cache lookup. That silently normalizes an ambiguous external identity while the persisted aggregate forbids that spelling. Add a RED covering padded product/screen inputs failing before I/O, then make the application boundary reject rather than normalize.
Record current protected main, ready/draft split, pending hosted jobs, and the leftover-pair a11y single-writer wait without claiming independent review or terminal GREEN.
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head product diff. Coverage is a separate gate.
Changed files
backend/app/main.py— API and service runtimebackend/app/translation_ledger.py— API and service runtimedocs/adr/0362-versioned-ui-translation-ledger.md— operator or user guidancedocs/product-technical-gap-baseline-history-2026-09-04.md— operator or user guidancedocs/product-technical-gap-baseline-history-2026-09-04.raw.txt— operator or user guidancedocs/product-technical-gap-baseline.md— operator or user guidancelineageweave/observability.py— Python module behaviormigrations/0246_ui_translation_ledger.sql— repository behaviormigrations/0247_ui_translation_truncate_guard.sql— repository behaviormigrations/rollback/0246_ui_translation_ledger.sql— repository behaviormigrations/rollback/0247_ui_translation_truncate_guard.sql— repository behaviorpyproject.toml— repository behaviortests/test_translation_api_driver_boundary.py— regression suitetests/test_translation_api_http.py— regression suitetests/test_translation_cache_recursion_real_payload.py— regression suitetests/test_translation_cache_timeout.py— regression suitetests/test_translation_documentation_alignment.py— regression suitetests/test_translation_exact_version_query_budget.py— regression suitetests/test_translation_ledger_cache_duplicate_members.py— regression suitetests/test_translation_ledger_cache_recursion.py— regression suitetests/test_translation_ledger_cache_surrogate.py— regression suitetests/test_translation_ledger_cache_version_type.py— regression suitetests/test_translation_ledger_contract.py— regression suitetests/test_translation_ledger_identity_type.py— regression suitetests/test_translation_ledger_postgres.py— regression suitetests/test_translation_ledger_read_model.py— regression suitetests/test_translation_ledger_resource_version.py— regression suitetests/test_translation_ledger_rollback.py— regression suitetests/test_translation_ledger_rollback_locale_guard.py— regression suitetests/test_translation_ledger_rollback_lock_contract.py— regression suitetests/test_translation_ledger_truncate_guard.py— regression suitetests/test_translation_ledger_truncate_publication_lock_contract.py— regression suitetests/test_translation_ledger_truncate_publication_race.py— regression suitetests/test_translation_ledger_unicode_whitespace.py— regression suitetests/test_translation_screen_value_object.py— regression suitetests/test_translation_wire_evidence_contract.py— regression suiteuv.lock— repository behavior
Changed behavior
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Backend: main.py (2 files)"]
S1 --> I1["API and service runtime"]
I1 --> R1["Review risk: Backend: main.py (2 files)"]
R1 --> V1["backend tests"]
Evidence --> S2["Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
R2 --> V2["docs review"]
Evidence --> S3["Python: observability.py"]
S3 --> I3["Python module behavior"]
I3 --> R3["Review risk: Python: observability.py"]
R3 --> V3["pytest plus coverage"]
Evidence --> S4["Repository file: 0246_ui_translation_ledger.sql"]
S4 --> I4["repository behavior"]
I4 --> R4["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R4 --> V4["required checks"]
Evidence --> S5["Repository file: 0247_ui_translation_truncate_guard.sql"]
S5 --> I5["repository behavior"]
I5 --> R5["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R5 --> V5["required checks"]
Evidence --> S6["Repository file: 0246_ui_translation_ledger.sql"]
S6 --> I6["repository behavior"]
I6 --> R6["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R6 --> V6["required checks"]
Evidence --> S7["Repository file: 0247_ui_translation_truncate_guard.sql"]
S7 --> I7["repository behavior"]
I7 --> R7["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R7 --> V7["required checks"]
Evidence --> S8["Repository file: pyproject.toml"]
S8 --> I8["repository behavior"]
I8 --> R8["Review risk: Repository file: pyproject.toml"]
R8 --> V8["required checks"]
Evidence --> S9["Test: test_translation_api_driver_boundary.py (24 files)"]
S9 --> I9["regression suite"]
I9 --> R9["Review risk: Test: test_translation_api_driver_boundary.py (24 files)"]
R9 --> V9["targeted test run"]
Evidence --> S10["Repository file: uv.lock"]
S10 --> I10["repository behavior"]
I10 --> R10["Review risk: Repository file: uv.lock"]
R10 --> V10["required checks"]
Findings
No source-backed product finding is synthesized from the coverage gate. A coverage miss belongs in the status comment.
- Head SHA:
0f4fd26a5f0fcf26932d0945188aefb2143d6605 - Workflow run: 34124343793
- Workflow attempt: 1
- Coverage gate:
failure
Review outcome
Coverage is a gate, not the review. This body reviews the changed product files.
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Backend: main.py (2 files)"]
S1 --> I1["API and service runtime"]
I1 --> R1["Review risk: Backend: main.py (2 files)"]
R1 --> V1["backend tests"]
Evidence --> S2["Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
R2 --> V2["docs review"]
Evidence --> S3["Python: observability.py"]
S3 --> I3["Python module behavior"]
I3 --> R3["Review risk: Python: observability.py"]
R3 --> V3["pytest plus coverage"]
Evidence --> S4["Repository file: 0246_ui_translation_ledger.sql"]
S4 --> I4["repository behavior"]
I4 --> R4["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R4 --> V4["required checks"]
Evidence --> S5["Repository file: 0247_ui_translation_truncate_guard.sql"]
S5 --> I5["repository behavior"]
I5 --> R5["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R5 --> V5["required checks"]
Evidence --> S6["Repository file: 0246_ui_translation_ledger.sql"]
S6 --> I6["repository behavior"]
I6 --> R6["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R6 --> V6["required checks"]
Evidence --> S7["Repository file: 0247_ui_translation_truncate_guard.sql"]
S7 --> I7["repository behavior"]
I7 --> R7["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R7 --> V7["required checks"]
Evidence --> S8["Repository file: pyproject.toml"]
S8 --> I8["repository behavior"]
I8 --> R8["Review risk: Repository file: pyproject.toml"]
R8 --> V8["required checks"]
Evidence --> S9["Test: test_translation_api_driver_boundary.py (24 files)"]
S9 --> I9["regression suite"]
I9 --> R9["Review risk: Test: test_translation_api_driver_boundary.py (24 files)"]
R9 --> V9["targeted test run"]
Evidence --> S10["Repository file: uv.lock"]
S10 --> I10["repository behavior"]
I10 --> R10["Review risk: Repository file: uv.lock"]
R10 --> V10["required checks"]
OpenCode Review Overview
Coverage evidence did not pass, so approval is blocked. The formal pull-request review is the source-backed diff review, not this status comment. |
Record the current Ready-head checks, stacked Ask evidence, and the frontend coverage fail-closed result without claiming merge or release.
Keep the 100% product-source gate; do not treat pending hosted jobs or the prior story-inclusive 82% failure as current-head acceptance.
Signed-off-by: Codex <codex@localhost>
seonghobae
left a comment
There was a problem hiding this comment.
Fresh authority correction: the branch is no longer at body-stated 0f4fd26...; current exact head is 603a7e1498a21f61bf4482746834bcca0bcfe1f2. The six-commit suffix from 0f4fd26... is docs-only and changes only docs/product-technical-gap-baseline.md (+22/-10 net compare), so the translation-ledger product/runtime tree is unchanged, but predecessor hosted results still do not transfer. On 603a7e..., frontend lint/test/build/Storybook, PROV-O, Ontology Pages, and SAST are terminal GREEN; the PostgreSQL full-suite job is terminal FAIL, Security remains fail-closed, and CodeQL remains fail-closed. No exact-head approval is claimed. Direct descendant #932 currently contains the older 0f4fd26... parent via 8c662a055...; the base branch has subsequently advanced through 1551f9e..., b05dec68..., and 603a7e.... Because both parent and child edit the large gap baseline, this is a real descendant-convergence finding rather than a base-label-only change; do not force/rebase or discard either side's evidence.
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head product diff. Coverage is a separate gate.
Changed files
backend/app/main.py— API and service runtimebackend/app/translation_ledger.py— API and service runtimedocs/adr/0362-versioned-ui-translation-ledger.md— operator or user guidancedocs/product-technical-gap-baseline-history-2026-09-04.md— operator or user guidancedocs/product-technical-gap-baseline-history-2026-09-04.raw.txt— operator or user guidancedocs/product-technical-gap-baseline.md— operator or user guidancelineageweave/observability.py— Python module behaviormigrations/0246_ui_translation_ledger.sql— repository behaviormigrations/0247_ui_translation_truncate_guard.sql— repository behaviormigrations/rollback/0246_ui_translation_ledger.sql— repository behaviormigrations/rollback/0247_ui_translation_truncate_guard.sql— repository behaviorpyproject.toml— repository behaviortests/test_translation_api_driver_boundary.py— regression suitetests/test_translation_api_http.py— regression suitetests/test_translation_cache_recursion_real_payload.py— regression suitetests/test_translation_cache_timeout.py— regression suitetests/test_translation_documentation_alignment.py— regression suitetests/test_translation_exact_version_query_budget.py— regression suitetests/test_translation_ledger_cache_duplicate_members.py— regression suitetests/test_translation_ledger_cache_recursion.py— regression suitetests/test_translation_ledger_cache_surrogate.py— regression suitetests/test_translation_ledger_cache_version_type.py— regression suitetests/test_translation_ledger_contract.py— regression suitetests/test_translation_ledger_identity_type.py— regression suitetests/test_translation_ledger_postgres.py— regression suitetests/test_translation_ledger_read_model.py— regression suitetests/test_translation_ledger_resource_version.py— regression suitetests/test_translation_ledger_rollback.py— regression suitetests/test_translation_ledger_rollback_locale_guard.py— regression suitetests/test_translation_ledger_rollback_lock_contract.py— regression suitetests/test_translation_ledger_truncate_guard.py— regression suitetests/test_translation_ledger_truncate_publication_lock_contract.py— regression suitetests/test_translation_ledger_truncate_publication_race.py— regression suitetests/test_translation_ledger_unicode_whitespace.py— regression suitetests/test_translation_screen_value_object.py— regression suitetests/test_translation_wire_evidence_contract.py— regression suiteuv.lock— repository behavior
Changed behavior
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Backend: main.py (2 files)"]
S1 --> I1["API and service runtime"]
I1 --> R1["Review risk: Backend: main.py (2 files)"]
R1 --> V1["backend tests"]
Evidence --> S2["Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
R2 --> V2["docs review"]
Evidence --> S3["Python: observability.py"]
S3 --> I3["Python module behavior"]
I3 --> R3["Review risk: Python: observability.py"]
R3 --> V3["pytest plus coverage"]
Evidence --> S4["Repository file: 0246_ui_translation_ledger.sql"]
S4 --> I4["repository behavior"]
I4 --> R4["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R4 --> V4["required checks"]
Evidence --> S5["Repository file: 0247_ui_translation_truncate_guard.sql"]
S5 --> I5["repository behavior"]
I5 --> R5["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R5 --> V5["required checks"]
Evidence --> S6["Repository file: 0246_ui_translation_ledger.sql"]
S6 --> I6["repository behavior"]
I6 --> R6["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R6 --> V6["required checks"]
Evidence --> S7["Repository file: 0247_ui_translation_truncate_guard.sql"]
S7 --> I7["repository behavior"]
I7 --> R7["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R7 --> V7["required checks"]
Evidence --> S8["Repository file: pyproject.toml"]
S8 --> I8["repository behavior"]
I8 --> R8["Review risk: Repository file: pyproject.toml"]
R8 --> V8["required checks"]
Evidence --> S9["Test: test_translation_api_driver_boundary.py (24 files)"]
S9 --> I9["regression suite"]
I9 --> R9["Review risk: Test: test_translation_api_driver_boundary.py (24 files)"]
R9 --> V9["targeted test run"]
Evidence --> S10["Repository file: uv.lock"]
S10 --> I10["repository behavior"]
I10 --> R10["Review risk: Repository file: uv.lock"]
R10 --> V10["required checks"]
Findings
No source-backed product finding is synthesized from the coverage gate. A coverage miss belongs in the status comment.
- Head SHA:
603a7e1498a21f61bf4482746834bcca0bcfe1f2 - Workflow run: 34209207594
- Workflow attempt: 1
- Coverage gate:
failure
Review outcome
Coverage is a gate, not the review. This body reviews the changed product files.
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Backend: main.py (2 files)"]
S1 --> I1["API and service runtime"]
I1 --> R1["Review risk: Backend: main.py (2 files)"]
R1 --> V1["backend tests"]
Evidence --> S2["Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs: 0362-versioned-ui-translation-ledger.md (4 files)"]
R2 --> V2["docs review"]
Evidence --> S3["Python: observability.py"]
S3 --> I3["Python module behavior"]
I3 --> R3["Review risk: Python: observability.py"]
R3 --> V3["pytest plus coverage"]
Evidence --> S4["Repository file: 0246_ui_translation_ledger.sql"]
S4 --> I4["repository behavior"]
I4 --> R4["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R4 --> V4["required checks"]
Evidence --> S5["Repository file: 0247_ui_translation_truncate_guard.sql"]
S5 --> I5["repository behavior"]
I5 --> R5["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R5 --> V5["required checks"]
Evidence --> S6["Repository file: 0246_ui_translation_ledger.sql"]
S6 --> I6["repository behavior"]
I6 --> R6["Review risk: Repository file: 0246_ui_translation_ledger.sql"]
R6 --> V6["required checks"]
Evidence --> S7["Repository file: 0247_ui_translation_truncate_guard.sql"]
S7 --> I7["repository behavior"]
I7 --> R7["Review risk: Repository file: 0247_ui_translation_truncate_guard.sql"]
R7 --> V7["required checks"]
Evidence --> S8["Repository file: pyproject.toml"]
S8 --> I8["repository behavior"]
I8 --> R8["Review risk: Repository file: pyproject.toml"]
R8 --> V8["required checks"]
Evidence --> S9["Test: test_translation_api_driver_boundary.py (24 files)"]
S9 --> I9["regression suite"]
I9 --> R9["Review risk: Test: test_translation_api_driver_boundary.py (24 files)"]
R9 --> V9["targeted test run"]
Evidence --> S10["Repository file: uv.lock"]
S10 --> I10["repository behavior"]
I10 --> R10["Review risk: Repository file: uv.lock"]
R10 --> V10["required checks"]
Scope / owner boundary
PostgreSQL-authoritative versioned UI translation ledger for #922 plus the first authenticated translation API slice. LineageWeave owns product UI copy/presentation read-model behavior only; ontology/concept labels and semantic truth remain with canonical owners. Product contract:
ko/en/ja/zh/vi/es/de/fr, exact screen-key completeness, immutable published versions, no cross-locale fallback, product/screen/version/locale cache identity.Current exact authority
main@83eba56149eb802cd63642c507c324c9976ec78e603a7e1498a21f61bf4482746834bcca0bcfe1f20f4fd26a5f0fcf26932d0945188aefb2143d6605: ahead 6 / behind 0; all six later commits are documentation-only and the net file delta is onlydocs/product-technical-gap-baseline.md(+22/-10). Runtime/API/DB ledger source did not change, but predecessor hosted results still do not transfer.Product/runtime boundary retained
Ledger rollback/publication/TRUNCATE, immutable projection, cache-integrity, typed validation, optional-cache timeout, independent real-wire recursion evidence, and exact-version cache query-budget repairs remain adopted. Valid cache hits require PostgreSQL digest/key-set admission without transferring the complete localized projection; true misses use one complete authoritative PostgreSQL projection after bounded Valkey I/O. Synthetic
RecursionErrorinjection remains supplemental only.GET /api/translations/{screen_key}is authenticated. Unsupported locale/malformed identity, missing resource, and incomplete requested-locale copy remain distinct request/read-model outcomes. No ontology/concept-label authority is moved into this presentation ledger.Fresh exact-head validation
On exact
603a7e...:Run full test suite against PostgreSQL; the available job metadata does not establish a source or infrastructure RCA, so no cause is invented and no unchanged rerun is treated as repair;Historical
0f4fd26...checks remain causal/history evidence only. Do not self-approve, synthesize status, weaken required gates, or substitute successful scanners for failing required workflows.Descendant convergence finding
Direct descendant #932 is still Draft/conflicting and currently carries #929 only through historical parent
0f4fd26...in child head8c662a055de51aff055c587f424849b610f72863. The parent has since advanced to603a7e.... The parent suffix and #932 both modifydocs/product-technical-gap-baseline.md, and #932's prior conflict resolution intentionally preserved child authorization/browser/full-suite evidence. Therefore this is not a base-label-only update: #932 must be reconstructed losslessly with ordinary ancestry, retaining the parent queue/coverage evidence and all child evidence. No force/rebase, ours/theirs wholesale replacement, or close is authorized.Remaining #922 buyer work
The ledger/API is foundation, not material-screen completion. A real Customer Master screen still needs one complete published eight-locale resource with reviewed product copy and current authenticated PostgreSQL/API acceptance. Normal/loading/empty/error/permission/responsive states, keyboard/focus/screen-reader behavior, CJK/text expansion/font fallback, desktop/mobile evidence, current all-page performance evidence where applicable, and PRD/TRD/ARCHITECTURE/UX/OPERABILITY/TEST_STRATEGY/CHANGELOG/baseline convergence remain required.
Keep Ready only as validation admission. Normal merge/release requires a repaired exact head with terminal required/security GREEN and qualifying independent review. Stacked #932 remains Draft until it is losslessly converged and independently validated.