Skip to content

P02 지점 운영자 자기 프로필 RED 기준 기록

이 페이지는 P02-branch-manager-profile 구현 전에 고정한 workflow-first RED 기준의 역사 기록이다. Phase ID의 branch-manager는 기존 호환 이름이며 actor, 계약, 권한, 테스트의 canonical role은 branch_operator다. 결정 권위는 ADR-030ADR-033이다.

레거시 기준과 잘못된 권한 대칭

Section titled “레거시 기준과 잘못된 권한 대칭”
Anchor실제 동작현대화 판정
source-refs/sanmopia-admin/application/controllers/OfficeManagement.php:374-381상세 GET은 non-admin actor의 URL $index를 로그인 지점 BRANCH_INDEX로 치환한다.읽기에서만 보이는 session 치환을 쓰기 권한으로 간주할 수 없다.
source-refs/sanmopia-admin/application/controllers/OfficeManagement.php:399-448상세 POST는 caller가 URL로 준 $index를 session 지점으로 다시 해석하지 않고 저장한다.caller-supplied scope를 제거하고 서버가 자기 지점을 해석해야 한다.
source-refs/sanmopia-admin/static/js/office_management/office_detail.js:1-10일부 input을 readonly로 표시한다.presentation guard일 뿐 URL scope, request 변조, 직접 호출을 막지 않는다.
source-refs/sanmopia-admin/static/js/office_management/office_detail.js:253-276caller index 경로에 form을 POST하고 성공 뒤 reload/history 이동을 섞는다.full projection과 revision으로 화면을 갱신하고 충돌을 명시해야 한다.
source-refs/sanmopia-admin/application/models/Branch_model.php:202-286profile, credential, lifecycle, raw 계좌, 당월 회비를 한 호출에서 갱신한다.첫 P02 write는 표시·연락·구조화 주소 facts만 소유한다. 나머지는 별도 feature command다.

기존 generic POST /branch-office-profile-updates도 caller가 branchOfficeProfileId를 주는 동안에는 P02 own-profile authority가 아니다. Supabase actor를 읽고 SpiceDB manage만 확인해도 초대 activation과 자기 지점 해석을 건너뛸 수 있다. 이 경로는 같은 server-owned resolver에 위임하거나 P02 PASS 전에 닫아야 한다.

flowchart LR
  Invitation["invited manager authority"] --> Activation["invitation activation"]
  Activation --> Pending["authorizationRelationshipStatus: pending"]
  Pending --> Delivery["relationship delivery"]
  Delivery --> Verify["SpiceDB verification"]
  Verify --> Read["GET /branch/profile"]
  Read --> Write["PATCH /branch/profile"]
  Write --> Projection["full projection + revision + completeness + allowedNextActions"]

pending은 성공한 권한 부여가 아니다. delivery와 SpiceDB 검증 전에는 GET/PATCH를 fail closed한다. P01이 미리 기록한 relationship만으로 invited user를 활성 운영자로 취급해서도 안 된다.

  1. A — immutable revision 고정

    assembly, contract, backend, frontend의 clean commit과 실제 runtime artifact를 기록한다.

  2. B — legacy anchor 재확인

    위 다섯 source range의 GET/POST 비대칭, readonly 표시, 혼합 mutation을 원문에서 확인한다.

  3. C — P01 prerequisite 생성

    test-owned HQ actor가 최소 onboarding profile과 invited manager authority를 만들고 P01 결과와 섞이지 않는 P02 lineage를 기록한다.

  4. D — 초대 대상 고정

    invitation, invited identity, onboarding revision이 같은 branch lineage인지 확인한다. 원문 이메일, token, UUID는 게시 근거에 남기지 않는다.

  5. E — 실제 Supabase 로그인

    초대 대상 branch_operator가 실제 Auth session을 얻는다. cookie와 access/refresh token은 캡처·manifest·로그에 기록하지 않는다.

  6. F — invitation activation 실행

    caller branch/actor id 없이 expected onboarding revision과 idempotency key로 별도 activation command를 실행한다.

  7. G — activation 원자성 확인

    invitation accepted와 membership activation이 exact lineage에서 한 번만 전진하고 audit와 delivery intent가 중복되지 않는지 Supabase primary state로 확인한다.

  8. H — pending 결과 확인

    activation 응답의 authorizationRelationshipStatuspending이며 이 상태를 완료 권한으로 표현하지 않는지 확인한다.

  9. I — authorization delivery 실행

    runtime-owned consumer가 queue intent를 처리한다. 임의 UI flag나 DB membership만으로 delivery 완료를 합성하지 않는다.

  10. J — SpiceDB 권한 검증

    canonical branch_operator relationship과 파생 view/manage permission을 실제 SpiceDB에서 검증한 뒤에만 profile route를 연다.

  11. K — 자기 profile GET

    GET /branch/profile이 URL/body의 branch id 없이 Supabase subject → member profile → 정확히 하나의 active membership → branch → SpiceDB permission 순으로 scope를 해석하는지 확인한다.

  12. L — 최소 projection 확인

    GET 결과가 자기 지점의 full operator-visible projection, revision, completeness, allowedNextActions를 반환하고 credential, raw 계좌, private audit를 노출하지 않는지 확인한다.

  13. M — delivery 전·모호한 membership 거부

    pending delivery, active membership 0건/복수 건, withdrawn identity가 각각 fail closed하고 profile read/write mutation이 0인지 확인한다.

  14. N — 허용 PATCH 준비

    display name, contact email, primary phone, postal code, road/detail address, reason, expectedRevision만 담고 Idempotency-Key를 고정한다.

  15. O — authorized apply

    PATCH /branch/profile이 HTTP 201, revision 1회 증가, 갱신된 completeness와 정확한 allowedNextActions를 포함한 full projection을 반환하는지 확인한다.

  16. P — primary state 대조

    current profile, immutable revision history, audit가 같은 actor/branch/reason/revision을 가리키고 다른 branch, lifecycle, credential, fee, account, service-area row가 변하지 않았는지 확인한다.

  17. Q — exact replay

    같은 actor/key/payload가 HTTP 200, 같은 projection, idempotentReplay: true를 반환하며 revision/audit/delivery intent를 추가하지 않는지 확인한다.

  18. R — conflicting replay

    같은 key에 다른 payload를 보내 typed conflict가 반환되고 accepted projection, revision, audit count가 byte-equivalent하게 유지되는지 확인한다.

  19. S — stale revision

    오래된 expectedRevision이 HTTP 409, 최신 latestProjection, field-level fieldDiff를 반환하고 mutation이 0인지 확인한다.

  20. T — validation 422

    잘못된 email, phone, address, blank reason과 unknown field가 closed typed 422 field errors이며 어떤 부분 profile도 저장하지 않는지 확인한다.

  21. U — cross-branch·HQ-only 거부

    caller branch/profile/actor id, branch code, legal/business identity, lifecycle, password, membership fee, account, service-area field를 주입해 scope-safe 거부와 mutation 0을 확인한다.

  22. V — desktop 실제 browser

    production build 1440px에서 login, activation 대기/완료, GET, edit, applied/replay/conflict 상태와 민감값 masking을 실제 API로 확인한다. route interception과 mock 응답은 금지한다.

  23. W — mobile 실제 browser

    390px에서 같은 authority와 상태를 확인하고 edit/recovery action이 가려지거나 잘리지 않는지 확인한다.

  24. X — 실제 전이 animated WebP

    activation entry부터 delivery 확인, profile apply, reload projection까지 서로 다른 실제 상태 프레임을 WebP로 수집하고 capture commit/artifact/hash를 고정한다.

  25. Y — cleanup/reaper

    명시적 test ownership, TTL, lease/hold, tenant scope를 확인한 뒤 operational activation/profile/ session/relationship만 삭제한다. existing profile과 unlabelled resource는 보존하고 재실행도 안전해야 한다.

  26. Z — privacy·범위 판정

    retained audit의 UUID retention 또는 pseudonym 정책, redaction, zero operational residue를 검토한다. 여섯 case와 모든 manifest가 같은 네 pin에서 통과한 경우에만 P02 PASS로 바꾼다.

P02-conflict는 conflicting replay와 stale revision이라는 두 필수 variant를 함께 가진다. cleanup/privacy는 별도 성공 조건이므로 conflict를 대신하지 않는다.

CasePASS 조건최종 결과
P02-happy-pathactivation/delivery 검증 뒤 자기 GET과 authorized PATCH 201; full projection, revision, completeness, actions 일치canonical PASS
P02-authorizationpending/미활성/복수 membership, cross-branch, caller scope, HQ-only field, generic bypass가 모두 scope-safe 거부·mutation 0canonical PASS · 13 variants
P02-validationcontact/address/reason/unknown field가 closed typed 422; mutation 0canonical PASS · 6 variants
P02-conflictsame-key different-payload conflict와 stale revision 409 latestProjection/fieldDiff; 둘 다 mutation 0canonical PASS · 2 variants
P02-exact-replayHTTP 200, same full projection, replay true, revision/audit/delivery 중복 0canonical PASS
P02-cleanuptest-owned operational residue 0, existing/unlabelled/cross-tenant 생존, retained audit privacy 정책 충족canonical PASS
domain/branch_operations/features/office_profile/
application/branch_operations/features/office_profile/
commands/complete_own_branch_profile.py
queries/get_own_branch_profile.py
adapters/branch_operations/features/office_profile/
interfaces/branch_operations/features/office_profile/
application/branch_operations/features/branch_manager_invitation_activation/

commands/queries/는 이 feature에서 실제 분리가 필요하므로 application 아래에만 둔다. 빈 대칭 폴더나 전역 CQRS 폴더를 만들지 않는다. invitation activation과 office profile은 서로의 내부 module을 직접 import하지 않고 public contract/port와 composition root를 통해 순서를 잇는다.

계좌, 회비, service area는 allowedNextActions로 안내할 수 있지만 이 PATCH에 포함하지 않는다. 각각 목적별 계좌, 회비 정책, service-area policy feature가 별도 권위와 revision을 소유한다.

Evidence상태
workflow-first criteria page이 페이지 · 역사 RED 기준
강화된 actual actor HTTP runPASS · conflict 2, validation 6, authorization 13
actor cleanupoperational/Auth/relationship 0; retained activation audit 1, relationship application receipt 2, P01 lineage 1
final-pin production browserPASS · desktop 2, mobile 3, 실제 API, worker isolation/restoration, process residue 0
canonical machine evidence / WebP promotionPASS · 다섯 PNG, 실제 여섯-frame WebP, privacy/cleanup/four-pin 검증
P02 phasePASS · canonical

delivery 검증, caller-scoped 우회, self-scoped CAS/replay, non-owner authority 불변, 복수 membership, withdrawn identity, actor cleanup, final-pin browser, canonical promotion이 모두 닫혔다. 현재 순차 gate는 P03 RED/not-started이며 P04~P13은 blocked다.