P02 지점 운영자 자기 프로필 RED 기준 기록
이 페이지는 P02-branch-manager-profile 구현 전에 고정한 workflow-first RED 기준의 역사
기록이다. Phase ID의
branch-manager는 기존 호환 이름이며 actor, 계약, 권한, 테스트의 canonical role은
branch_operator다. 결정 권위는
ADR-030과
ADR-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-276 | caller index 경로에 form을 POST하고 성공 뒤 reload/history 이동을 섞는다. | full projection과 revision으로 화면을 갱신하고 충돌을 명시해야 한다. |
source-refs/sanmopia-admin/application/models/Branch_model.php:202-286 | profile, 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를 활성 운영자로 취급해서도
안 된다.
A→Z 실행 기준
Section titled “A→Z 실행 기준”-
A — immutable revision 고정
assembly, contract, backend, frontend의 clean commit과 실제 runtime artifact를 기록한다.
-
B — legacy anchor 재확인
위 다섯 source range의 GET/POST 비대칭, readonly 표시, 혼합 mutation을 원문에서 확인한다.
-
C — P01 prerequisite 생성
test-owned HQ actor가 최소 onboarding profile과 invited manager authority를 만들고 P01 결과와 섞이지 않는 P02 lineage를 기록한다.
-
D — 초대 대상 고정
invitation, invited identity, onboarding revision이 같은 branch lineage인지 확인한다. 원문 이메일, token, UUID는 게시 근거에 남기지 않는다.
-
E — 실제 Supabase 로그인
초대 대상
branch_operator가 실제 Auth session을 얻는다. cookie와 access/refresh token은 캡처·manifest·로그에 기록하지 않는다. -
F — invitation activation 실행
caller branch/actor id 없이 expected onboarding revision과 idempotency key로 별도 activation command를 실행한다.
-
G — activation 원자성 확인
invitation accepted와 membership activation이 exact lineage에서 한 번만 전진하고 audit와 delivery intent가 중복되지 않는지 Supabase primary state로 확인한다.
-
H — pending 결과 확인
activation 응답의
authorizationRelationshipStatus가pending이며 이 상태를 완료 권한으로 표현하지 않는지 확인한다. -
I — authorization delivery 실행
runtime-owned consumer가 queue intent를 처리한다. 임의 UI flag나 DB membership만으로 delivery 완료를 합성하지 않는다.
-
J — SpiceDB 권한 검증
canonical
branch_operatorrelationship과 파생view/managepermission을 실제 SpiceDB에서 검증한 뒤에만 profile route를 연다. -
K — 자기 profile GET
GET /branch/profile이 URL/body의 branch id 없이 Supabase subject → member profile → 정확히 하나의 active membership → branch → SpiceDB permission 순으로 scope를 해석하는지 확인한다. -
L — 최소 projection 확인
GET 결과가 자기 지점의 full operator-visible projection, revision, completeness,
allowedNextActions를 반환하고 credential, raw 계좌, private audit를 노출하지 않는지 확인한다. -
M — delivery 전·모호한 membership 거부
pending delivery, active membership 0건/복수 건, withdrawn identity가 각각 fail closed하고 profile read/write mutation이 0인지 확인한다.
-
N — 허용 PATCH 준비
display name, contact email, primary phone, postal code, road/detail address, reason,
expectedRevision만 담고Idempotency-Key를 고정한다. -
O — authorized apply
PATCH /branch/profile이 HTTP201, revision 1회 증가, 갱신된 completeness와 정확한allowedNextActions를 포함한 full projection을 반환하는지 확인한다. -
P — primary state 대조
current profile, immutable revision history, audit가 같은 actor/branch/reason/revision을 가리키고 다른 branch, lifecycle, credential, fee, account, service-area row가 변하지 않았는지 확인한다.
-
Q — exact replay
같은 actor/key/payload가 HTTP
200, 같은 projection,idempotentReplay: true를 반환하며 revision/audit/delivery intent를 추가하지 않는지 확인한다. -
R — conflicting replay
같은 key에 다른 payload를 보내 typed conflict가 반환되고 accepted projection, revision, audit count가 byte-equivalent하게 유지되는지 확인한다.
-
S — stale revision
오래된
expectedRevision이 HTTP409, 최신latestProjection, field-levelfieldDiff를 반환하고 mutation이 0인지 확인한다. -
T — validation 422
잘못된 email, phone, address, blank reason과 unknown field가 closed typed
422field errors이며 어떤 부분 profile도 저장하지 않는지 확인한다. -
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을 확인한다.
-
V — desktop 실제 browser
production build 1440px에서 login, activation 대기/완료, GET, edit, applied/replay/conflict 상태와 민감값 masking을 실제 API로 확인한다. route interception과 mock 응답은 금지한다.
-
W — mobile 실제 browser
390px에서 같은 authority와 상태를 확인하고 edit/recovery action이 가려지거나 잘리지 않는지 확인한다.
-
X — 실제 전이 animated WebP
activation entry부터 delivery 확인, profile apply, reload projection까지 서로 다른 실제 상태 프레임을 WebP로 수집하고 capture commit/artifact/hash를 고정한다.
-
Y — cleanup/reaper
명시적 test ownership, TTL, lease/hold, tenant scope를 확인한 뒤 operational activation/profile/ session/relationship만 삭제한다. existing profile과 unlabelled resource는 보존하고 재실행도 안전해야 한다.
-
Z — privacy·범위 판정
retained audit의 UUID retention 또는 pseudonym 정책, redaction, zero operational residue를 검토한다. 여섯 case와 모든 manifest가 같은 네 pin에서 통과한 경우에만 P02 PASS로 바꾼다.
여섯 acceptance case
Section titled “여섯 acceptance case”P02-conflict는 conflicting replay와 stale revision이라는 두 필수 variant를 함께 가진다.
cleanup/privacy는 별도 성공 조건이므로 conflict를 대신하지 않는다.
| Case | PASS 조건 | 최종 결과 |
|---|---|---|
P02-happy-path | activation/delivery 검증 뒤 자기 GET과 authorized PATCH 201; full projection, revision, completeness, actions 일치 | canonical PASS |
P02-authorization | pending/미활성/복수 membership, cross-branch, caller scope, HQ-only field, generic bypass가 모두 scope-safe 거부·mutation 0 | canonical PASS · 13 variants |
P02-validation | contact/address/reason/unknown field가 closed typed 422; mutation 0 | canonical PASS · 6 variants |
P02-conflict | same-key different-payload conflict와 stale revision 409 latestProjection/fieldDiff; 둘 다 mutation 0 | canonical PASS · 2 variants |
P02-exact-replay | HTTP 200, same full projection, replay true, revision/audit/delivery 중복 0 | canonical PASS |
P02-cleanup | test-owned operational residue 0, existing/unlabelled/cross-tenant 생존, retained audit privacy 정책 충족 | canonical PASS |
Layer-first 구현 경계
Section titled “Layer-first 구현 경계”domain/branch_operations/features/office_profile/application/branch_operations/features/office_profile/ commands/complete_own_branch_profile.py queries/get_own_branch_profile.pyadapters/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을 소유한다.
최종 증거 상태
Section titled “최종 증거 상태”| Evidence | 상태 |
|---|---|
| workflow-first criteria page | 이 페이지 · 역사 RED 기준 |
| 강화된 actual actor HTTP run | PASS · conflict 2, validation 6, authorization 13 |
| actor cleanup | operational/Auth/relationship 0; retained activation audit 1, relationship application receipt 2, P01 lineage 1 |
| final-pin production browser | PASS · desktop 2, mobile 3, 실제 API, worker isolation/restoration, process residue 0 |
| canonical machine evidence / WebP promotion | PASS · 다섯 PNG, 실제 여섯-frame WebP, privacy/cleanup/four-pin 검증 |
| P02 phase | PASS · 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다.