Skip to content

P03 산모 계정·프로필 부분 검증

이 페이지는 P03-mother-account-profile부분 증거다. 신규 무이력 합성 산모의 clean-pinned 동일 composition machine actor에서 여섯 acceptance case와 primary state는 통과했다. 기존 실제 브라우저는 withdrawal-only다. P03 phase 전체 판정은 red, 구현 증거는 partial, phaseAcceptancePassed: false, completionClaim: false다. 온라인 배포는 수행하지 않았다.

상위 결정은 ADR-034ADR-033이다. 기계 증거는 docs/evidence/p03-mother-account-profile/가 소유하며, actor-chain SSOT는 workflow/sanmopia_actor_chain.v1.yaml이다.

근거실행 경계현재 판정증명하지 않는 것
actual-actor-run.json + primary-state-manifest.json실제 local Supabase Auth/PostgREST, disposable SpiceDB, actual disposable Restate의 한 clean composition신규 무이력 6-case machine actor와 revision/state 전이 PASS이력 계정 privacy lifecycle
actual-browser-source-actor-run.json + actual-browser-run.json + WebP 5개과거 실제 local Supabase·SpiceDB·Restate network와 Chromium탈퇴·exact replay·reload/pageshow recovery PASS가입·수정 및 authorization·validation·conflict browser coverage
cleanup-manifest.json6-case wrapper teardown cardinalityAuth/operational/relationship/container/process/temp-directory residue 0retained record expiry, delayed purge, source별 eraser
WebP 6개frontend commit bdb8d7cc2a8f19bf14470157db8718e3474b650e의 synthetic UI fixtureUI contract 회귀 PASSAPI/network, Supabase primary state, 실제 actor transition
관리자 검토 provisional WebP 4개frontend 23d999a, contract c4b2488, backend ed8174ca, actual loopback Astro/ChromiumMaterial rail, 1280/390 responsive, unauthenticated fail-closed PASSHQ 권한 성공, review completion, source purge
four-repository pinassembly 9cab9b2, contract dc3c210, backend 0a12678, frontend fbed8f8, 각 revision/tree/clean: true등록P03 전체 PASS를 뜻하지 않음
Anchor레거시 동작새 경계
source-refs/sanmopia_web/static/js/signup.js:7-121이메일 중복, 필수 입력, 비밀번호 일치, 브라우저 약관 DOM을 검사한다.native Auth가 credential·contact verification을 소유하고 backend가 published consent를 재검증한다.
source-refs/sanmopia_web/application/controllers/api/Account.php:36-100이메일·이름·전화·비밀번호를 받아 자체 bcrypt와 auth token을 만든다.POST /mother/accounts는 credential·OTP·actor id·role·branch scope를 받지 않는다.
source-refs/sanmopia_web/application/controllers/api/Account.php:153-203산모 정보와 주소를 별도 table에 저장하며 주소 실패가 성공에 가려질 수 있다.profile, address revision, verification receipt, consent receipt, audit, owner relation intent를 한 authority로 묶는다.
source-refs/sanmopia_web/application/models/User_model.php:57-115일반·소셜 계정의 profile completeness 규칙이 다르다.하나의 lifecycle·completeness 규칙과 server-owned allowed next action을 사용한다.
  1. A — 범위 고정

    신규 무이력 합성 산모 한 계정의 동일 composition machine 6-case를 검증 대상으로 고정한다. 이력 계정은 포함하지 않고 별도 RED로 남긴다.

  2. B — 레거시 anchor 확인

    가입, profile, 주소, completeness의 PHP·JavaScript source line을 읽고 새 경계와 대조한다.

  3. C — public contract 확인

    GET /mother/account-consent-offer, POST /mother/accounts, GET|PATCH /mother/profile, POST /mother/account-withdrawals의 closed schema와 server-resolved scope를 확인한다.

  4. D — 실제 local runtime 준비

    local Supabase Auth/PostgREST, disposable SpiceDB, disposable actual Restate를 사용한다. 네 repository의 revision과 tree가 clean인지 먼저 확인한다. managed cloud와 mock route는 쓰지 않는다.

  5. E — 합성 Auth identity 생성

    fresh confirmed test identity를 만들고 원문 token, credential, contact, subject id는 증거에 남기지 않는다.

  6. F — verified contact 재확인

    backend가 bearer subject와 verified Auth contact를 다시 읽고 E.164 projection을 만든다.

  7. G — 가입 command 제출

    인증 없는 요청 401과 필수값 누락 요청 422를 먼저 보내 primary state 무변경을 확인한다. 이어 profile·주소·published consent reference와 Idempotency-Key만 제출한다. password, OTP, provider token, member/profile id, role, branch scope는 body에 넣지 않는다.

  8. H — 가입 authority 확인

    HTTP 201, lifecycle active, profile revision 1, address revision [1], consent receipt 2를 확인한다. 같은 가입 key replay는 200이고 primary state를 바꾸지 않아야 한다.

  9. I — 관계 authority 확인

    disposable SpiceDB에 mother profile owner가 적용됐는지 확인한다.

  10. J — 접근 capability 확인

    active mother capability가 다음 예약 action의 prerequisite가 되는지 server projection으로 확인한다.

  11. K — 자기 profile 수정 경계 확인

    PATCH /mother/profile이 expected profile/address revision만 받고 actor·account scope를 받지 않는지 확인한다. 첫 수정은 201과 profile revision 2, address revisions [1,2]; exact replay는 200 무변경; payload mismatch는 typed 409 무변경이어야 한다. 이 단계의 실제 browser 증거는 없다.

  12. L — body-free 탈퇴 제출

    POST /mother/account-withdrawals에 body 없이 bearer session과 Idempotency-Key만 보낸다. machine actor와 별개인 browser capture는 이 시점부터의 withdrawal UI만 증명한다.

  13. M — 탈퇴 접수 확인

    첫 응답 HTTP 202와 raw profile access 선차단을 확인한다. terminal state는 profile revision 3, address revisions [], verified contacts [], owner delete receipt로 고정한다.

  14. N — durable contract baseline 실행

    새 6-case actor wrapper와 기존 browser run은 각각 disposable 실제 Restate service의 network invocation을 사용한다. 두 런의 source lineage를 섞지 않는다. browser manifest는 byte-preserved actual-browser-source-actor-run.json만 가리켜야 한다.

  15. O — Restate 시간 계약 RED→GREEN

    WorkflowContext.time() journal이 epoch seconds float임을 확인한다. 실제 값 1784236748.7164872/1000하던 코드는 1970년대 transitioned_at을 만들어 requested_at보다 과거라는 DB guard로 실패했다. /1000을 제거하고 datetime.fromtimestamp(await ctx.time(), timezone.utc)로 바꾼 뒤 fresh actual Restate invocation이 completed/success인지 확인한다.

  16. P — lifecycle 완료 확인

    withdrawal, privacy workflow, account lifecycle과 profile erasure가 terminal 상태인지 확인한다.

  17. Q — Auth masking 확인

    실제 local Auth identity의 Sanmopia privacy status와 contact masking이 적용됐는지 확인한다.

  18. R — profile erasure 확인

    mother profile PII erasure가 완료되고 no-history 계정의 retained snapshot이 0인지 확인한다.

  19. S — 관계 삭제 확인

    SpiceDB mother profile owner가 삭제되고 relationship receipt가 남는지 확인한다.

  20. T — lost-response exact replay

    가입, profile 수정, 탈퇴에 같은 key를 재전송해 모두 HTTP 200, idempotent replay, primary state 무변경을 확인한다. browser에서는 탈퇴 exact replay HTTP 200만 확인한다.

  21. U — 새 command 차단

    탈퇴 후 새 key 요청은 HTTP 401이며 primary state와 workflow effect가 증가하지 않는지 확인한다.

  22. V — cardinality 확인

    withdrawal request, privacy lifecycle, profile erasure가 각각 1, relationship receipt가 2인지 확인한다. authorization 401, validation 422, conflict 409가 각각 stateChanged false인지도 같은 manifest에서 확인한다.

  23. W — actual browser recovery 검증

    최초 HTTP 202 뒤 저장된 pending 상태, Auth mask 뒤 full reload fallback을 재현한다. 같은 body-free submission key의 exact replay HTTP 200으로 terminal receipt를 복원하고, exact receipt ID와 requested-at이 모두 맞을 때만 storage를 withdrawn으로 승격한다. 실제 Chromium에서 수동 dispatch한 pageshowastro:page-load 뒤에도 terminal 상태가 유지되는지 확인한다.

  24. X — actual browser responsive·privacy 검토

    mobile/desktop horizontal overflow 0, console error/warning 0, raw credential·PII·withdrawal receipt 0과 3-frame actual animated WebP를 확인한다. wrapper teardown에서 Auth, operational, relationship, container, process, temp-directory residue가 모두 0인지 확인한다.

  25. Y — synthetic UI 회귀 분리

    join desktop/mobile, profile edit cards, withdrawal confirmation/result와 4-frame UI contract WebP를 확인한다. 이 여섯 자산은 실제 browser 증거로 승격하지 않는다.

  26. Z — privacy와 phase 판정

    actor/primary-state/browser manifest와 문서에서 raw credential, PII, identifier, body, private topology가 없는지 검토한다. 신규 무이력 6-case machine actor와 withdrawal-only browser를 각각 PASS로 기록한다. 이력·retention 3-gap 때문에 P03은 partial/red, P04는 blocked, 전체 완료는 false다.

역사 계정 operator-resume 후속 workflow

Section titled “역사 계정 operator-resume 후속 workflow”

backend pin ed8174ca는 기존 signal-only 경계를 실제 완료 경계로 교체했다. 아래 순서는 코드·fresh Supabase smoke·전체 backend regression으로 검증됐지만, HQ reviewer의 실제 브라우저 성공 pack은 아직 없으므로 P03 phase 판정을 올리지 않는다.

순서actor/authority검증된 동작현재 증거
1산모이력이 있는 계정이 탈퇴를 요청한다.SQL/fresh actor
2privacy lifecycle열린 예약·분쟁 권위·source drift가 있으면 irreversible effect 전에 waiting_for_operator_review로 멈춘다.Restate unit + Supabase smoke
3HQ 검토자UI는 탈퇴 receipt와 expected review revision만 전송한다. actor·role·branch·PII·note는 받지 않는다.TS contract + endpoint test + actual browser
4backendbearer actor와 전용 SpiceDB 권한, 최신 review cycle, server-owned blocker facts를 다시 읽는다.API/application tests
5review command해소되지 않은 blocker는 typed 409; 정확한 요청은 durable signal receipt를 만든다.API + Supabase smoke
6Restatereservation, pricing/settlement, care-delivery owner adapter가 source별 separation/redaction과 purge schedule을 각각 journaled step으로 적용한다.full regression + fresh database gate
7atomic completion전용 RPC가 signal consumption, 원 privacy lifecycle, withdrawal request, immutable completion receipt를 한 transaction으로 완료한다.review-completion smoke
8projection완료된 reviewed lifecycle은 completed를 우선 표시하고 역사적 review flag만 보존한다.projection unit test
9purge workerSKIP LOCKED lease로 due item을 claim하고 DB-authoritative terminal drift만 멈춘다. 일시적 network failure는 Restate retry로 올린다.claim race + purge tests

backend 전체 regression은 6667 passed, P03 fresh Supabase gate는 migration/lint/registration/profile/ withdrawal/review-resumption/review-completion/source-retention/source-purge/source-drift/purge-race/replay/ authorization/cleanup을 모두 통과했다. contract 전체 gate는 484 passed; frontend 전체 run은 새 nav 목적지 기대값을 갱신한 뒤 569개 테스트가 모두 통과한다.

관리자 검토 actual-browser provisional WebP

Section titled “관리자 검토 actual-browser provisional WebP”

아래 자산은 실제 Astro 7 loopback 서버와 Chromium을 사용했고 route interception은 없었다. 데스크톱 Material navigation rail은 88px, 모바일은 app bar와 temporary drawer로 치환됐으며 1280/390 모두 horizontal overflow 0, console error/warning 0이었다. 로그인하지 않은 제출은 결과 card를 열지 않고 authentication_required로 닫혔다.

산모 탈퇴 검토 모바일 준비 화면, 관리자 drawer, 인증 필요 결과의 실제 세 프레임 전이
Actual Astro + Chromium · 3 frames · unauthenticated fail-closed · completion claim 없음
Material navigation rail과 산모 탈퇴 검토 form이 보이는 실제 desktop 관리자 화면
Actual browser · desktop 1280px · rail 88px · overflow 0
산모 탈퇴 검토 form이 세로 card로 배치된 실제 mobile 관리자 화면
Actual browser · mobile 390px · app bar · overflow 0
로그인 없는 검토 제출을 본사 계정 인증 필요로 차단한 실제 mobile 화면
Actual browser · no session · result card hidden · fail-closed

원본 관찰값, 네 repository pin, SHA-256, frame count, 명시적 한계는 manifest.json에 있다. 이 pack은 actual-browser-provisional이며 authenticated HQ reviewer가 없었으므로 성공 acceptance나 source purge를 증명하지 않는다.

동일 composition 실제 machine actor 결과

Section titled “동일 composition 실제 machine actor 결과”
항목관찰값
happy path가입 201, profile 수정 201, 탈퇴 202, Restate completed, owner apply/delete
authorization미인증 가입 401, 탈퇴 뒤 새 key 401, stateChanged false
validationclosed-schema 누락 요청 422, stateChanged false
conflictprofile replay payload mismatch 409, stateChanged false
exact replay가입/profile/탈퇴 각각 200, 세 primary state 모두 unchanged
cleanupAuth/operational/relationship/container/process/temp-directory residue 0
primary stateprofile revision 1 → 2 → 3, address [1] → [1,2] → [], consent receipt 2
effect countwithdrawal/privacy/erasure 1/1/1, relationship receipts 2

actual-actor-run.json의 runtime 표기는 actual_local Supabase Auth/PostgREST, actual_disposable SpiceDB, actual_disposable_restate다. caseSetPassed: true지만 evidenceStatus: provisional, completionClaim: false다. primary-state-manifest.json이 동일 actor SHA-256과 six-case state를 잠근다.

별도 browser run은 clean frontend commit 1a314bc6a7bc09a7518bba819079c4abe99fb285와 served Astro build aggregate sha256:4a0596fa85b6b6245c6835c4ce4eaae127f19f924b5734f4a8a64ecc2bb189ec를 사용했다. local Supabase CLI, disposable SpiceDB v1.54.0, disposable Restate service, Chromium을 실제로 연결했다. route interception과 mock network는 사용하지 않았다.

이 browser run의 network source는 당시 actor JSON을 byte-preserve한 actual-browser-source-actor-run.json이다. 새 actual-actor-run.json의 6-case 결과나 최신 frontend pin으로 browser capture를 소급해 재분류하지 않는다. 아래 browser 결과는 withdrawal-only다.

항목실제 관찰
Restatenetwork invocation completed, completion success
terminal authorityaccount/profile/privacy/withdrawal `withdrawn
exact recovery같은 body-free submission replay HTTP 200
reload lifecyclefull reload, pageshow, astro:page-load 모두 terminal recovery PASS
responsivemobile 390×844, desktop 1440×900, horizontal overflow 모두 0
diagnosticsconsole error 0, warning 0
privacyPII·credential·raw withdrawal receipt 0; unsafe intermediate는 assembly에 없음

실제 network 첫 invocation에서 WorkflowContext.time() journal 값 1784236748.7164872가 epoch seconds float임을 확인했다. 기존 /1000은 1970년대 transitioned_at을 만들었고 DB가 transitioned_at < requested_at으로 terminal transition을 거부했다. 수정은 아래 한 줄이다.

datetime.fromtimestamp(await ctx.time(), timezone.utc)

/1000 제거 뒤 fresh Restate invocation은 completed/success, DB workflow와 withdrawal status는 모두 completed였다. 이 증거에서 ctx.time()을 epoch milliseconds라고 부르지 않는다.

수정 전에는 최초 HTTP 202withdrawal_pending만 storage에 남았다. manual pageshow도 pending이었고, Auth masking 뒤 full reload는 profile controller 없이 auth-required fallback만 그렸다. 수정 후 같은 stored submission의 exact body-free replay가 HTTP 200 terminal authority를 가져온다. exact receipt ID와 requested-at이 모두 일치할 때만 storage를 withdrawn으로 승격하고, fallback을 숨긴 뒤 PII-free recovery panel을 보인다. reload 1회와 동시에 들어온 lifecycle event의 deduplicated replay 1회로 network 200은 총 2회였고, 이후 pageshowastro:page-load에서도 terminal 상태가 바뀌지 않았다. full reload는 실제 navigation이고, 두 lifecycle event는 실제 Chromium page에서 수동 dispatch했다.

실제 산모 프로필, 탈퇴 확인, terminal recovery의 redacted 세 프레임 전이
Actual local services + Chromium · animated WebP 3 frames · redaction reviewed
개인값을 캡처 전에 치환한 실제 mobile 산모 profile
Actual · profile entry · mobile 390px
실제 body-free 탈퇴 전 redacted mobile 확인 카드
Actual · two-step confirmation · mobile 390px
full reload 뒤 탈퇴 완료와 profile 접근 중지를 보여주는 실제 mobile recovery panel
Actual · terminal recovery · mobile 390×844
full reload 뒤 탈퇴 완료와 profile 접근 중지를 보여주는 실제 desktop recovery panel
Actual · terminal recovery · desktop 1440×900

아래 자산은 UI 계약 회귀다. synthetic fixture와 frontend clean commit을 사용했지만 실제 backend, Supabase, SpiceDB와 연결한 브라우저 캡처는 아니다.

산모 프로필, 탈퇴 확인, 탈퇴 결과를 보여주는 네 프레임 synthetic UI-only 전이
UI contract only · animated WebP 4 frames · runtime acceptance 아님
입력 전 산모 가입 순차 카드의 synthetic desktop UI
Synthetic UI-only · join desktop · 1440×1000
입력 전 산모 가입 순차 카드의 synthetic mobile UI
Synthetic UI-only · join mobile · 390×844
정보별 수정 카드를 보여주는 synthetic 산모 profile mobile UI
Synthetic UI-only · profile cards · 390px
2단계 탈퇴 확인 카드를 보여주는 synthetic mobile UI
Synthetic UI-only · withdrawal confirmation
탈퇴 처리 결과 카드를 보여주는 synthetic mobile UI
Synthetic UI-only · withdrawal result
Case동일 composition machine actorBrowser 범위판정
P03-happy-path가입 201, profile 수정 201, 탈퇴 202, owner apply/deletewithdrawal UI만machine PASS
P03-authorization미인증 401, 탈퇴 뒤 새 key 401, 무변경없음machine PASS
P03-validationclosed-schema 요청 422, 무변경없음machine PASS
P03-conflictreplay payload mismatch 409, 무변경없음machine PASS
P03-exact-replay가입/profile/탈퇴 200, primary state 무변경탈퇴 reload replay 200machine PASS + withdrawal browser PASS
P03-cleanup여섯 residue category 0redacted terminal recoverymachine PASS + withdrawal browser PASS

기존 gate 용어 operator resume, source-specific retention erasure, retention expiry는 이제 backend 구현 누락이 아니라 아래 actual actor/browser 증거 누락을 가리킨다.

  • 실제 역사 계정 + HQ reviewer의 동일 composition operator-resume actor/browser pack
  • source별 retained payload와 operational erasure를 실제 actor가 확인하는 browser/primary-state chain
  • expiry 뒤 delayed purge, retry, child-record drift, exact replay의 실제 clock actor/browser chain
  • P04 complaint/dispute owner authority가 없을 때 계속 fail-closed라는 cross-phase 증거