docs(proofline): capture issue 25 approved specification
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0006"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Installable Alembic migration payload와 exact inventory parity
|
||||
|
||||
## Criterion
|
||||
|
||||
`portfolio-hub`의 Alembic 설정, script template 및 모든 migration version은 wheel과 runtime image에 installable package data로 포함되어야 하며 runtime loader는 source checkout의 상위 경로에 의존하지 않고 `importlib.resources`로 이를 조회해야 한다. source, wheel 및 image의 canonical migration inventory는 non-empty이고 정확히 같아야 하며 migration 수를 영구 상수로 고정하지 않아야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: source checkout과 installed wheel/image에서 migration 경로 조회와 `pfhub migrate-db`가 가능하고, 세 inventory가 non-empty exact match다.
|
||||
- 실패·경계 조건: `alembic.ini`, script template 또는 migration 하나라도 누락·추가·이름 불일치하면 검증이 실패하며, 단순 package import 또는 `pfhub --help` 성공으로 migration payload를 대체하지 않는다.
|
||||
- 검사 방법: package-data 설정과 `app/db/migrations.py`를 검사하고 source↔wheel/image inventory regression 및 isolated PostgreSQL migration smoke를 실행한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0007"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Locked Python 3.11 non-root image와 deny-by-default context
|
||||
|
||||
## Criterion
|
||||
|
||||
Repository-root image build는 Python 3.11을 사용하고 repository-owned `uv.lock`에 고정된 runtime dependency를 `--locked --no-dev --no-editable` 경계로 설치해야 한다. 최종 image는 고정 non-root UID/GID로 실행되고 `.dockerignore`와 명시적 `COPY` allowlist가 source control metadata, local environment, credential, 운영 설정 및 불필요한 파일을 build context/layer에서 제외해야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: hosted/local verification과 runtime image가 Python 3.11·동일 lock resolution을 사용하고 기본 user가 non-root이며 필요한 package/runtime 파일만 image에 존재한다.
|
||||
- 실패·경계 조건: lock drift, broad `COPY .`, root default user, `.env`·key·token·local DB·VCS/worktree metadata의 context 포함은 계약 실패다.
|
||||
- 검사 방법: `uv lock --check`, Dockerfile/`.dockerignore` contract tests, image inspect의 UID/GID 및 synthetic canary scan을 실행한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0008"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Branch 검증과 requester-versioned publication authority 분리
|
||||
|
||||
## Criterion
|
||||
|
||||
일반 branch와 `main`은 registry write authority 없이 full pytest와 release/container contract를 실행해야 한다. GHCR publication은 exact `vX.Y.Z` Git tag push만 허용하고 malformed tag, prerelease tag, `workflow_dispatch`, branch/main/PR event에서는 registry login 또는 push를 수행하지 않아야 한다. Publication job만 최소 `packages: write` 권한을 가지며 automatic version 증가와 moving `latest`를 만들지 않아야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: branch/main verification workflow는 registry credential·write path 없이 회귀와 release contract를 검사하고 strict version-tag event만 publication path에 도달한다.
|
||||
- 실패·경계 조건: tag shape 또는 checked-out HEAD가 expected tag commit과 다르면 login/push 전에 실패하며, 다른 event가 publication 권한이나 write step을 얻으면 실패다.
|
||||
- 검사 방법: workflow event/permissions/step-order contract tests와 representative valid·invalid ref helper tests를 실행한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0009"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Build-once tested-artifact identity·integrity·provenance
|
||||
|
||||
## Criterion
|
||||
|
||||
Verify 단계는 exact source에서 image를 한 번만 build하고 tested image ID, full OCI source revision 및 exported archive SHA-256을 기록해야 한다. Publish 단계는 archive checksum·loaded image ID·revision을 재검증한 뒤 rebuild 없이 동일 image에 source commit selector와 requester version selector만 부여·push하고 두 selector가 동일 registry digest를 가리키는지 확인해야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: build→smoke→export→artifact handoff→load→tag→push가 하나의 tested image identity를 보존하고 source revision, archive checksum, selectors 및 registry digest가 추적 가능하다.
|
||||
- 실패·경계 조건: archive 손상, ID/revision 불일치, publish-side build, selector digest 불일치 또는 malformed/missing digest는 nonzero이며 성공 summary를 금지한다. Login/push 전 검증 실패는 registry write 전에 중단한다.
|
||||
- 검사 방법: release helper unit/contract tests에서 command ordering과 no-rebuild/no-login-on-precheck-failure를 확인하고 실제 local archive scan/load identity를 검증한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0010"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Installed payload와 DB-disabled HTTP liveness
|
||||
|
||||
## Criterion
|
||||
|
||||
검증된 image는 networkless·read-only·non-root 조건에서 `app` package import, `pfhub` CLI 및 uvicorn application entrypoint를 제공해야 한다. 외부 dependency와 DB 없이 API를 기동할 수 있고 `/health`는 HTTP 200과 `status=ok`, `service=portfolio-hub`, `db_enabled=false`의 정확한 shape를 반환해야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: installed image에서 public import/CLI/entrypoint가 실행되고 DB-disabled health payload가 계약과 일치한다.
|
||||
- 실패·경계 조건: source checkout bind mount에 의존하거나 help-only 검사로 HTTP liveness를 대체하거나 health payload가 누락·오형이면 실패다.
|
||||
- 검사 방법: built-image installed payload smoke와 DB-disabled startup/HTTP probe를 실행하며 기본 non-root/read-only 조건을 함께 확인한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0011"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Isolated PostgreSQL migration·readiness와 exact cleanup
|
||||
|
||||
## Criterion
|
||||
|
||||
Publication verification은 internal network의 disposable PostgreSQL, synthetic credential과 synthetic encryption key만 사용해 별도 pre-start `pfhub migrate-db`를 head까지 성공시킨 뒤 application을 시작해야 한다. Alembic revision, 별도 `pfhub health-check` DB ping, DB-enabled `/health` liveness를 각각 확인하고 성공·timeout·wrong-shape·canary/scan failure에서 실제 시도한 container, network 및 volume identity를 정확히 정리해야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: migration이 head에 도달하고 DB ping과 HTTP liveness가 각각 통과하며 생성·시도한 exact resource가 모두 cleanup된다.
|
||||
- 실패·경계 조건: production credential/DB 사용, application startup 중 암묵적 migration, bootstrap-user 비격리 side effect, readiness/liveness 혼합, timeout 또는 wrong-shaped payload 후 resource leak은 실패다.
|
||||
- 검사 방법: actual local image/PostgreSQL startup smoke, timeout/wrong-shape regressions, canary-bearing `docker create` cleanup regression과 exact attempted/removed identity assertions를 실행한다.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "ac-0012"
|
||||
status: active
|
||||
---
|
||||
|
||||
# Immutable operator handoff와 deployment exclusion
|
||||
|
||||
## Criterion
|
||||
|
||||
Container operations 문서는 게시 산출물의 immutable `image@sha256:<digest>` identity, source revision, 별도 pre-start `pfhub migrate-db`, runtime secret injection, read-only root filesystem, 명시적 writable mount를 정의해야 한다. Image rollback은 이전 recorded digest의 재선택이고 DB migration rollback·data recovery와 같지 않음을 명시하며 운영 deployment 기능·설정·실행 명령을 포함하지 않아야 한다.
|
||||
|
||||
## Verification
|
||||
|
||||
- 성공 조건: 운영자가 publication output에서 immutable artifact와 필수 runtime/pre-start 입력 경계를 식별할 수 있고 deployment·DB rollback은 별도 승인 범위로 명확히 분리된다.
|
||||
- 실패·경계 조건: moving tag를 rollback identity로 제시하거나 image rollback이 DB 상태를 되돌린다고 암시하거나 Compose/Kubernetes/Helm/systemd/Dockge 배포 구성·실행·재시작 명령을 포함하면 실패다.
|
||||
- 검사 방법: `docs/container-operations.md` contract test로 digest, migration, secret/read-only/writable mount, image≠DB rollback 및 deployment-command 부재를 검사한다.
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"next_line_number": 2,
|
||||
"next_ac_number": 6
|
||||
"next_line_number": 3,
|
||||
"next_ac_number": 13
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
---
|
||||
id: "dcy-0002"
|
||||
status: confirmed
|
||||
---
|
||||
|
||||
# Requester-versioned GHCR 이미지 게시 계약
|
||||
|
||||
## Problem
|
||||
|
||||
### 배경
|
||||
|
||||
`portfolio-hub`도 다른 TREX 서비스처럼 검증된 OCI image를 GHCR에 게시하고, 운영자가 immutable digest로 배포·롤백할 수 있어야 한다.
|
||||
|
||||
비교 대상의 현재 상태는 서로 다르다.
|
||||
|
||||
- `trex-core`: build-once, image ID, archive SHA-256, OCI revision, canary scan, no-rebuild publish, selector digest 검증은 강하지만 `main`과 `v*` push 모두 publication하며 runtime dependency 설치가 `uv.lock`에 묶이지 않는다.
|
||||
- `kis-data-hub`: built-container `/health` liveness와 tested archive handoff가 있으나 `main`도 publication하고 CI/runtime install이 unlocked이며 image ID/checksum/canary/non-root/same-digest 검증이 약하다.
|
||||
- `trex-mega-swing-v2`: strict `vX.Y.Z` tag-only, locked CI/runtime, build-once, installed/startup smoke, image ID/checksum/revision/canary, no-rebuild publish, commit/version selector same-digest, non-root, digest deploy/rollback 경계를 구현했고 실제 `v0.1.0` hosted run이 성공했다.
|
||||
|
||||
후속 hardening 이슈도 같은 방향을 가리킨다.
|
||||
|
||||
- `trex-core` #57: https://github.com/fourblocks2020/trex-core/issues/57
|
||||
- `kis-data-hub` #30: https://github.com/fourblocks2020/kis-data-hub/issues/30
|
||||
|
||||
두 이슈는 현재 OPEN proposal이며, 기존 방식을 공통 표준으로 유지하는 근거가 아니라 `trex-mega-swing-v2`형 requester-versioned publication으로 이관하려는 근거다.
|
||||
|
||||
### 문제 정의
|
||||
|
||||
일반 `main` push마다 image를 게시하면 문서·governance-only 변경도 새 registry artifact를 만들며 requester의 release/version 선택과 publication이 분리되지 않는다. 반대로 image build만 성공했다고 운영 가능한 artifact가 되는 것도 아니다.
|
||||
|
||||
`portfolio-hub`는 다음을 동시에 만족해야 한다.
|
||||
|
||||
1. publication authority가 명시적인 requester-selected version에만 귀속될 것
|
||||
2. repository verification과 runtime dependency가 동일한 lock resolution을 사용할 것
|
||||
3. verify job에서 시험한 exact image만 publish job으로 전달되고 rebuild되지 않을 것
|
||||
4. source commit, image ID, archive checksum, registry digest가 연결될 것
|
||||
5. secret·운영 설정이 build context, layer, history, archive, metadata에 포함되지 않을 것
|
||||
6. installed image에 API, CLI 및 모든 Alembic migration payload가 존재할 것
|
||||
7. HTTP liveness와 PostgreSQL readiness/migration 검증을 구분할 것
|
||||
8. image rollback과 DB migration rollback을 구분할 것
|
||||
|
||||
## Evidence
|
||||
|
||||
### 원문·승인·사후 문서화 경계
|
||||
|
||||
- 원문: https://github.com/fourblocks2020/portfolio-hub/issues/25
|
||||
- 원문 제목: `discovery: requester-versioned GHCR 이미지 게시 계약`
|
||||
- 원문 상태: `OPEN`
|
||||
- Discovery 승인 댓글: https://github.com/fourblocks2020/portfolio-hub/issues/25#issuecomment-5277036560
|
||||
- 승인 시각(KST): `2026-08-13T15:51:34+09:00`
|
||||
- 승인 선택: `OQ-1-A`, `OQ-2-B`, `OQ-3-A`, `OQ-4-A`, `OQ-5-A`, `OQ-6-A`
|
||||
- 요청자는 2026-08-13 이슈 #25 원문을 최대한 포함한 승인 상태의 Discovery와 승인된 REQ/AC를 새 Line에 사후 작성하도록 명시했다.
|
||||
- 이 Line은 이미 수행된 로컬 구현을 승인된 지속 사양으로 포착한다. #25 구현이 ProofLine 정식 시퀀스로 수행됐다는 주장, ProofLine delivery closure, push, publication, release 또는 deployment 완료를 소급 생성하지 않는다.
|
||||
|
||||
### 이슈 원문의 문서 성격
|
||||
|
||||
- 유형: **Discovery 제안**
|
||||
- 상태: **approved / requester confirmed**
|
||||
- 승인 시각(KST): `2026-08-13T15:51:34+09:00`
|
||||
- 승인 결정: `OQ-1-A`, `OQ-2-B`, `OQ-3-A`, `OQ-4-A`, `OQ-5-A`, `OQ-6-A`
|
||||
- 대상 저장소: `fourblocks2020/portfolio-hub`
|
||||
- 목적: `portfolio-hub`의 GHCR 운영 이미지 게시 방식을 결정하기 위한 문제·근거·경계·미결정 질문 정리
|
||||
|
||||
이 Discovery는 requester가 아래 OQ 선택으로 **승인했다**. 이 승인은 `portfolio-hub` 저장소 안의 후속 요구사항 작성과 bounded implementation 범위 제안을 허용하지만, PR 생성, Git tag 생성·push, GHCR publication, GitHub Release 생성, 운영 배포·재시작 또는 production DB migration 실행을 승인하지 않는다. 각 외부 write는 별도 requester 지시가 필요하다.
|
||||
|
||||
### 관찰된 `portfolio-hub` 현황
|
||||
|
||||
현재 `main` 기준:
|
||||
|
||||
- Python `>=3.11`, FastAPI/uvicorn HTTP service다.
|
||||
- `uv.lock`이 존재하며 `uv lock --check`가 통과한다.
|
||||
- `/health`는 HTTP 200 응답과 `status`, `service`, `version`, `checked_at`, `db_enabled`를 반환한다.
|
||||
- `pfhub` CLI에 `init-db`, `migrate-db`, `health-check`가 있다.
|
||||
- PostgreSQL과 Alembic을 사용하며 `migrations/versions`에 28개 migration이 있다.
|
||||
- DB-enabled startup은 `CREDENTIAL_ENCRYPTION_KEY`를 요구하며, 설정에 따라 initial admin user를 생성할 수 있다.
|
||||
- Dockerfile, deny-by-default `.dockerignore`, image publication workflow, release helper/tests, container operations 문서는 아직 없다.
|
||||
|
||||
#### 패키징 위험
|
||||
|
||||
현재 wheel을 실제 build해 inventory를 확인한 결과:
|
||||
|
||||
```text
|
||||
wheel: portfoliohub-0.1.0-py3-none-any.whl
|
||||
members: 61
|
||||
migration_members: 0
|
||||
alembic_ini: False
|
||||
pfhub_entrypoint: True
|
||||
```
|
||||
|
||||
현재 migration loader는 설치된 `app` 위치의 상위에서 `alembic.ini`와 `migrations/`를 찾는다. 따라서 `.venv`만 runtime stage로 옮기는 단순 multi-stage image는 API import와 `pfhub --help`는 통과하면서도 운영의 `pfhub migrate-db`가 실패할 수 있다.
|
||||
|
||||
이 문제는 첫 image publication 전에 fail-closed package/runtime contract로 해결해야 한다.
|
||||
|
||||
### 이슈 원문 근거
|
||||
|
||||
- `portfolio-hub/pyproject.toml`
|
||||
- `portfolio-hub/uv.lock`
|
||||
- `portfolio-hub/app/main.py`
|
||||
- `portfolio-hub/app/api/health.py`
|
||||
- `portfolio-hub/app/db/migrations.py`
|
||||
- `portfolio-hub/app/cli/commands/health.py`
|
||||
- `portfolio-hub/alembic.ini`
|
||||
- `portfolio-hub/migrations/versions/`
|
||||
- `trex-core/.github/workflows/publish-image.yml`
|
||||
- `trex-core/Dockerfile`
|
||||
- `kis-data-hub/.github/workflows/publish-image.yml`
|
||||
- `kis-data-hub/Dockerfile`
|
||||
- `kis-data-hub/docs/container-operations.md`
|
||||
- `trex-mega-swing-v2/.github/workflows/publish-image.yml`
|
||||
- `trex-mega-swing-v2/Dockerfile`
|
||||
- `trex-mega-swing-v2/scripts/image_release.py`
|
||||
- `trex-mega-swing-v2/tests/release/`
|
||||
- `trex-mega-swing-v2/docs/container-operations.md`
|
||||
- 성공한 `trex-mega-swing-v2 v0.1.0` publication run: https://github.com/fourblocks2020/trex-mega-swing-v2/actions/runs/31468042611
|
||||
|
||||
### 구현·검증 증거
|
||||
|
||||
- 구현 브랜치: `feat/25-ghcr-image-publication`
|
||||
- 구현 기준: `origin/main` `35e85981af845489236c18d709d0545c830e7f03`
|
||||
- 로컬 구현 커밋: `96260b6302e050c6e564f3eca4e723558f26246f`
|
||||
- 구현 진행 댓글: https://github.com/fourblocks2020/portfolio-hub/issues/25#issuecomment-5277080186
|
||||
- 구현 완료 댓글: https://github.com/fourblocks2020/portfolio-hub/issues/25#issuecomment-5277346458
|
||||
- 최종 증거: release tests `23 passed`; 전체 pytest `621 passed, 6 skipped`; `uv lock --check`, scoped Ruff, `git diff --check`, 실제 Python 3.11 image build/canary scan, installed-image/PostgreSQL startup smoke 통과.
|
||||
- 독립 최초 리뷰의 MAJOR 1건과 MINOR 1건은 수정·회귀검증됐고 독립 재리뷰 PASS에서 모두 CLOSED됐다.
|
||||
- 두 댓글의 exact body는 이 Line의 `evidence/implementation-progress.md`와 `evidence/implementation-complete.md`에 비정식 증적으로 보존한다.
|
||||
|
||||
## Scope
|
||||
|
||||
### 승인된 방향
|
||||
|
||||
publication·공급망 계약은 `trex-mega-swing-v2`를 기준으로 채택하고, smoke와 payload 검증은 `portfolio-hub`에 맞춘다.
|
||||
|
||||
#### 공통 publication 계약 후보
|
||||
|
||||
- strict `vX.Y.Z` Git tag push만 publication event로 허용
|
||||
- `main`, 일반 branch, PR, `workflow_dispatch`, malformed/prerelease tag에서 registry write 금지
|
||||
- 자동 version 증가 및 moving `latest` 금지
|
||||
- exact tagged commit checkout 및 checked-out HEAD 일치 검증
|
||||
- pinned `uv` + `uv sync --extra dev --locked` 검증
|
||||
- runtime도 repository-owned `uv.lock`으로 `--locked --no-dev --no-editable` 설치
|
||||
- full pytest와 새 release helper/contract test에 대한 정직한 lint gate
|
||||
- source image를 한 번만 build
|
||||
- OCI full revision, tested image ID, exported archive SHA-256 기록
|
||||
- synthetic canary를 image filesystem, history, inspect metadata, archive 및 helper output에서 검사
|
||||
- publish job은 checksum/image ID/revision을 확인한 동일 archive를 load하고 rebuild 없이 게시
|
||||
- 다음 두 selector만 부여하고 동일 registry digest를 검증
|
||||
- `ghcr.io/fourblocks2020/portfolio-hub:<source-commit-first-12>`
|
||||
- `ghcr.io/fourblocks2020/portfolio-hub:vX.Y.Z`
|
||||
- 실제 deploy/rollback identity는 `ghcr.io/fourblocks2020/portfolio-hub@sha256:<digest>`
|
||||
- 기본 runtime user는 non-root 고정 UID/GID, 운영 root filesystem은 read-only, 필요한 경로만 명시적으로 writable mount
|
||||
|
||||
#### `portfolio-hub` 전용 verification 후보
|
||||
|
||||
1. **Installed payload smoke**
|
||||
- networkless/read-only/non-root container에서 `python -I -c 'import app'`, `pfhub --help`, uvicorn entrypoint 확인
|
||||
2. **DB-disabled HTTP liveness**
|
||||
- 외부 dependency 없이 API를 기동하고 `/health`의 `status=ok`, `service=portfolio-hub`, `db_enabled=false` 확인
|
||||
3. **Migration payload exact inventory**
|
||||
- source의 canonical migration 목록과 wheel/image 안의 목록이 non-empty이고 정확히 같은지 확인
|
||||
- `alembic.ini` 및 resource lookup 계약 확인
|
||||
4. **Isolated PostgreSQL migration/readiness smoke**
|
||||
- internal network의 synthetic PostgreSQL과 synthetic encryption key만 사용
|
||||
- migration을 head까지 적용하고 Alembic revision 확인
|
||||
- DB-enabled API 기동 후 `/health` liveness와 `pfhub health-check` DB ping을 별도로 확인
|
||||
- production credential/DB 접속 금지, bootstrap-user side effect 억제 또는 격리 DB로 제한
|
||||
- container/network/volume cleanup까지 PASS 조건에 포함
|
||||
|
||||
### 원문 범위 후보 — In scope
|
||||
|
||||
#### In scope
|
||||
|
||||
- Dockerfile 및 deny-by-default `.dockerignore`
|
||||
- strict tag-only GHCR publication workflow
|
||||
- image release helper와 contract/regression tests
|
||||
- locked verification/runtime dependency 설치
|
||||
- installed payload·HTTP·PostgreSQL/Alembic smoke
|
||||
- migration resource/package layout의 최소 수정
|
||||
- immutable digest 중심의 container operations 문서
|
||||
- secret, non-root, read-only filesystem, writable mount, migration/rollback 경계
|
||||
|
||||
### 확정 결정(OQ) — 원문 전체
|
||||
|
||||
> **승인 기록:** requester가 각 OQ에서 하나씩 선택했으며, 아래 `[x]` 항목이 승인된 scope authority다. 미선택 항목은 구현 범위가 아니다.
|
||||
|
||||
#### OQ-1 — migration payload 위치 (확정)
|
||||
|
||||
어떤 방식을 canonical runtime contract로 채택할 것인가?
|
||||
|
||||
- [x] **OQ-1-A (승인):** migration과 Alembic 설정을 installable package data로 포함하고 `importlib.resources` 기반으로 조회
|
||||
- [ ] **OQ-1-B:** runtime image의 고정 경로에 `alembic.ini`와 `migrations/`를 명시적으로 복사하고 loader가 그 경로를 사용
|
||||
|
||||
결정 근거: **A**는 wheel/image payload parity를 검증하기 쉽고 source checkout 경로에 덜 의존한다. 단, Alembic script layout과 CLI 호환성을 prototype으로 검증해야 한다.
|
||||
|
||||
#### OQ-2 — publication smoke의 DB 깊이 (확정)
|
||||
|
||||
- [ ] **OQ-2-A:** DB-disabled `/health`만 publication gate로 두고 PostgreSQL migration은 별도 CI에서 검증
|
||||
- [x] **OQ-2-B (승인):** publication gate 안에서 isolated PostgreSQL migration + DB ping까지 필수로 검증
|
||||
|
||||
결정 근거: **B**. 이 서비스의 운영 가능성은 migration payload와 DB 연결에 의존하며 `/health`는 DB ping을 수행하지 않는다.
|
||||
|
||||
#### OQ-3 — migration과 application startup 순서 (확정)
|
||||
|
||||
다음 운영 순서를 계약으로 고정할 것인가?
|
||||
|
||||
1. 별도 pre-start `pfhub migrate-db`
|
||||
2. 성공 후 application container start
|
||||
3. `/health` liveness와 별도 DB readiness 확인
|
||||
|
||||
- [x] **OQ-3-A (승인):** 위 pre-start migration 순서를 운영 계약으로 고정
|
||||
- [ ] **OQ-3-B:** application startup 과정에서 migration을 수행
|
||||
|
||||
결정 근거: **A**는 application startup이 production schema mutation을 암묵적으로 수행하지 않게 한다.
|
||||
|
||||
#### OQ-4 — runtime entrypoint (확정)
|
||||
|
||||
- [x] **OQ-4-A (승인):** image 기본 command를 `uvicorn app.main:app ...`로 명시하고 shutdown/worker/root-path/forwarded-header 계약을 operations 문서와 smoke에 고정
|
||||
- [ ] **OQ-4-B:** 명시적인 `portfolio-hub` server CLI/entrypoint를 추가하고 이를 image 기본 command로 사용
|
||||
|
||||
결정 근거: 첫 범위에서는 **A**로 publication 구축 범위를 제한한다. 별도 server CLI는 독립 필요성이 확인될 때 추가한다.
|
||||
|
||||
#### OQ-5 — Python minor baseline (확정)
|
||||
|
||||
- [x] **OQ-5-A (승인):** 현재 저장소 계약과 일치하도록 image와 hosted verification을 Python 3.11로 고정
|
||||
- [ ] **OQ-5-B:** 이 범위에서 Python 3.12로 함께 승격
|
||||
|
||||
결정 근거: **A**. runtime 승격을 publication 구축에 묶지 않는다.
|
||||
|
||||
#### OQ-6 — branch CI와 publication 분리 (확정)
|
||||
|
||||
Tag-only publication과 별개로 일반 branch/main에서 어떤 검증을 수행할 것인가?
|
||||
|
||||
- [x] **OQ-6-A (승인):** branch/main에서 registry write 없이 full pytest와 release/container contract 검증을 수행하고, strict version tag workflow만 `packages: write`와 publication을 수행
|
||||
- [ ] **OQ-6-B:** branch/main에서는 기존 repository 검증만 수행하고 release/container contract와 publication 검증은 strict version tag workflow에서만 수행
|
||||
|
||||
결정 근거: **A**는 registry write 권한을 tag publication에만 제한하면서 publication 결함을 tag push 전에 발견할 수 있다.
|
||||
|
||||
### 승인 결정 요약
|
||||
|
||||
| OQ | 승인 선택 | 확정 내용 |
|
||||
|---|---|---|
|
||||
| OQ-1 | A | migration/Alembic 설정을 installable package data로 포함하고 `importlib.resources`로 조회 |
|
||||
| OQ-2 | B | publication gate에서 isolated PostgreSQL migration과 DB ping을 필수 검증 |
|
||||
| OQ-3 | A | `pfhub migrate-db` 성공 후 application을 시작하는 pre-start migration 순서 |
|
||||
| OQ-4 | A | 첫 범위에서는 명시적인 uvicorn command 사용, 운영 파라미터를 문서·smoke에 고정 |
|
||||
| OQ-5 | A | Python 3.11 고정 |
|
||||
| OQ-6 | A | branch/main은 registry write 없이 full pytest와 release/container contract 검증, strict version tag만 publication |
|
||||
|
||||
### Discovery 결정 게이트
|
||||
|
||||
다음 결정 게이트는 requester의 OQ 선택 및 본 Discovery 승인으로 확정되었다.
|
||||
|
||||
- [x] OQ-1 migration payload/resource layout
|
||||
- [x] OQ-2 PostgreSQL publication smoke 깊이
|
||||
- [x] OQ-3 pre-start migration 운영 순서
|
||||
- [x] OQ-4 runtime entrypoint
|
||||
- [x] OQ-5 Python minor baseline
|
||||
- [x] OQ-6 branch CI와 publication workflow 분리 방식
|
||||
- [x] image name 및 selector 규칙
|
||||
- [x] production writable mount 및 secret injection 경계
|
||||
- [x] 구현이 tag push/publication/deployment를 자동 승인하지 않는다는 외부-write 경계
|
||||
|
||||
### Discovery 성공 조건
|
||||
|
||||
다음 산출이 완료되면 요구사항/implementation scope를 제안할 수 있다.
|
||||
|
||||
- [x] 비교 대상 세 서비스의 현재 방식과 목표 방식 차이가 위 근거와 일치한다.
|
||||
- [x] `portfolio-hub`의 migration payload 실패 seam을 재현 가능한 contract로 정의한다.
|
||||
- [x] OQ-1~OQ-6이 requester 결정으로 해소된다.
|
||||
- [x] HTTP liveness, DB readiness, schema migration, publication, deployment의 상태가 서로 분리된다.
|
||||
- [x] secret·non-root·read-only·cleanup 및 immutable rollback 경계가 확정된다.
|
||||
- [ ] 후속 요구사항에서 구현 AC를 source/lock → build → smoke → artifact handoff → publish → operator handoff 전체에 추적한다.
|
||||
|
||||
### Acceptance admission
|
||||
|
||||
기존 active AC는 포트폴리오 ledger 목록 집계 계약으로 이 publication capability와 의미가 다르므로 `update` 또는 `satisfy` 대상으로 사용하지 않는다. 이 Line은 다음 일곱 가지 version-independent behavior를 새 AC로 `create`한다.
|
||||
|
||||
- installable Alembic migration resource와 exact inventory parity
|
||||
- Python 3.11 locked non-root image와 deny-by-default build context
|
||||
- branch 검증과 strict version-tag publication authority 분리
|
||||
- build-once tested-artifact identity·integrity·provenance
|
||||
- installed payload와 DB-disabled HTTP liveness
|
||||
- isolated PostgreSQL migration·readiness와 exact cleanup
|
||||
- immutable operator handoff와 deployment exclusion
|
||||
|
||||
## Out of Scope
|
||||
|
||||
### 원문 범위 후보 — Out of scope
|
||||
|
||||
#### Out of scope
|
||||
|
||||
- 이 Discovery에서 실제 version 결정 또는 tag push
|
||||
- GHCR image publication 및 package visibility 변경
|
||||
- GitHub Release 생성
|
||||
- 운영 서버/Dockge/Compose 배포 또는 재시작
|
||||
- production credential·DB 사용
|
||||
- production DB migration, downgrade, data repair, reset 또는 backup 실행
|
||||
- application API/domain behavior 변경
|
||||
- deployment orchestrator 선정·구축
|
||||
- unrelated repository-wide lint debt 정리
|
||||
- `trex-core` #57 또는 `kis-data-hub` #30 구현
|
||||
|
||||
### 사후 ProofLine 경계
|
||||
|
||||
- 이 사후 사양 포착은 구현이 ProofLine 정식 시퀀스로 수행됐다는 주장이나 implementation/delivery/release/deployment lifecycle closure를 만들지 않는다.
|
||||
- 구현 진행·완료 댓글은 canonical lifecycle artifact가 아니라 출처가 명시된 비정식 evidence다.
|
||||
- 후속 승인 사양은 `req-0002`와 active `ac-0006`~`ac-0012`로 추적한다. 이는 원문의 미완료 성공 조건인 source/lock → build → smoke → artifact handoff → publish → operator handoff AC 추적을 사후 충족한다.
|
||||
- 기능 브랜치 push, PR, Git tag 생성·push, GHCR publication, GitHub Release, 운영 배포·재시작 및 production DB migration은 별도 requester 승인 없이는 수행하지 않는다.
|
||||
@@ -0,0 +1,50 @@
|
||||
# 구현 완료 — GitHub Issue #25 비정식 증적
|
||||
|
||||
> 이 문서는 ProofLine canonical lifecycle artifact가 아니다. #25 구현 당시 GitHub 댓글을 출처와 함께 원문 보존한 비정식 증적이다.
|
||||
> 이 증거는 로컬 구현·검증 상태를 기록하지만 ProofLine 정식 implementation chronology, delivery closure, push, publication, release 또는 deployment 완료를 소급 증명하지 않는다.
|
||||
> Source: https://github.com/fourblocks2020/portfolio-hub/issues/25#issuecomment-5277346458
|
||||
> Comment ID: `IC_kwDOR4l8PM8AAAABOo3qmg`
|
||||
> Author: `genichin`
|
||||
> Created: `2026-08-13T07:27:30Z`
|
||||
> Original body SHA-256: `e71adc00bfec74b8802134a154876e6325e051e8b10d8d3fff79f51aa48042d6`
|
||||
|
||||
---
|
||||
|
||||
## 구현 완료
|
||||
|
||||
`portfolio-hub` #25에서 승인한 requester-versioned GHCR 이미지 게시 기능을 구현하고 로컬 검증과 독립 리뷰까지 완료했습니다.
|
||||
|
||||
### 구현 내용
|
||||
|
||||
- Alembic 설정과 migration 28개를 installable package data로 이전하고 `importlib.resources` 기반 조회 및 source↔wheel/image inventory parity를 구현했습니다.
|
||||
- Python 3.11·`uv.lock` 기반 multi-stage non-root(`10001:10001`) Docker image와 deny-by-default `.dockerignore`를 추가했습니다.
|
||||
- branch/main 검증과 registry publication을 분리하고, strict `vX.Y.Z` tag push만 GHCR 게시 권한을 갖도록 구성했습니다.
|
||||
- build-once tested artifact handoff, image ID, archive SHA-256, OCI revision, secret canary, no-rebuild publish, commit/version selector same-digest 검증을 구현했습니다.
|
||||
- installed payload, DB-disabled `/health`, isolated PostgreSQL migration·DB ping, DB-enabled `/health` 및 exact-resource cleanup smoke를 구현했습니다.
|
||||
- 게시 산출물의 digest identity, pre-start `pfhub migrate-db`, secret/read-only/writable mount 및 image rollback≠DB rollback 경계를 문서화했습니다. 운영 배포 기능·설정·실행은 포함하지 않았습니다.
|
||||
- 로컬 커밋 `96260b6302e050c6e564f3eca4e723558f26246f`(`feat(release): add verified image publication pipeline`)으로 구현 스냅샷을 고정했습니다.
|
||||
|
||||
### 검증 결과
|
||||
|
||||
최종 스냅샷에서 코드·패키지·이미지·DB smoke와 독립 재리뷰가 모두 통과했습니다.
|
||||
|
||||
- Release focused tests: **23 passed**
|
||||
- 전체 pytest: **621 passed, 6 skipped**
|
||||
- `uv lock --check`: 통과
|
||||
- Scoped Ruff: 통과
|
||||
- `git diff --check`: 통과
|
||||
- 실제 Python 3.11 local image build 및 canary scan: 통과
|
||||
- 실제 installed-image/PostgreSQL startup smoke: 통과
|
||||
- 이미지 UID/GID `10001:10001`, OCI revision과 기준 SHA 일치, migration 28개 및 최신 `0028` 포함 확인
|
||||
- 최초 독립 리뷰 FAIL의 MAJOR 1건·MINOR 1건을 수정하고 영구 회귀 테스트를 추가함
|
||||
- 독립 재리뷰: **PASS**, 기존 발견 모두 CLOSED, 새 회귀·배포 범위 확장 없음
|
||||
- `.proofline` 변경 없음
|
||||
|
||||
### 비차단 후속 권고
|
||||
|
||||
- 원격 반영이 필요할 때 별도 requester 지시에 따라 기능 브랜치 push와 PR 생성 여부를 결정합니다.
|
||||
- 원격 통합 후 branch/main hosted CI 결과를 exact commit 기준으로 확인합니다.
|
||||
- 실제 `vX.Y.Z` tag push와 GHCR publication은 별도 명시 승인 후 수행하고, exact tag/SHA workflow 및 registry digest를 확인합니다.
|
||||
- 운영 배포·재시작·production DB migration은 계속 별도 범위와 별도 승인으로 유지합니다.
|
||||
|
||||
> 현재 상태는 **로컬 구현·검증·커밋 완료**입니다. 푸시·PR·이미지 게시·배포·릴리스 완료를 의미하지 않습니다.
|
||||
@@ -0,0 +1,53 @@
|
||||
# 구현 진행 상황 — GitHub Issue #25 비정식 증적
|
||||
|
||||
> 이 문서는 ProofLine canonical lifecycle artifact가 아니다. #25 구현 당시 GitHub 댓글을 출처와 함께 원문 보존한 비정식 증적이다.
|
||||
> 이 증거는 로컬 구현·검증 상태를 기록하지만 ProofLine 정식 implementation chronology, delivery closure, push, publication, release 또는 deployment 완료를 소급 증명하지 않는다.
|
||||
> Source: https://github.com/fourblocks2020/portfolio-hub/issues/25#issuecomment-5277080186
|
||||
> Comment ID: `IC_kwDOR4l8PM8AAAABOonaeg`
|
||||
> Author: `genichin`
|
||||
> Created: `2026-08-13T06:57:02Z`
|
||||
> Original body SHA-256: `1b616d14c9f05a75da1345ef58b7a69a05b1bf2e62b79063476490b758e06bd0`
|
||||
|
||||
---
|
||||
|
||||
## 구현 진행 상황
|
||||
|
||||
- **대상:** discovery: requester-versioned GHCR 이미지 게시 계약
|
||||
- **브랜치:** `feat/25-ghcr-image-publication`
|
||||
- **기준:** `origin/main` (`35e85981af845489236c18d709d0545c830e7f03`)
|
||||
- **상태:** 구현·검증·독립 리뷰 완료 — 로컬 커밋 완료, push/PR/publication/배포 미수행
|
||||
- **최종 업데이트:** `2026-08-13T16:23:22+09:00`
|
||||
|
||||
### 구현 계획 및 작업 목록
|
||||
|
||||
- [x] migration/Alembic resource를 installable package data로 전환하고 source↔wheel inventory parity를 검증한다.
|
||||
- [x] Python 3.11·`uv.lock` 기반 multi-stage non-root Docker image와 deny-by-default build context를 구현한다.
|
||||
- [x] strict `vX.Y.Z` tag-only, build-once, tested-artifact handoff, no-rebuild GHCR publication workflow를 구현한다.
|
||||
- [x] image ID·archive SHA-256·OCI revision·canary·commit/version selector same-digest fail-closed helper와 contract tests를 구현한다.
|
||||
- [x] installed payload, DB-disabled `/health`, isolated PostgreSQL migration + DB ping smoke와 cleanup 검증을 구현한다.
|
||||
- [x] branch/main 무게시 검증 CI와 tag publication 권한 경계를 구현한다.
|
||||
- [x] 게시된 image digest와 pre-start `pfhub migrate-db`, secret/read-only/writable mount의 operator handoff 경계를 문서화한다(배포 구현 제외).
|
||||
- [x] focused release tests와 전체 pytest, lock/diff hygiene 및 실제 local image smoke를 통과한다.
|
||||
- [x] 독립 코드 리뷰 결과를 반영하고 최종 작업 상태를 기록한다.
|
||||
|
||||
### 진행 기록
|
||||
|
||||
- `2026-08-13T15:56:37+09:00` — 승인된 OQ(`1-A`, `2-B`, `3-A`, `4-A`, `5-A`, `6-A`)를 구현 범위로 고정했다.
|
||||
- `2026-08-13T15:56:37+09:00` — 최신 원격 `origin/main` `35e85981af845489236c18d709d0545c830e7f03`에서 격리 worktree와 기능 브랜치를 생성했다.
|
||||
- PR 생성, tag push, 실제 GHCR publication, GitHub Release 및 운영 배포는 이 구현 실행 범위에 포함하지 않는다.
|
||||
- `2026-08-13T15:58:13+09:00` — requester 확인에 따라 운영 배포 기능·배포 설정·배포 실행을 명시적으로 제외했다. digest 관련 문서는 게시 산출물의 operator handoff identity와 image rollback 경계 설명에만 한정한다.
|
||||
- `2026-08-13T16:05:01+09:00` — release focused tests `22 passed`, scoped Ruff 통과.
|
||||
- `2026-08-13T16:06:34+09:00` — migration 경로 변경에 따른 기존 테스트를 정합화한 뒤 전체 pytest 통과.
|
||||
- `2026-08-13T16:07:00+09:00` — Python 3.11 locked non-root 로컬 이미지 build 성공.
|
||||
- `2026-08-13T16:07:26+09:00` — 구현 runner가 600초 제한으로 timeout 종료했다. 변경은 worktree에 보존되었고, 마지막 installed inventory/image smoke는 중단되어 미검증 상태다. 다음 담당은 팀장이며 diff 검토, 배포 문서 범위 보정, 실제 image smoke와 독립 리뷰를 계속한다.
|
||||
- `2026-08-13T16:08:49+09:00` — 보존된 로컬 이미지에서 installed inventory, DB-disabled `/health`, isolated PostgreSQL migration·DB ping, DB-enabled `/health`, non-root, cleanup smoke를 재실행해 exit 0 확인.
|
||||
- `2026-08-13T16:10:50+09:00` — 배포 범위를 넘던 실행 예시를 제거하고 publication artifact/operator handoff 계약만 남겼다. release contract에서 배포 명령 부재를 회귀 검증한다.
|
||||
- `2026-08-13T16:10:50+09:00` — 최종 스냅샷에서 `uv lock --check`, scoped Ruff, release `22 passed`, 전체 `620 passed, 6 skipped`, `git diff --check` 통과. 이미지 UID/GID `10001:10001`, OCI revision 기준 SHA 일치, installed migration 28개(0028 포함), `.proofline` 변경 없음 확인.
|
||||
- `2026-08-13T16:16:37+09:00` — 독립 리뷰 FAIL(1 MAJOR, 1 MINOR, 0 BLOCKER). MAJOR: canary가 `docker create` 출력에서 검출될 때 생성된 scan container cleanup이 누락될 수 있음. MINOR: startup cleanup tests가 정확한 attempted/removed resource identity 대신 호출 개수만 검사함.
|
||||
- `2026-08-13T16:20:45+09:00` — MAJOR를 수정해 `docker create` ID를 canary 검사 전에 보존하고 모든 post-create 실패에서 정확한 ID cleanup을 보장했다. MINOR를 수정해 attempted/removed container·network·volume identity가 정확히 일치하도록 구현·테스트를 강화했다.
|
||||
- `2026-08-13T16:21:20+09:00` — 보정 후 release `23 passed`, 전체 `621 passed, 6 skipped`, 실제 image canary scan 및 startup smoke, lock·Ruff·diff 검증 통과.
|
||||
- `2026-08-13T16:22:58+09:00` — 독립 재리뷰 PASS. 기존 MAJOR/MINOR 모두 CLOSED, 새 회귀 및 배포 범위 확장 없음.
|
||||
- `2026-08-13T16:23:22+09:00` — 로컬 커밋 `96260b6302e050c6e564f3eca4e723558f26246f`(`feat(release): add verified image publication pipeline`) 생성. 작업트리 clean 확인.
|
||||
- 구현·검증·독립 리뷰는 완료했다. 기능 브랜치 push, PR, tag push, 실제 GHCR publication, GitHub Release 및 운영 배포는 수행하지 않았다.
|
||||
|
||||
> 각 작업이 실제로 완료되고 검증될 때마다 이 댓글의 체크박스와 진행 기록을 갱신한다. 체크 표시는 구현 완료 또는 해당 검증의 실제 통과를 의미하며, 배포·릴리스 완료를 의미하지 않는다.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
id: "line-0002"
|
||||
---
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: "req-0002"
|
||||
status: approved
|
||||
discovery: "dcy-0002"
|
||||
criteria:
|
||||
create:
|
||||
- "ac-0006"
|
||||
- "ac-0007"
|
||||
- "ac-0008"
|
||||
- "ac-0009"
|
||||
- "ac-0010"
|
||||
- "ac-0011"
|
||||
- "ac-0012"
|
||||
update: []
|
||||
retire: []
|
||||
satisfy: []
|
||||
---
|
||||
|
||||
# Requester-versioned GHCR 이미지 게시 계약
|
||||
|
||||
## Objective
|
||||
|
||||
`portfolio-hub`가 requester가 명시적으로 선택한 strict `vX.Y.Z` tag의 exact source에서 검증된 OCI image를 한 번만 build하고, 설치 payload·HTTP·PostgreSQL migration/readiness·비밀 제외·artifact identity를 검증한 동일 artifact만 GHCR에 게시할 수 있는 fail-closed capability를 제공한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- Alembic 설정과 모든 migration을 installable package data로 포함하고 source, wheel 및 image inventory의 non-empty exact parity를 유지한다.
|
||||
- Python 3.11과 repository-owned `uv.lock`을 verification 및 runtime dependency 설치에 사용하며 non-root runtime과 deny-by-default build context를 제공한다.
|
||||
- 일반 branch/main은 registry write 권한 없이 full regression과 release/container contract를 검증하고, strict `vX.Y.Z` tag push만 publication authority와 `packages: write`를 가진다.
|
||||
- verify 단계가 exact tagged commit에서 image를 한 번 build하고 installed payload, DB-disabled HTTP liveness, isolated PostgreSQL migration·DB ping·DB-enabled liveness 및 cleanup을 검증한다.
|
||||
- tested image ID, archive SHA-256, OCI revision과 secret canary를 fail-closed로 검사하고, publish 단계는 같은 archive를 checksum 검증 후 load하여 rebuild 없이 source selector와 requester version selector에 게시하며 same digest를 확인한다.
|
||||
- 운영자에게 immutable digest, 명시적 pre-start `pfhub migrate-db`, runtime secret/read-only/writable mount 및 image rollback과 DB rollback의 분리 경계를 문서화한다.
|
||||
|
||||
## Constraints
|
||||
|
||||
- automatic version bump, malformed/prerelease tag publication, moving `latest`, branch/main registry write를 허용하지 않는다.
|
||||
- 실제 credential, production DB, production migration 및 bootstrap-user side effect를 smoke에 사용하지 않는다.
|
||||
- image build·검증·publication capability는 운영 deployment 기능·설정·실행과 분리한다.
|
||||
- registry login/push 전 검증 실패는 external registry write 전에 fail-closed여야 한다. push 이후에만 얻는 digest가 없거나 malformed이면 workflow는 nonzero로 실패하고 성공 summary를 내지 않지만 이미 발생한 registry write를 없었다고 주장하지 않는다.
|
||||
- 이 승인된 사양은 로컬 구현 커밋 `96260b6302e050c6e564f3eca4e723558f26246f`의 의도를 기술하지만, 사후 ProofLine 문서화 자체가 정식 implementation chronology, delivery closure, push, publication, release 또는 deployment 완료를 소급 생성하지 않는다.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 이 Requirement에서 version 선택, Git tag 생성·push, 기능 브랜치 push, PR, GHCR publication 또는 package visibility 변경을 실행하지 않는다.
|
||||
- GitHub Release, 운영 서버/Dockge/Compose 배포·재시작, production credential·DB·migration·downgrade·repair·reset·backup을 수행하지 않는다.
|
||||
- deployment orchestrator, multi-architecture build, signing, SBOM, attestation을 추가하지 않는다.
|
||||
- application API/domain behavior, sibling repository 또는 unrelated repository-wide lint debt를 변경하지 않는다.
|
||||
- ProofLine 정식 시퀀스로 구현됐다는 소급 주장을 만들지 않는다.
|
||||
Reference in New Issue
Block a user