8월 7일까지 워킹트리에만 남아 있던 미커밋 작업을 커밋한다. 여러 사본 폴더(worktree·clone)에 흩어져 있던 중간 스냅샷을 정리하기 전에 원본을 git 이력으로 고정하는 것이 목적이다. - contracts/routes/services: measurement, outcome_trajectory, rupture_repair, deliberate_practice, calibration_transfer, supervision_research, multimodal_alliance, continuous_improvement 계열 신규 모듈과 테스트 - infra/db/init: 07~16 마이그레이션(측정 기반~calibration transfer 실행) - apps/web: 세션 리뷰 카드·관리 화면·E2E 스펙 추가 - docs/ops: G0~G8 라이브 통합·배포·롤백 증거 문서와 evidence JSON/PNG - scripts: smoke·ledger·릴리스 에이전트·NAS 프리뷰 운영 스크립트 engine.public 로그 .bak과 apps/web/test-results 산출물은 커밋에서 제외했다.
8 KiB
Outcome & Alliance OS 배포 프리플라이트 증거 — 2026-08-07
결론
G0~G8 구현을 운영 환경에 올리기 전에 누락된 migration과 내부 ingestion 비밀을 차단하는 fail-closed 프리플라이트를 추가했다. 앞부분은 배포 전 상태를 보존하며, 마지막 절은 실제 direct-runtime preflight와 공개 배포 결과를 기록한다.
검사 계약
- G3~G8 내부 write 경계의 토큰 6개는 모두 존재하고 32자 이상이어야 한다.
- 토큰은
change-me,replace-with,dummy,example계열 예시 값이 아니어야 한다. - 여섯 토큰은 서로 다른 값이어야 하며 한 경계의 폐기가 다른 경계에 영향을 주지 않아야 한다.
- Docker Compose는 여섯 토큰을 선택값이 아니라 필수 API 환경변수로 전달한다.
- DB 프리플라이트는 기존 인증·회기·알림 테이블 외에 G0~G8 migration별 대표 원장 테이블을 검사한다.
--require-app-role실행 시에는 기존과 같이 runtime role의BYPASSRLS와 owner 권한도 거부한다.
실행 증거
2026-08-07 KST 로컬에서 다음을 확인했다.
| 검증 | 결과 |
|---|---|
python -X utf8 -m unittest scripts.test_deploy_preflight |
6/6 passed |
python -X utf8 scripts/check-deploy-preflight.py --skip-db --env-file infra/.env.example --allow-placeholder-secrets |
requirements, KB, example env OK |
ruff check scripts/check-deploy-preflight.py scripts/test_deploy_preflight.py |
passed |
docker compose --env-file .env.example config --quiet + 프로세스 한정 검증용 OAuth 값 |
passed |
로컬 API 설정의 DB URL로 check_database(..., require_app_role=False) |
G0~G8 migration sentinel 전건 OK, current_user=vignette |
Compose 정적 검증에는 실제 비밀 대신 프로세스 수명의 검증용 OAuth 값만 사용했다. 운영 .env를
읽거나 비밀 값을 출력하지 않았다. 로컬 DB 검사는 migration 존재만 증명하며 운영 app-role/RLS 증거로
승격하지 않는다. 운영 배포에서는 반드시 실제 app-role DSN과 --require-app-role로 다시 실행해야 한다.
배포 전 공개 상태 재확인
2026-08-07 00:37 KST에 https://api-vignette.chanpaca.net/health는 status=ok,
environment=prod였고 공개 OpenAPI는 전체 69개 경로 중 multimodal-alliance 포함 경로 0개,
continuous-improvement 포함 경로 0개였다. 따라서 이 프리플라이트 완료만으로 G8의 실제 배포·public
proof 게이트를 닫지 않는다.
01:16 KST 공개 release audit
동일 공개 런타임을 다시 읽기 전용으로 조회했다.
| 표면 | 결과 | 판정 |
|---|---|---|
/health |
status=ok, environment=prod, db=true, engine=true |
기존 공개 런타임 자체는 건강함 |
/openapi.json |
전체 69 paths, G5 calibration 0, G6 supervision-research 0, G7 multimodal-alliance 0, G8 continuous-improvement 0 |
G5~G8 release가 공개 API에 없음 |
인증 기준선 GET /sessions |
401 not authenticated |
공개 API의 인증 경계는 동작함 |
| G5~G8 human read route 직접 GET | 네 경로 모두 404 Not Found |
인증 실패가 아니라 route 미배포 |
공개 WebSocket /voice/ws |
handshake 후 not authenticated, close 1008 |
WSS transport 존재만 확인; 인증 회기·물리 마이크·50분 증거 아님 |
| 공개 web asset | index-BDlbUpT7.js, index-BbmNnVmr.css; JS에 G5~G8 API marker 없음 |
정적 asset도 현재 release를 포함하지 않음 |
직접 확인한 human read route는 /calibration/learners/me,
/supervision-research/supervision-view,
/sessions/00000000-0000-0000-0000-000000000001/multimodal-alliance,
/continuous-improvement다. 로컬 현재 OpenAPI는 같은 분류가 각각 G5 8개, G6 9개,
G7 9개, G8 8개라 공개본과 소스의 차이가 명확하다.
실제 infra/.env fail-closed 재현
비밀 값을 출력하지 않고 --env-file infra/.env --skip-db를 실행했을 때 exit 1이었다.
ENVIRONMENT, Google OAuth client ID/secret, G3~G8 내부 토큰 6개가 누락됐고
ENGINE_MODE=claude_p는 허용 모드가 아니었다. 이 파일에는 DATABASE_URL 또는 app-role 식별자도
없어서 DB 포함 --require-app-role 검증을 실행할 운영 권한 자료가 없다.
따라서 당시 완료된 것은 구현·로컬 DB/API 증거와 fail-closed 검사기였다. 실제 소유자 secret/app-role 주입, 검증된 release-only patch 배포, 공개 route/assets/API/auth 재검증 전에는 G8과 AOS-011을 닫지 않는다.
release-only patch 적용 계약
release patch는 manifest의 base_commit 전용이다. 적용자는 먼저 clean checkout의
git rev-parse HEAD가 76d0b9ae9bb11b62af86ccd9a55e798bbedc9e31인지 확인하고, 다음 순서로
검증한다.
py -3.11 -X utf8 scripts/check-outcome-os-release-manifest.py --json
git apply --check docs/ops/evidence/outcome-os-release-only-2026-08-07.patch
git apply docs/ops/evidence/outcome-os-release-only-2026-08-07.patch
Set-Location apps/web
npm.cmd run check:api-types
npm.cmd run typecheck
npm.cmd run build
builder는 source worktree와 별도의 임시 clean checkout 적용 검사에 더해 임시 GIT_INDEX_FILE에
base tree를 읽고 git apply --cached --check를 실행한다. patch hunk는 canonical LF만 허용한다.
후보 산출물은 두 적용 검사와 source snapshot 불변 guard를 모두 통과한 뒤에만 기존 artifact를
원자적으로 교체한다. 따라서 다른 에이전트 편집이 빌드 중 발생하면 이전 검증 artifact를 보존한 채
fail-closed해야 한다.
통합 중 임시 artifact 상태 — 배포 금지
2026-08-07 01:28 KST 기준, line-ending guard를 추가한 첫 재생성 도중 다른 에이전트가 관련 소스 3개를 변경해 source snapshot guard가 실패했다. 수정 전 builder는 최종 guard 전에 destination을 기록했기 때문에 canonical 후보 patch가 기존 artifact를 덮어썼고, 현재 patch의 SHA/bytes는 manifest에 기록된 마지막 검증값과 일치하지 않는다. 현 artifact는 검증된 release-only 입력이 아니며 배포에 사용하면 안 된다.
builder는 이후 후보를 임시 경로에 유지하고 모든 guard 통과 뒤 원자 교체하며, 실패 후 이전 artifact를 복구하도록 수정됐다. 병렬 편집 통합과 dirty-path 재분류가 끝난 뒤 builder를 두 번 실행해 동일 SHA를 확인하고 manifest의 SHA/bytes를 갱신한 다음 checker가 green일 때만 이 임시 상태를 해제한다.
03:07~03:13 KST direct-runtime·공개 배포 종료 증거
임시 artifact 상태는 이후 전건 재분류와 두 번의 결정적 재조립으로 해제했다. 최초 공개 승격 입력은
229 files, 3,350,569 bytes, SHA-256 d9ac3c85c219640d14f74e705f766e6e1728464b0e32cc59e13acf8a45934d41이며
clean HEAD와 canonical index 적용 검사를 모두 통과했다.
현재 public runtime은 Compose 패키징이 아니라 apps/api/.env의 DATABASE_URL을 쓰는 direct-runtime이다.
아래 검사가 비밀값을 출력하지 않고 통과했다.
python scripts\provision-outcome-os-runtime-secrets.py --env-file apps\api\.env
python scripts\check-deploy-preflight.py --env-file apps\api\.env --deployment-profile direct-runtime --require-app-role
결과는 필수 direct-runtime key OK, current_user=vignette, non-owner app role이다. 이 clean worktree에서
backend 362, 회기·Outcome desktop/mobile 126, layout visual gate 15/15, typecheck·API contract·build를
통과한 뒤 production API 8001과 Cloudflare Pages를 승격했다. 공개 OpenAPI는 69→119 paths가 됐고 G1~G8
route, prod/db/engine health, auth 401, Google OAuth 302+state cookie, 새 custom-domain asset과 브라우저
로그인 렌더를 확인했다. 상세 public proof는 outcome-os-public-deployment-evidence-2026-08-07.md가 소유한다.
infra/.env의 Compose profile은 향후 컨테이너 패키징 전환용 별도 게이트로 남는다. 이것을 현재
direct-runtime의 미배포 증거로 다시 해석하지 않는다.