Skip to content

feat(composition): persist deployment activation authority - #437

Draft
seonghobae wants to merge 258 commits into
feat/product-composition-generation-registryfrom
feat/product-composition-activation-registry
Draft

seonghobae wants to merge 258 commits into
feat/product-composition-generation-registryfrom
feat/product-composition-activation-registry

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Scope

Advances #435 as the durable deployment activation/rollback/recovery and serving-currentness slice, stacked directly on #436 exact 30d89fa8f4ba95d7ddb84dde8e3b7e5faebf0343. Source head remains 8a67b5cef24332619b3a4466e3787a851c299215, open · Draft · mergeable=true, ordinary-forward 258 ahead / 0 behind that exact parent. Orgmetra HR domain truth remains with its bounded-context owners; this slice owns product-composition durable activation/evidence/recovery/currentness only.

Migration order remains 0018→0019→0020→0021→0022→0023→0024→0025. The final 0025 validators enforce owner-observation lifetime dominance for activation/recovery evidence. No PostgreSQL GREEN is inferred from source contracts.

recover_active_route_snapshot() remains the startup/reload acquisition path. current_route_ids_for_snapshot() remains the request-time DB linearization boundary for current activation, exact/latest recovery attestation, DB-owned recovery time, clock rewind and evidence expiry. Newer recovery/activation authority invalidates older serving decisions for subsequent requests; no deployment lock is held across owner HTTP I/O.

Descendant request/HTTP stack

#438 remains exact d642eb71c05ae3990349a26facf30593a5703914; #439 remains exact 2256112d09a7130cd361264eda8303b12d7ad027; #440 remains exact d8f161af80e2e9bcbd15a51c3d248798b839f4ce; current leaf #442 is exact cd908b65a8047d85e57cb82c0665185c201972b3, 2 ahead / 0 behind #440 with #440 exact as merge base.

#438 performs declaration-first concrete-before-template routing and consumes this PR's currentness boundary. #439 binds raw ASGI request-target evidence. #440 uses durable serviceability for dynamic 405 Allow, GET/HEAD route parity and HTTP problem responses. #442 adds one narrow adapter around the two request-routing currentness consumers:

  • RED ee642f086b8d08a14d1a7f9befef9ee490a1baa7 proves ActivationConflictError from this PR's currentness boundary otherwise escapes both a selected route and a resource-specific 405 candidate;
  • repair cd908b65a8047d85e57cb82c0665185c201972b3 translates only that typed superseded-state/conflict signal to CompositionRouteUnavailableError, so the established HTTP layer can emit its existing 503 representation;
  • ActivationAuthorizationError remains unwrapped. Descendants are not allowed to infer expiry/integrity/security meaning from exception text or to change this PR's durable currentness rules.

This preserves currentness ownership here while distinguishing a normal snapshot supersession from authorization/integrity failures at the consumer boundary. No descendant changes SQL/migration bytes.

Verification boundary

Keep Draft. Canonical acceptance must use one unchanged exact #442 cd908b65a8047d85e57cb82c0665185c201972b3 candidate so superseded-state classification, #440 HTTP/current-Allow/GET-HEAD semantics, #439 transport, #438 routing and the inherited #437 suite execute together with exact 100% owned statement/branch/docstring/edge coverage. #311 must execute the complete 0018→0025 inventory on the same candidate, including evidence-lifetime, DB-owned recovery time, optional-route acceptance, hostile search_path, 0020/0021 upgrade, recovery serialization and provenance/owner-drift roots. Predecessor workflow/review/status evidence does not transfer.

#433 source remains exact 6fc85e4d8bb273adb0d5866d87554067c749179d, ADR 0432 Proposed/Draft. Its source must eventually distinguish durable currentness, typed consumer-side currentness-failure classification, request-time 405 serviceability, GET/HEAD shared resource authority, and the still-missing successful-response owner-dispatch/lifecycle/performance boundary. PR-body currentization alone is not source currentization.

This slice is not buyer-ready composition serving. Positive acceptance still requires canonical exact-tree execution, immutable external identity/owner evidence, qualifying independent review, production DB-role/security/fault/recovery evidence, deployable authenticated owner dispatch, and full edge -> composition -> owner HTTP -> PostgreSQL k6/E2E at p95≤20 ms.

No force-push, destructive rebase, self-approval, routine administrator bypass, gate weakening, mutable sibling source, cross-service SQL, feature-local quality workflow, synthetic status, predecessor-GREEN transfer, migration-number theft, premature Ready/merge, or simple Close is authorized.

Refs #435 #436 #434 #433 #432 #438 #439 #440 #442 #260 #311 #100 #51. Keyverse owner dependency: ContextualWisdomLab/keyverse#158.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: f4accb46-1b7f-42fc-96a4-a7ead20062af

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc653e and a49701c.

📒 Files selected for processing (14)
  • services/product-composition-api/src/orgmetra_product_composition/activation.py
  • services/product-composition-api/src/orgmetra_product_composition/activation_authorization.py
  • services/product-composition-api/src/orgmetra_product_composition/activation_runtime_integrity.py
  • services/product-composition-api/src/orgmetra_product_composition/serving_snapshot.py
  • services/product-composition-api/tests/test_activation_authorization_evidence.py
  • services/product-composition-api/tests/test_activation_deployment_identity_use_boundary.py
  • services/product-composition-api/tests/test_activation_recovery_sequence.py
  • services/product-composition-api/tests/test_activation_route_snapshot_currentness.py
  • services/product-composition-api/tests/test_activation_route_snapshot_deployment_integrity.py
  • services/product-composition-api/tests/test_activation_route_snapshot_deployment_use_boundary.py
  • services/product-composition-api/tests/test_activation_route_snapshot_recovery_attestation_currentness.py
  • services/product-composition-api/tests/test_activation_runtime_use_integrity.py
  • services/product-composition-api/tests/test_activation_state_bound_route_snapshot.py
  • services/product-composition-api/tests/test_authorized_recovery_persistence.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/product-composition-api/tests/test_activation_runtime_use_integrity.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

제품 구성 활성화와 복구를 위한 PostgreSQL 레지스트리와 Python API를 추가했습니다. 인가 증거, 소유자 관측, 쓰기 직렬화와 소유권 출처를 검증합니다. 복구 증명 시퀀스에 연결된 경로 스냅샷은 사용 시 현재 상태를 다시 확인합니다.

Changes

활성화 권한과 복구 흐름

Layer / File(s) Summary
활성화 레지스트리와 인가 증거
database/migrations/0019_product_composition_activation_registry.sql, database/migrations/0020_product_composition_activation_authority_enforcement.sql, database/migrations/0021_product_composition_activation_observation_wall_clock.sql, services/product-composition-api/src/orgmetra_product_composition/activation.py, services/product-composition-api/src/orgmetra_product_composition/activation_authorization.py, services/product-composition-api/tests/*activation*, tests/test_product_composition_activation_*postgres.sh
배포, 활성화 이벤트, 인가 증거와 소유자 관측 테이블을 추가합니다. 이벤트 삽입 시 시퀀스, 전환 의도, 경로 관측 범위와 데이터베이스 시각 기준 신선도를 검증합니다. 레지스트리의 UPDATE, DELETE와 TRUNCATE를 거부합니다.
복구 증명과 데이터베이스 쓰기 보호
database/migrations/0022_product_composition_recovery_attestation.sql, database/migrations/0023_product_composition_deployment_write_serialization.sql, database/migrations/0024_product_composition_activation_trigger_function_provenance.sql, database/migrations/0025_product_composition_activation_relation_owner_provenance.sql, tests/test_product_composition_*postgres.sh, services/product-composition-api/tests/test_*migration.py
복구 증명 테이블과 순차 검증을 추가합니다. 활성화·복구 삽입은 배포 행 잠금으로 직렬화합니다. 마이그레이션은 트리거 함수의 스키마·소유자와 관련 관계의 소유자 일치를 확인합니다.
승인된 API와 서빙 스냅샷
services/product-composition-api/src/orgmetra_product_composition/activation.py, services/product-composition-api/src/orgmetra_product_composition/activation_authorization.py, services/product-composition-api/src/orgmetra_product_composition/activation_runtime_integrity.py, services/product-composition-api/src/orgmetra_product_composition/route_availability.py, services/product-composition-api/src/orgmetra_product_composition/serving_snapshot.py, services/product-composition-api/src/orgmetra_product_composition/__init__.py, services/product-composition-api/README.md, services/product-composition-api/tests/*
승인된 활성화·롤백·복구 API와 런타임 참조 검증을 추가합니다. 복구 결과에는 커밋된 복구 시퀀스가 포함됩니다. 경로 스냅샷 확인 시 발급 당시의 복구 증명과 현재 활성 상태를 다시 조회합니다.

Priority: ⬇️ Low

Estimated code review effort: 5 (Critical) | ~90 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AuthorizedPostgresActivationRegistry
  participant EvidenceProvider
  participant PostgresActivationRegistry
  participant PostgreSQL
  Caller->>AuthorizedPostgresActivationRegistry: activate 또는 rollback 호출
  AuthorizedPostgresActivationRegistry->>EvidenceProvider: 인가 증거 요청
  EvidenceProvider-->>AuthorizedPostgresActivationRegistry: ActivationAdmissionEvidence 반환
  AuthorizedPostgresActivationRegistry->>PostgresActivationRegistry: 증거 다이제스트와 writer 전달
  PostgresActivationRegistry->>PostgreSQL: 배포 행 잠금 및 증거·이벤트 저장
  PostgreSQL-->>PostgresActivationRegistry: 저장 결과 반환
  PostgresActivationRegistry-->>Caller: 승인된 전환 결과 반환
Loading

Merge Risk: ⚪ Minimal · up to a4970

This change adds durable activation, rollback, and recovery tracking, plus serving snapshots tied to specific recovery attestations. No blocking defect is established at the current head. Before relying on this for production serving, the owners should confirm two edge cases: reuse of a recovery sequence after a restore, and clock comparisons within the same millisecond. They should also run the PostgreSQL contract suite, which the change's own notes list as still pending.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.49% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 404 functions across 48 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed #260과 #436은 각각 남은 Foundation 증거 remediation과 이전 generation/configuration authority 작업에 직접 연결됩니다.
Out of Scope Changes check ✅ Passed 변경 사항은 product composition의 durable activation authority, recovery, write serialization, provenance, serving currentness 및 관련 테스트 범위에 일관되게 포함됩니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 배포 활성화 권한을 영속화하는 주요 변경을 정확하고 간결하게 설명합니다. 활성화·복구·서빙 currentness 세부 사항을 모두 포함하지 않지만, 전체 변경의 핵심과 직접 관련됩니다.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review
Please review current exact head e57195d1cb33c9289093e7bf0885c67f0ca80ff8 without transferring predecessor verdicts. Focus on the recovery chronology boundary introduced by RED 01096f4753fcf8fd511f0d1240e502012c6c60f8 and repair e57195d1cb33c9289093e7bf0885c67f0ca80ff8: whether final 0025 now makes recovered_at database-owned from the same clock_timestamp() sample used for recovery evidence/operation freshness, whether explicit caller timestamps can still weaken serving rewind detection, and whether this preserves recovery commit semantics and the existing 0018→0025 authority stack.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
head=e57195d1cb33c9289093e7bf0885c67f0ca80ff8
red=01096f4753fcf8fd511f0d1240e502012c6c60f8
printf 'HEAD '; git rev-parse HEAD
printf 'requested '; git rev-parse "$head^{commit}"
git show -s --format='%H %P %s' "$head"
git show -s --format='%H %P %s' "$red"
printf '%s\n' '== changed paths =='
git diff --name-status "$red^" "$head" | head -n 90
printf '%s\n' '== exact migration diff =='
git diff --unified=50 "$red^" "$head" -- database/migrations/0025_product_composition_activation_relation_owner_provenance.sql
printf '%s\n' '== 0025 chronology and authority checks =='
git show "$head:database/migrations/0025_product_composition_activation_relation_owner_provenance.sql" | nl -ba | sed -n '1,385p'
printf '%s\n' '== prior attestation schema and recovery SQL =='
git show "$head:database/migrations/0022_product_composition_recovery_attestation.sql" | nl -ba | sed -n '1,182p'
ast-grep outline services/product-composition-api/src/orgmetra_product_composition/activation.py --items all | head -n 95
git show "$head:services/product-composition-api/src/orgmetra_product_composition/activation.py" | nl -ba | sed -n '32,115p;300,415p'
printf '%s\n' '== chronology regression file names =='
git diff --name-only "$red^" "$head" | rg 'recovery|activation|migration|postgres|serving' | head -n 100

Length of output: 45241


🏁 Script executed:

#!/bin/bash
set -euo pipefail
head=e57195d1cb33c9289093e7bf0885c67f0ca80ff8
src=services/product-composition-api/src/orgmetra_product_composition
testfile=tests/test_product_composition_recovery_attestation_postgres.sh
printf '%s\n' '== regression diff =='
git diff --unified=25 01096f4753fcf8fd511f0d1240e502012c6c60f8^ "$head" -- "$testfile"
printf '%s\n' '== regression focused body =='
git show "$head:$testfile" | nl -ba | sed -n '1,310p'
printf '%s\n' '== serving query and chronology comparison =='
ast-grep outline "$src/serving_snapshot.py" --items all | head -n 85
git show "$head:$src/serving_snapshot.py" | nl -ba | sed -n '30,106p;345,482p'
printf '%s\n' '== recovery completion and post commit wrapper =='
git show "$head:$src/activation.py" | nl -ba | sed -n '393,451p'
git show "$head:$src/activation_runtime_integrity.py" | nl -ba | sed -n '265,345p'
printf '%s\n' '== 0024 binding and 0025 scope =='
git show "$head:database/migrations/0024_product_composition_activation_trigger_function_provenance.sql" | rg -n -C2 'validate_product_composition_recovery_attestation_insert|CREATE TRIGGER product_composition_recovery_attestation_insert_guard|owner|LOCK TABLE' | head -n 90
git diff --name-status 01096f4753fcf8fd511f0d1240e502012c6c60f8^ "$head" -- database/migrations

Length of output: 35439


@seonghobae Follow-up to my exact-head review: I inspected the PostgreSQL regression, serving query, and recovery return path. The chronology repair is sound, but two serving-currentness concerns remain in the current bytes.

  • In migration 0025, the BEFORE INSERT trigger replaces NEW.recovered_at with its own clock_timestamp() sample. It derives the evidence and owner-operation freshness checks from that same sample. An explicit caller timestamp cannot weaken this boundary. The PostgreSQL regression inserts a timestamp from 2000 and checks that the stored timestamp is recent.
  • The recovery write path is unchanged. After recover_active_authorized() returns, the product wrapper adds no clock, capability, or freshness check. The change does not introduce a post-commit false-failure path.
  • The 00180025 migration order remains unchanged. Migration 0025 retains its relation-owner check and replaces the existing public trigger function without recreating the binding established by 0024.

The repair does not close two previously identified serving gaps. In serving_snapshot.py Line 60 and Line 61, the query still truncates recovered_at and database time to milliseconds. The comparison at Line 470 can therefore accept a database time that precedes recovered_at within the same millisecond. Also, a restore can lose an attestation and permit reuse of its recovery sequence; the serving query can then match an old snapshot if the later attestation uses the same still-valid evidence digest.

I inspected source and regression definitions. I did not run Python tests or PostgreSQL contracts.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please focus this pass on exact head f530896ccd3b787e1601b247241621a5674eb4ee, especially serving_snapshot.py and the three serving-currentness/use-boundary test files. Verify that the materialized single PostgreSQL wall-clock sample, exact timestamptz recovery ordering, integer-ms evidence-expiry boundary, 10-field durable row validation, and checked-as-used deployment coordinates fail closed without introducing a second clock sample or post-commit ambiguity.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
head=f530896ccd3b787e1601b247241621a5674eb4ee
src=services/product-composition-api/src/orgmetra_product_composition
printf 'HEAD '; git rev-parse HEAD
printf 'requested '; git rev-parse "$head^{commit}"
git status --short
git show -s --format='%H %P %s' "$head"
printf '%s\n' '== changed files after preceding chronology fix =='
git diff --name-status e57195d1cb33c9289093e7bf0885c67f0ca80ff8 "$head"
printf '%s\n' '== serving test file discovery =='
fd -t f . services/product-composition-api/tests | rg 'test_activation_route_snapshot_(currentness|recovery_attestation_currentness|deployment_use_boundary)\.py$'
for file in "$src/serving_snapshot.py" services/product-composition-api/tests/test_activation_route_snapshot_currentness.py services/product-composition-api/tests/test_activation_route_snapshot_recovery_attestation_currentness.py services/product-composition-api/tests/test_activation_route_snapshot_deployment_use_boundary.py; do
 printf '\n== outline %s ==\n' "$file"; ast-grep outline "$file" --items all | head -n 115
done
printf '%s\n' '== focused diff =='
git diff --unified=25 e57195d1cb33c9289093e7bf0885c67f0ca80ff8 "$head" -- "$src/serving_snapshot.py" services/product-composition-api/tests/test_activation_route_snapshot_currentness.py services/product-composition-api/tests/test_activation_route_snapshot_recovery_attestation_currentness.py services/product-composition-api/tests/test_activation_route_snapshot_deployment_use_boundary.py | head -n 780
printf '%s\n' '== source SQL and request routing =='
nl -ba "$src/serving_snapshot.py" | sed -n '1,120p;265,505p'

Length of output: 46597


@seonghobae I reviewed exact head f530896ccd3b787e1601b247241621a5674eb4ee.

I found one blocking serving-currentness issue.

current_route_ids_for_snapshot() still cannot distinguish an issuing recovery attestation from a recreated attestation after restore.

  • The SELECT binds the attestation by recovery_sequence and evidence_bundle_sha256 in serving_snapshot.py Lines 71-74.
  • The snapshot stores those same two values.
  • A restore can remove the issuing attestation.
  • A later recovery can reuse the lost sequence and the same still-valid evidence digest.
  • The query then accepts the later attestation as if it issued the old snapshot.

The existing tests reject a missing attestation and a different recovery sequence. They do not cover reuse of both the original sequence and evidence digest after restore.

Bind each snapshot to a durable recovery issuance identifier that cannot be reused after restore. The serving query must require that identifier.

The requested current-head repair is otherwise sound.

  • serving_clock AS MATERIALIZED creates one PostgreSQL clock_timestamp() sample for the statement.
  • The query uses that sample for both the integer-millisecond expiry decision and the full-precision recovery ordering decision.
  • recovery_clock_rewound compares timestamptz values before millisecond truncation. This closes the sub-millisecond rewind gap.
  • The 10-field row contract validates every field, including the boolean ordering result.
  • The deployment coordinates and recovery evidence digest are captured before connection use and rechecked after connection use.
  • The post-I/O checks apply only to the read-only serving boundary. They do not add a fallible check after an activation or recovery write commit.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Descendant authority update: #438 remains exact d642eb71c05ae3990349a26facf30593a5703914; new Draft #439 is exact 2256112d09a7130cd361264eda8303b12d7ad027, 5 ahead / 0 behind #438 with #438 exact as merge base. #439 owns only ASGI raw request-target normalization before #438 selection/currentness and adds no SQL or migration bytes. Durable activation/recovery/serving-currentness remains owned here in #437; canonical PostgreSQL acceptance stays 0018 -> 0025 on the same exact #439 candidate used for package execution.

Copy link
Copy Markdown
Contributor Author

Currentness-owner follow-up finding from the #442 consumer review: current_route_ids_for_snapshot() still uses ActivationAuthorizationError for both ordinary evidence expiry (observed_at_unix_ms >= recovery_valid_until_unix_ms) and fail-closed integrity/security conditions such as clock rewind, invalid durable shape, deployment/snapshot retargeting and runtime-capability drift. The inherited currentness regression explicitly expects ActivationAuthorizationError for expiry.

That means descendants cannot safely turn expiry into an ordinary 503 by catching ActivationAuthorizationError, and they must not message-match "expired" to guess semantics. #442 therefore translates only the already-typed ActivationConflictError supersession path and deliberately leaves ActivationAuthorizationError untouched.

Before the deployable host can give a truthful external availability response for expired recovery evidence, this #437 owner should split expected serving-liveness expiry from integrity/authorization failures with a dedicated typed contract (name/shape to be decided here), keep DB wall-clock authority unchanged, and extend the #437 tests before descendants consume it. Do not patch this in #442 by source-copying currentness logic or string-classifying errors. This is an open repair finding, not a reason to close or bypass the stack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant