본문으로 건너뛰기

Pipeline Studio 비-admin 코드 작성 QA 가이드 — 페르소나별 시나리오

이 문서는 #2106 P1(비-admin 워크스페이스 멤버의 코드 노드 작성 — 승인 게이트 기반)을 페르소나별로 실제 브라우저에서 직접 검증하는 가이드입니다. 모든 화면은 AKS prod(gend.genon.ai)의 실 워크스페이스(finance-invest / energy-kogas)에서, 각 역할의 실제 토큰으로 캡처했습니다. API 우회 없이 전부 UI로 수행했습니다.

이전 가이드와의 차이코드 노드 QA 가이드는 "코드 노드 추가·배포는 관리자만" 시점의 문서입니다. 본 가이드는 그 이후 추가된 비-admin 경로(드라이런 허용 + ws-admin 승인 + library_ref-only 배포)를 다룹니다. 게이트는 완화하지 않고 경로만 추가됐습니다.

등장 페르소나

페르소나권한Keycloak 그룹할 수 있는 일
워크스페이스 멤버비-admin/tenants/<slug>코드 드라이런, 스텝 제출(승인 대기), 승인된 라이브러리 스텝으로 배포
워크스페이스 adminws-admin/tenants/<slug>/admins위 + 스텝 승인/거부
글로벌 adminadmin(realm role)전 워크스페이스 무제한 (인라인 코드 직접 배포 포함)

보안 불변식 (이 가이드가 검증하는 것)

  • I1 — 미승인/인라인 코드는 비-admin 으로 절대 배포되지 않는다.
  • I3 — 드라이런은 격리 샌드박스 전용 + 워크스페이스 멤버십 펜스 + per-ws rate limit.
  • I4 — ws-admin 은 다른 테넌트의 스텝을 승인할 수 없다(워크스페이스 펜스).

1. 멤버 — 코드 드라이런 (I3)

워크스페이스 멤버는 admin 이 아니어도 코드 노드를 캔버스에 놓고 드라이런으로 즉시 테스트할 수 있습니다. 격리 러너(gend-codexec)에서 실행되고, 결과(status: ok · output · metrics)가 바로 보입니다.

멤버 드라이런 성공 — status ok + output marker

확인 포인트import socket 같은 정책 위반 코드는 드라이런 단계에서 L1 AST 검사로 거부됩니다. 토스트에 python 코드 정책 위반 [import] line 1: socket 이 뜹니다.

멤버 드라이런 — 정책 위반 차단

rate limit — 드라이런은 워크스페이스 단위 토큰 버킷(기본 rps=0.5, burst=5)으로 보호됩니다. 한 멤버가 러너를 연타하면 같은 워크스페이스 버킷을 공유하므로 429 로 제한됩니다. (UI 에서는 "드라이런 중…" 동안 버튼이 비활성화돼 연타 자체가 직렬화됩니다 — rate limit 은 다중 멤버 동시 남용 방어용입니다.)


2. 멤버 — 인라인 코드 배포는 차단 (I1)

멤버가 캔버스에 인라인 코드 노드(library_ref 없이 직접 작성한 shell/python)를 넣고 배포를 시도하면 403 으로 막힙니다.

멤버 인라인 코드 배포 — 403 차단

토스트: "코드 노드는 워크스페이스 관리자만 추가/배포할 수 있습니다". 인라인 코드를 배포하려면 아래 승인 흐름을 거쳐야 합니다.


3. 멤버 — 재사용 스텝 제출 (승인 대기)

멤버는 스텝 라이브러리 탭에서 재사용 스텝을 제출할 수 있습니다. shell/python 스텝은 승인 대기(pending) 상태로 등록됩니다.

멤버가 제출한 스텝 — 대기중

멤버 화면에는 승인/거부 버튼이 없습니다 — 승인은 ws-admin 의 권한입니다.


4. ws-admin — 스텝 승인/거부

워크스페이스 admin(/tenants/<slug>/admins)에게는 같은 목록에 ✓ 승인 / ✗ 거부 버튼이 보입니다.

ws-admin — 승인/거부 버튼 표시 + 대기중 배지

✓ 승인을 누르면 배지가 대기중승인됨 으로 바뀝니다.

ws-admin — 멤버 스텝 승인됨

✗ 거부를 누르면 거부됨 으로 바뀝니다.

ws-admin — 스텝 거부됨

모든 승인/거부는 ApprovalRequest(request_type="ps_step_approval") 감사 레코드로 기록됩니다.

4-1. 크로스테넌트 펜스 (I4)

finance-invest 의 ws-admin 은 energy-kogas 의 스텝을 볼 수도, 승인할 수도 없습니다. 아래 화면은 finance-invest ws-admin 의 스텝 목록 — 같은 화면에 finance 스텝은 보이지만, energy-kogas 멤버가 만든 스텝은 목록에 아예 없습니다(워크스페이스 펜스).

ws-admin — 타 테넌트 스텝은 보이지 않음

서버에서 decide_step_approval 이 호출자 워크스페이스로 펜스하므로, 타 테넌트 스텝 ID 로 직접 승인 요청해도 404 입니다.


5. 멤버 — 승인된 스텝으로 배포 (library_ref-only)

스텝이 승인되면, 멤버의 빌더 노드 팔레트 "라이브러리" 섹션에 나타납니다. (승인된 스텝만 — 거부/대기 스텝은 제외.)

멤버 팔레트 — 승인된 라이브러리 스텝

이 라이브러리 노드를 캔버스로 드래그해 구성한 파이프라인은, 코드노드가 전부 승인된 library_ref 이므로 멤버도 배포 가능합니다(배포됨 (rev 1)).

멤버 — library_ref-only 배포 성공

5-1. 실행 + per-node 로그 (#2107)

배포본을 실행하면 실행 탭에서 각 노드의 로그(stdout/stderr)를 펼쳐볼 수 있습니다. 아래는 실패한 노드의 로그 디스클로저 — 코드 러너의 stderr 와 실패 사유가 그대로 노출됩니다.

멤버 — per-node 실행 로그

계약 주의 — shell/python 코드는 stdout 에 JSON 을 출력해야 합니다. 일반 텍스트를 출력하면 code execution failed: output not JSON 으로 실패합니다(위 화면).

5-2. 승인 코드 재정의는 차단 (I1 강화)

멤버가 인스턴스화한 라이브러리 노드의 코드를 편집해서 재배포하려 하면 차단됩니다 — 승인된 코드를 우회 변경할 수 없습니다.

멤버 — 승인 코드 재정의 배포 차단

토스트: "승인된 라이브러리 스텝의 코드는 재정의할 수 없습니다 — 재승인이 필요합니다 (관리자만)". (배포 게이트가 원본 그래프의 노드 코드와 승인된 스텝 코드를 대조해 거부합니다.)


6. 멤버 — 인라인 코드 배포-요청 (P2, #2106 경로 B)

P1(§1~5)은 멤버가 승인된 라이브러리 스텝으로만 배포하는 경로였습니다. P2 는 멤버가 인라인 코드를 그대로 배포하고 싶을 때 — 직접 배포는 여전히 막되(I1) ws-admin 승인 경로로 prod 에 도달하게 합니다.

6-1. 멤버 — 인라인 코드 파이프라인 + 직접 배포 차단 (I1)

멤버가 인라인 python 코드 노드로 파이프라인을 구성합니다.

멤버 인라인 코드 파이프라인

배포를 직접 누르면 P1 과 동일하게 403 — 게이트는 완화되지 않습니다.

멤버 직접 배포 — 403 차단

6-2. 멤버 — 배포 요청 제출

대신 배포 요청 버튼을 누르면, 현재 드래프트가 불변 snapshot(content_hash 포함)으로 굳어 ws-admin 승인 큐에 올라갑니다.

멤버 배포 요청 제출

토스트: "배포 요청을 제출했습니다 — 관리자 승인 대기".

엣지 — 인라인 코드가 없는 파이프라인에서 배포 요청을 누르면 "인라인 코드 노드가 없습니다 — 직접 배포할 수 있습니다"(422)로 안내합니다.

인라인 코드 없음 — 배포 요청 거부

6-3. ws-admin — 배포 요청 큐 + 코드 검토

ws-admin 에게는 Pipeline Studio 목록에 "배포 요청" 탭이 보입니다(멤버에겐 미노출). 제출된 요청과 실제 제출 코드를 그대로 검토합니다.

ws-admin 배포 요청 큐

ws-admin 제출 코드 미리보기

6-4. ws-admin — 승인·배포 (I2: 승인된 코드 == 배포된 코드)

승인·배포를 누르면 요청 시점의 불변 snapshot 이 배포됩니다. 멤버가 요청 후 드래프트 코드를 바꿔도, 배포되는 것은 승인 시점에 검토한 코드입니다(content_hash 무결성 + 승인 시 AST 재검증).

ws-admin 승인 → 배포

prod 검증: 요청 후 드래프트를 DRAFT_CHANGED 로 바꾼 뒤 승인 → 배포된 리비전 코드는 요청 시점 원본(p2-member-original)이었고 변경분은 반영되지 않았습니다(I2).

6-5. 크로스테넌트 펜스 (I4)

다른 테넌트(energy-kogas) 멤버가 올린 배포 요청은 finance-invest ws-admin 의 큐에 보이지 않습니다. 승인 권한이 워크스페이스로 펜스됩니다.

ws-admin — 타 테넌트 배포 요청 미표시


검증 매트릭스 (실 브라우저 prod 결과)

#시나리오페르소나기대결과
1코드 드라이런멤버status: ok
2정책 위반(import socket) 드라이런멤버AST 거부
3인라인 코드 직접 배포멤버403 차단
4스텝 제출멤버대기중, 버튼 없음
5스텝 승인ws-admin승인됨
6스텝 거부ws-admin거부됨
7타 테넌트 스텝ws-admin미표시(펜스)
8library_ref-only 배포멤버배포 성공
9per-node 실행 로그멤버stdout/stderr 노출
10승인 코드 재정의 배포멤버차단
11P2 인라인 배포 요청 제출멤버승인 대기
12P2 인라인 코드 없음 → 요청멤버422 안내
13P2 배포 요청 탭멤버미노출
14P2 배포 요청 큐 + 코드 검토ws-admin코드 표시
15P2 승인·배포(불변 snapshot)ws-admin요청 시점 코드 배포 (I2)
16P2 타 테넌트 배포 요청ws-admin미표시(펜스 I4)

P1 스크린샷(§1~5)은 2026-06-12, P2 스크린샷(§6)은 2026-06-13 AKS prod 실 브라우저 세션에서 모든 기능 동작을 UI 로만(API 우회 없이) 수행해 캡처했습니다. 단위/통합 테스트는 apps/api/tests/test_auth_workspace_admin.py, test_ps_step_approval.py, test_ps_code_gate.py, test_pipeline_studio_code_test.py, test_ps_nonadmin_deploy.py, test_ps_code_deploy_request.py, test_ps_code_deploy_router.py + ui/.../CodeDeployRequestsTab.test.tsx 에서 동일 불변식(I1/I2/I3/I4)을 커버합니다.