Skip to content

feat(approval-gated): AC-F5 후반 — 공유 볼륨을 파일시스템 아카이브에 실어 동결·복원을 건너 나른다 - #127

Open
dlddu-k3s-homelab-k3s-mcp[bot] wants to merge 9 commits into
mainfrom
reconcile/rct_20260912-0007-session-platform-docs-impl
Open

dlddu-k3s-homelab-k3s-mcp[bot] wants to merge 9 commits into
mainfrom
reconcile/rct_20260912-0007-session-platform-docs-impl

Conversation

@dlddu-k3s-homelab-k3s-mcp

@dlddu-k3s-homelab-k3s-mcp dlddu-k3s-homelab-k3s-mcp Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

무엇을 왜

reconciler task rct_20260912-0007 (tbm_session-platform-docs-impl) — AC-F5 의 후반(아카이브 축).

AC-F5 는 현재형으로 단언한다: 「이 볼륨의 내용은 파일시스템 아카이브에 포함된다 … 따라서 동결·복원을 건너서도 이전 실행이 만든 파일이 남아 있고」. 실제로는 approval-gated 에 체크포인터가 등록돼 있지 않아(main.go 가 등록하는 것은 shell 의 CRIU 와 claude-code 의 아카이브 둘뿐) Service.checkpointerForErrCheckpointDisabled 를 내고 이 타입의 POST /snapshot503 이었다. 즉 그 문장을 실행할 코드 경로가 어느 환경에도 없었다.

그리고 공유 볼륨 PVC 는 그 라운드 헬퍼 파드의 ownerReferences 에 묶여 동결 시 함께 GC 되므로, 전략만 등록하고 볼륨을 아카이브에 넣지 않으면 동결이 성공하는 대신 승인된 결과물이 소리 없이 사라진다 — 지금의 503 보다 나쁜 거짓이 된다. 그래서 세 조각을 함께 넣는다.

무엇을 했나

① 전략 등록 — 조립 루트가 CLAUDE_CODE_ARCHIVE_ENABLED 한 게이트 안에서 claude-codeapproval-gated 둘 모두에 AgentArchiveCheckpointer 를 등록한다.

게이트를 새로 만들지 않은 것이 이 PR 의 유일한 설계 선택이다. 그 값은 타입이 아니라 전략(아카이브를 CHECKPOINT_S3_* 에 쓰는 것)을 켜는 스위치이고 — config 필드 doc 이 이미 그렇게 적는다 — 그래서 이 착지는 k8s/·deploy/ 를 한 줄도 바꾸지 않는다: base k8s/deployment.yaml 이 이미 그 게이트를 true 로 두고 kind 오버레이가 그것을 덮지 않으므로 프로덕션과 SUT 에서 같은 순간에 유효해진다. 새 env 를 만들었다면 오버레이가 그것을 켜기 전까지 아래 ③ 의 e2e 가 SUT 에서 빨간 채로 남았을 것이다.

② 아카이브 포맷 — 가산적 확장 — tar 최상위에 shared/scrollback·state/ 옆에 붙는다. 공유 디렉터리가 없는 세션은 부모와 바이트 동일한 아카이브를 내므로 claude-code 경로와 이미 저장된 모든 아카이브가 그대로 복원된다.

복원이 state 와 갈리는 지점이 하나 있고 그것이 이 축의 핵심 제약이다 — /shared 는 마운트 포인트라 rename 으로 덮을 수 없다. state 트리는 형제 디렉터리에 풀고 통째로 swap 하지만 그 방법을 여기 쓸 수 없어서, 공유 트리는 마운트 안의 스테이징으로 풀린 뒤(같은 파일시스템이라 설치는 여전히 rename) 전체 검증이 끝난 다음 최상위 엔트리 단위로 옮겨진다. 결정 둘:

  • 이미 있는 이름은 병합이 아니라 거부. 새로 프로비저닝된 클레임이 담고 있을 수 있는 것은 남의 라운드 잔여이거나 반쪽 복원뿐이다.
  • 마운트 없는 대상으로의 복원은 실패. 설정이 freeze 와 restore 사이에 꺼진 경우인데, 사람이 한 번 준 승인의 산출물이 말없이 사라지는 쪽이 더 나쁘다.

공유 트리는 이 에이전트가 drain 하지 않는 컨테이너(헬퍼 파드의 MCP)가 쓰므로, 걷는 도중 착지한 spill 은 per-entry SameFile 검사에 걸려 체크포인트를 실패시킨다 — 반쪽 파일이 아카이브되지 않는다.

③ 거짓이 된 단언 교체e2e_f4_helper_pod_test.gorestoreTheFreezeBranches 지시문이 지목한 그대로:

  • e2e: 거부 케이스 → 동결이 두 파드를 함께 회수하고, 복원이 새 쌍을 세우며, 워크로드가 이번 라운드 헬퍼 IP 로 다시 배선된다. 마지막 항은 동결 전에 SESSION_MCP_URL 을 실행 중인 컨테이너에서 재 두고 비교하므로 파드 재생성에 대한 가정이 아니라 측정이다.
  • 단위: TestSnapshotIsRefusedForApprovalGated둘로 갈림. 「전략이 등록되면 언다」(셸 체크포인터를 disabled 로 주입해 공허하지 않게 만든다)와 「게이트가 꺼진 배포에서는 여전히 fail-closed 다」 — 후자를 남기지 않으면 checkpointerFor 의 실패-닫힘 계약을 잃는다.
  • shared_volume_test.go왕복 단언을 더한다: 마커를 MCP 컨테이너로 쓰고 동결 전에 워크로드가 읽는 것을 먼저 확인한 뒤, 동결 → 옛 클레임 GC 확인 → 복원 → 이름이 다른 새 쌍의 새 클레임에서 같은 바이트를 읽는다.

④ 거짓이 된 문서 걷기 (docs/test/e2e.md) — ③ 이 이 파일의 네 자리를 한꺼번에 거짓으로 만든다:

  • §「남은 미검증 분기」의 두 행(동결 시 동반 회수 · 복원 시 새 쌍)을 내렸다.
  • 그 표의 이유 열거에서 (2)「approval-gated 에 스냅샷 전략이 없다」를 해소로 표시했다 — 그 이유를 쓰는 행이 하나도 남지 않았다. 번호는 뒤를 밀지 않도록 비워 뒀다.
  • 시나리오 7 행의 프로즈를 거부 서술에서 두 갈래 서술로 바꿨다.
  • AC-F4 규범 판정이 「이 판정을 실측이 뒤집는 경우는 하나, 아카이브 전략이 등록되는 때이고 그때는 파일이 스스로 빨개진다」라고 적어 둔 자리에 그 하나가 실제로 일어났다는 갱신을 붙였다. 판정의 결론은 바뀌지 않는다 — 관측 가능한 AC-F4 갈래 중 단언되지 않은 것은 여전히 없고, 달라진 것은 관측 가능한 것의 집합이다. 그 규범이 실제로 집행된 사례라 그렇게 적었다.
  • AC-F5 「선행이 하나 더 있다」 블록을 후반 착지로 갱신했다.

검증

로컬에서 CI 게이트를 1:1 재현했고 전부 초록이다.

게이트 결과
control-plane: gofmt -l . · go vet ./... · go vet -tags=e2e ./... · go test ./... OK
data-plane: gofmt -l . · go vet ./... · go test ./... OK
scripts/check-fidelity-allowlist.py rc=0 (부모와 동일 — seam 을 움직이지 않는다)
scripts/check-render-fidelity.py rc=0 (부모와 동일 — web/ 무접촉)
scripts/e2e/check-scenario-mapping.sh rc=0 (부모와 동일 — 매핑 단위를 움직이지 않는다)
scripts/check_comment_policy.py rc=0 (원장 증분 재판정 포함 — 아래)

클러스터가 필요한 e2e 는 여기서 못 돌린다 — ③ 의 세 단언은 e2e.yml(kind + API + browser)이 이 PR 에서 실제로 사는 부분이고, 그것이 이 변경의 진짜 게이트다.

주석 원장 증분 재판정 (R2/R4 — 같은 PR 요구사항)

등재 4행을 정책 절차대로 재판정했다. 합계 2989 → 3016.

신규 판정 결과 줄 수
#2 internal/service 11줄 제거 6 · 유지 5 (+ 거짓이 된 기존 6줄 삭제) 239 → 238
#4 data-plane/cmd/agent 63줄 제거 25 · 유지 38 927 → 965
#6 cmd/control-plane 10줄 제거 4 · 유지 6 (+ 낡은 1줄 교체) 65 → 70
#9 control-plane/test 19줄 제거 14 · 유지 5 (+ 지시문 등 기존 20줄 삭제) 441 → 426

부수적으로 틀린 주석 1건을 찾아 걷었다(sharedArchiveWorkload 의 doc 이 실제와 다른 반환값을 적고 있었다). 미등재 파일 shared_volume_test.go 는 이 행들에 편입하지 않았다 — 9~13차가 그 파일을 유예한 사유인 「살아 있는 소유 선언」이 바로 이 슬라이스였고, 이 PR 이 그것을 걷는다. 판정 자체는 선언이 사라진 뒤 자매 모델(tbm_session-platform-comment-redundancy)이 여는 패스의 몫이다.

⚠️ 검토자가 볼 것

  1. 유휴 리퍼가 이 타입을 처음으로 회수 대상으로 본다. 지금까지 approval-gated 세션은 ErrCheckpointDisabled 때문에 리퍼가 그냥 지나쳤다(reaper.go). 이 PR 뒤로는 유휴 한계를 넘긴 세션이 실제로 얼고 파드 쌍이 회수된다. 대기 중인 승인은 AC-F3 의 유휴 예외가 계속 막는다.
  2. 롤백에 한 방향 문이 하나 있다. 포맷은 가산적이라 구 아카이브 → 신 바이너리는 문제없지만, 이 변경이 살아 있는 동안 얼린 approval-gated 세션의 아카이브는 shared 멤버를 담으므로 되돌린 바이너리가 그것을 「unexpected entry」로 거절한다. 되돌릴 계획이라면 그 전에 approval-gated 스냅샷을 복원하거나 삭제해 비울 것.
  3. 프로덕션에서는 공유 볼륨이 여전히 꺼져 있다. base 가 RWX 클래스를 이름 짓지 않아(SESSION_SHARED_VOLUME_STORAGE_CLASS 미설정) 그 환경에서 이 PR 이 켜는 것은 「approval-gated 세션이 얼고 복원된다」까지다. shared/ 경로는 kind SUT 에서만 실행된다.
  4. 아카이브 상한은 공유 볼륨과 공유된다. maxClaudeArchiveBytes(4 GiB)·maxClaudeArchiveEntries(200k)가 두 트리의 합에 걸리므로, 가득 찬 1 GiB 볼륨 + 큰 워크스페이스는 체크포인트를 조용한 절단이 아니라 실패로 만든다(기존 동작 그대로).

범위 밖 (후속)


🔄 재작업 (2026-09-12) — 베이스 갱신 + 원장 4차 행 재측정

이 PR 은 승인까지 받은 뒤 실행 직전 신선도 게이트에서 멈췄다(task rct_20260912-0007 1차 시도 failed). 승인 대기 사이에 #128(12:30Z)·#129(12:48Z)가 착지해 mergeable_state=dirty 가 됐기 때문이고, 지침이 「충돌 해소는 실행이 아니라 재계획의 몫」으로 선을 그어 실행은 대상 레포에 아무것도 하지 않았다. 이 섹션이 그 재계획의 결과다.

충돌면은 한 파일·한 행이었다. origin/main(419eb2eb) 대비 이 브랜치의 15파일 중 교집합은 docs/comment-policy/ledger.md 하나이고 코드 델타는 한 파일도 겹치지 않는다(양측 전수 대조). 충돌 행은 4차 판정 패스(data-plane/cmd/agent) 한 행으로, #129 가 증분 재판정하며 지문을 옮긴 자리다.

해소 방법 — rebase+force 가 아니라 main 머지. PR 번호·승인 이력·리뷰 스레드를 보존하려고 origin/main 을 이 브랜치로 머지했다(일반 fast-forward push). 원장 값은 손으로 더하지 않고 게이트가 찍는 실측값을 파싱해 넣었다:

항목 조상 main(#129) 이 브랜치 재측정 후
4차 행 줄 수 927 927 (불변) 965 965 (불변)
4차 행 지문 d9018a9d32b6 5ee3891c5ca4 a34b6372619e 2600cc3562c9
합계 마커 2989 2989 3016 3016 (불변)

🔴 기계적 해소였다면 조용히 사라졌을 것 하나. git checkout --ours 로 원장을 해소하면 #129 가 그 행에 쓴 2026-09-12 (4) 증분 재판정 산문이 통째로 없어진다 — R2 는 줄 수와 지문만 보고 산문은 검사하지 않으므로 게이트가 초록인 채 지나간다. 양쪽이 같은 (4) 키를 집은 것이 원인이다. 해소: #129(4) 산문을 복원하고, 나중에 착지하는 이쪽 주석을 (5) 로 재발급했다(원장 행 + passes/2026-09-04-data-plane-agent.md 헤딩 양쪽).

따라서 1차 승인본 대비 실질 차이는 정확히 셋이다 — 지문 1개 재측정 · #129 산문 복원 · (4)→(5) 재발급. 코드·테스트·docs/test/e2e.md·doc-tracker.md 는 한 글자도 바뀌지 않았다.

재측정 후 로컬 전수 재검증: 정적 게이트 4종 rc=0, 양 모듈 gofmt -l 빈 출력 · go vet(기본/-tags=e2e/-tags=integration) · go test ./... 전부 rc=0.

재작업 2차 (2026-09-13) — #132 의 매핑 표 축소에 맞춰 재정렬

1차 재작업 뒤 승인 대기 사이에 #132(매핑 공간 Playwright 단독 축소)와 #134 가 착지해 다시 dirty 가 됐다.

충돌은 docs/test/e2e.md 한 파일 · 매핑 블록 한 곳뿐이었다. #132 가 시나리오↔e2e 매핑 표에서 Go 행 전체를 유예 표로 옮겨 표를 비웠기 때문에, 이 PR 이 고치려던 「시나리오 7」 행이 표에서 사라졌다 — 고칠 대상이 없으므로 main 판(빈 표)을 그대로 취했다.

나머지 4개 hunk 는 충돌 없이 그대로 산다:

  • AC-F4 규범 판정에 「그 하나가 실제로 일어났다」 갱신
  • AC-F5 「선행이 하나 더 있다」 → 후반 착지
  • 차단 이유 열거의 (2) 해소 표시
  • §「남은 미검증 분기」의 두 행 제거

증거: 매핑 게이트 출력이 main 과 바이트 동일(매핑 축 무접촉 — #132 가 그 게이트 스크립트도 다시 썼으므로 새 게이트로 잰 값이다). 코드 8파일은 이 머지로 한 바이트도 움직이지 않았다. 정적 게이트 4종 rc=0, 양 모듈 gofmt/go vet(3태그)/go test rc=0.

⚠️ 원장은 이번 머지에서 충돌하지 않았다(#132·#134ledger.md 를 건드리지 않았다). 1차 재작업의 재측정값(4차 행 965 / 2600cc3562c9, 합계 3016)이 그대로 유효하다.


🔄 재작업 3차 (2026-09-13) — 검토 반려 반영: 아카이브 게이트를 분리한다

반려 사유: 이 PR 이 approval-gated 체크포인터 등록을 기존 if cfg.claudeArchiveEnabled 블록 안에 넣어, 새 동작의 on/off 가 CLAUDE_CODE_ARCHIVE_ENABLED 한 값에 완전히 종속됐다. 되돌리기가 조건부 불가역인 고위험 변경에서 가장 싼 완화 수단(플래그 강하)의 granularity 를 잃는다 — 그 값을 내리면 잘 돌고 있던 claude-code 아카이브와 snapshotEnabled 까지 함께 떨어진다.

채택한 형태 (승인 협의 중 제시된 문구 그대로):

approvalGatedArchiveEnabled: envBool("SESSION_APPROVAL_GATED_ARCHIVE_ENABLED", claudeArchiveEnabled),

세 형태를 실측으로 저울질했다 — 1차 계획의 기각 근거가 과장이었던 지점

형태 킬 스위치 granularity 매니페스트 SUT
① 게이트 재사용 (반려된 형태) ❌ 없음 0줄 초록
② 폴백 없는 새 env ✅ 있음 k8s/ + deploy/ 동시 수정 오버레이가 켜기 전까지 e2e 빨강
폴백 기본값을 가진 새 env채택 ✅ 있음 0줄 초록

②의 비용이 ③에서 0 이 되는 것을 매니페스트 실측으로 확인했다 — k8s/deployment.yaml:108CLAUDE_CODE_ARCHIVE_ENABLED: "true"프로덕션 base 에서 세우고, deploy/kustomization.yaml(kind 오버레이)은 그 값을 덮지 않는다. 따라서 새 env 를 아무도 이름 짓지 않아도 두 환경 모두 true 를 상속한다.

🔴 ②를 택했다면 조용히 깨졌을 자리가 하나 더 있다. deploy/kustomization.yaml 의 patch 는 env 를 위치 인덱스로 집는다(env/1=DATA_PLANE_IMAGE · env/2=CRIU_ENABLED · env/3=DATA_PLANE_CLAUDE_CODE_IMAGE · env/4=DATA_PLANE_APPROVAL_GATED_IMAGE). base 의 env 목록 앞쪽에 한 항목이라도 끼면 그 넷이 전부 한 칸씩 밀려 오버레이가 엉뚱한 값을 덮는다. 「매니페스트 0줄」은 편의가 아니라 이 레포에서 안전한 형태다.

킬 스위치가 실제로 남기는 것

SESSION_APPROVAL_GATED_ARCHIVE_ENABLED=false 만 내리면 — approval-gated fail-closed(ErrCheckpointDisabled)로 돌아가고, claude-code 아카이브는 계속 돌며, 유휴 리퍼도 계속 산다(snapshotEnabled 가 세 플래그의 OR). ①에서는 셋이 한꺼번에 떨어졌다.

코드 델타 (3파일)

  • control-plane/cmd/control-plane/main.go — config 필드 추가 · loadConfig 상속 기본값 · 체크포인트 스토어 게이트를 새 플래그까지 확장(새 플래그만 켠 배포에서 cstore 가 nil 이 되지 않게) · 등록을 두 개의 독립 if 로 분리(claude-code 쪽 이미지 검사 유지) · snapshotEnabled 확장 · 게이트 열거 주석 정정.
  • control-plane/cmd/control-plane/main_test.goTestLoadConfigApprovalGatedArchiveGate: 미설정 상속(on/off) · 명시 false(킬 스위치) · 명시 true(독립) 4조합. 이 테이블이 없으면 나중에 기본값을 false 로 뒤집어도 아무것도 빨개지지 않는다.
  • control-plane/internal/service/workload_type_test.go — fail-closed 케이스 doc 을 새 게이트 이름 기준으로. 본문 무변경(서비스 계층이라 env 를 모른다).

새 base(068e921) 로의 머지 — 충돌 2파일

파일 충돌 해소
docs/comment-policy/ledger.md 합계 마커 한 줄 (3016 vs 3010) R4 실측값으로 재측정
docs/doc-tracker.md 불릿 2개(양쪽이 다시 씀) + 이력 표(양쪽이 행 추가) #138 의 https 전제 정정을 살리고 그 위에 AC-F5 착지 델타를 다시 얹었다 · 이력 두 행 모두 보존

docs/test/e2e.md자동 병합(충돌 0), 코드 8파일도 충돌 0 — 병합 직후 파일 단위 대조로 6파일이 바이트 단위 == #127 임을 확인했다(게이트 분리로 의도적으로 손댄 3파일 제외).

원장 재측정 — 손으로 더하지 않았다

게이트가 R2/R4 로 찍는 실측값을 정규식으로 파싱해 넣었다.

등재 → 재측정 지문
#2 internal/service/… 238 → 239 99956da0e422
#6 cmd/control-plane/main.go 70 → 75 e9bb2e6a3d8d
#7 …/main_test.go 86 → 90 92bb531c13c9
합계 마커 3037 → 3047 (R4 실측)

⚠️ 직전 증분이 「유지」로 남긴 2줄을 걷었다. 2026-09-12 증분은 「왜 env 이름을 바꾸지 않았는가」를 유지로 남겼는데, 이름을 바꾼 것이 아니라 게이트를 갈랐으므로 그 문장의 전제가 사라졌다. 남겨 두면 원장이 존재하지 않는 주석을 유지로 세게 된다 — R2 는 줄 수·지문만 보므로 이런 형태는 기계가 잡아 주지 않는다.

로컬 전수 검증 (전부 rc=0)

정적 게이트 4종 · 양 모듈 gofmt -l(빈 출력) · go vet 3태그(기본/-tags=e2e/-tags=integration) · go test ./.... 매핑 게이트 출력은 머지 직후와 바이트 동일(이 커밋이 매핑 축을 건드리지 않았다는 증거).

reconciler-bot added 4 commits September 12, 2026 11:51
`approval-gated` 에는 체크포인터가 등록돼 있지 않아 `POST /snapshot` 이 503 이었고, 그래서
AC-F5 가 현재형으로 단언하는 「동결·복원을 건너서도 이전 실행이 만든 파일이 남아 있다」를
실행할 코드 경로가 어느 환경에도 없었다. 세 조각을 함께 넣는다.

1. 조립 루트가 `CLAUDE_CODE_ARCHIVE_ENABLED` **한 게이트 안에서** `claude-code` 와
   `approval-gated` 둘 모두에 `AgentArchiveCheckpointer` 를 등록한다. 그 값은 타입이 아니라
   전략(아카이브를 `CHECKPOINT_S3_*` 에 쓰는 것)을 켜는 스위치이므로 `k8s/`·`deploy/` 는 한 줄도
   바뀌지 않는다 — base 가 이미 켜고 오버레이가 덮지 않는다.
2. 아카이브 포맷에 `shared/` 를 **가산적으로** 더한다. 공유 디렉터리가 없는 세션은 부모와
   바이트 동일한 아카이브를 내므로 `claude-code` 경로와 이미 저장된 아카이브가 전부 그대로
   복원된다. `/shared` 는 마운트 포인트라 state 트리의 디렉터리 rename-swap 을 쓸 수 없어,
   마운트 안 스테이징 → 전체 검증 → 최상위 엔트리 단위 설치로 짓는다. 이름 충돌은 병합이
   아니라 거부이고, 마운트 없는 대상으로의 복원은 조용히 버리지 않고 실패한다.
3. 그 전환이 거짓으로 만든 거부 단언 둘을 교체한다 — e2e 는 동결이 두 파드를 함께 회수하고
   복원이 새 쌍을 세우며 워크로드가 이번 라운드 헬퍼 IP 로 다시 배선됨을 사고, 단위는
   「전략이 등록되면 언다」와 「게이트가 꺼지면 여전히 fail-closed 다」로 갈린다.

왕복 자체는 `shared_volume_test.go` 가 배포된 SUT 에서 단언한다: 클레임이 옛 헬퍼와 함께
사라진 뒤 이름이 다른 새 쌍의 같은 경로에서 같은 바이트가 읽힌다.

⚠️ 유휴 리퍼가 이 타입을 처음으로 회수 대상으로 본다(지금까지는 `ErrCheckpointDisabled` 로
건너뛰었다). 대기 중인 승인은 AC-F3 의 유휴 예외가 계속 막는다.

reconciler: rct_20260912-0007 / tbm_session-platform-docs-impl
아카이브의 `shared` 루트 엔트리가 나르는 모드는 **소스 파드의 마운트 포인트**의 것이다.
그것을 복원 쪽에서 적용하면 실제로 닿는 자리는 버려지는 스테이징 디렉터리이고, 소유자
쓰기가 빠진 모드였다면 설치 직전에 그 안의 엔트리들이 갇힌다. 건너뛰되 디렉터리인지는
계속 확인한다 — 마운트의 모드는 클러스터의 것이지 아카이브가 정할 것이 아니다.

왕복 테스트에 마운트 자신의 모드가 불변임을 단언하는 갈래를 더했고, 주석 원장 4차 행을
그만큼 재판정했다(962 → 965, 합계 3013 → 3016).
`e2e_f4_helper_pod_test.go` 가 동결 동반 회수와 복원 재배선을 실제로 사게 됐으므로,
그 둘을 「아직 단언하지 못하는 경로」로 적던 자리들이 한꺼번에 거짓이 됐다.

- §「남은 미검증 분기」에서 두 행(동결 시 동반 회수 · 복원 시 새 쌍)을 내린다.
- 그 표의 이유 열거에서 (2)「approval-gated 에 스냅샷 전략이 없다」를 해소로 표시한다.
  그 이유를 쓰는 행이 하나도 남지 않았다. 번호는 뒤를 밀지 않도록 비워 둔다.
- 시나리오 7 행의 프로즈를 거부 서술에서 두 갈래 서술로 바꾼다.
- AC-F4 규범 판정이 「이 판정을 실측이 뒤집는 경우는 하나, 아카이브 전략이 등록되는
  때이고 그때는 파일이 스스로 빨개진다」고 적어 둔 자리에, **그 하나가 실제로
  일어났다**는 갱신을 붙인다. 결론은 그대로다 — 관측 가능한 AC-F4 갈래 중 단언되지
  않은 것은 여전히 없고, 달라진 것은 관측 가능한 것의 집합이다.
- AC-F5 「선행이 하나 더 있다」 블록을 후반 착지로 갱신한다.

이 파일을 처음에 범위 밖으로 둔 사유는 자매 축의 열린 PR(#123·#126)과 hunk 가 실측으로
겹친다는 것이었다. 그 PR 들이 처분되며 사유가 소멸해 같은 슬라이스에서 걷는다.
reconciler-bot added 2 commits September 12, 2026 13:26
#129(054aac9)가 4차 판정 패스 행(`data-plane/cmd/agent`)을 증분 재판정하면서
지문을 옮겼고, 같은 행을 이 브랜치도 옮겨 충돌했다. 코드 델타는 한 파일도
겹치지 않는다 — 교집합은 `docs/comment-policy/ledger.md` 한 파일뿐이다.

해소:
- 4차 행 지문을 병합 트리에서 재측정 (게이트 실측값 `2600cc3562c9`)
- #129 의 `2026-09-12 (4)` 증분 재판정 산문을 복원 (`--ours` 가 조용히 떨어뜨렸다)
- 이 브랜치의 주석을 `(5)` 로 재발급 — `(4)` 는 #129 가 먼저 가져갔다
  (ledger 행 + `passes/2026-09-04-data-plane-agent.md` 헤딩 양쪽)

줄 수(965)와 합계 마커(3016)는 재측정 후에도 불변 — #129 는 그 범위의
줄 수를 바꾸지 않았다(927 → 927).
#132(b772cb3)가 `docs/test/e2e.md` 의 시나리오↔e2e 매핑 표를 **Playwright
단독으로 축소**하면서 Go 행 전체를 유예 표로 옮겼고(-286/+196), 같은 PR 이
`scripts/e2e/check-scenario-mapping.sh` 도 다시 썼다. #134 는 이 브랜치와
무관한 파일만 건드린다.

해소: 충돌은 `docs/test/e2e.md` 한 파일 · 매핑 블록 한 곳뿐이다.
- 매핑 블록은 **main 판(빈 표)을 그대로 취했다** — 이 브랜치가 고치려던
  「시나리오 7」 행이 #132 로 **표에서 사라져** 고칠 대상이 없다.
- 나머지 4개 hunk(AC-F4 규범 갱신 · AC-F5 후반 착지 · 이유 (2) 해소 ·
  「남은 미검증 분기」 두 행 제거)는 충돌 없이 그대로 산다.

증거: 매핑 게이트 출력이 main 과 **바이트 동일**(매핑 축 무접촉).
코드 8파일은 이 머지로 한 바이트도 움직이지 않았다.
정적 게이트 4종 rc=0, 양 모듈 gofmt/vet(3태그)/test rc=0.
@dlddu-k3s-homelab-k3s-mcp

Copy link
Copy Markdown
Contributor Author

#138docs/doc-tracker.md 56행 항목 ③ 의 전제 문장을 정정했습니다 — validateFetchTargethttphttps 를 함께 받고(session_mcp_tools.go:228), 리터럴 url must be https 는 코드에 0건입니다. 밀린 좌표 둘(:184·:208)도 그 파일 자신의 2026-09-08 규범에 따라 심볼 이름(validateFetchTarget·http.DefaultClient)으로 옮겼습니다.

이 PR 은 같은 56행을 항목 ② 쪽에서 통째로 다시 쓰므로, rebase 하실 때 ⚠️ 전제 정정(2026-09-13) 문단을 반드시 보존해 주세요 — 두 편집은 같은 줄의 다른 문장 이라 둘 다 취하면 됩니다. doc-tracker.md 를 읽는 CI 게이트가 0개라 되돌아가도 아무도 모릅니다.

변경 이력 표 끝도 겹칩니다(이 PR 은 2026-09-12 (5) 를, #1382026-09-13 행을 붙였습니다) — 날짜 순으로 둘 다 남기면 됩니다.

덧붙여, 현재 이 PR 이 CONFLICTING/DIRTY 인 원인은 #138 이 아니라 #131 단독입니다(docs/comment-policy/ledger.md 에 15차 행이 들어오면서 이 PR 이 같은 자리에 더하는 행과 부딪힘). #138 이 만진 doc-tracker.md:56·test/e2e.md:770 은 이 PR 의 열린 hunk 와 행 단위로 겹치지만 충돌 원인은 아닙니다.

reconciler tbm_session-platform-docs-impl / rct_20260913-0002 실행 단계의 인계 코멘트입니다.

reconciler-bot added 3 commits September 13, 2026 13:30
… 재측정

- ledger.md: 합계 마커만 충돌(3016 vs 3010) → R4 실측 3037
- doc-tracker.md: 「미검증 참조 구현 전제」·「구현 사슬 부채」 두 불릿을 양쪽이 다시 썼다.
  #138 의 https 전제 정정(리터럴 0건 · validateFetchTarget 이 평문 http 도 받는다 · 좌표를
  심볼 이름으로)을 살리고 그 위에 AC-F5 착지 델타를 다시 얹었다. 변경 이력 표는 두 행을 모두 남겼다.
- docs/test/e2e.md 는 자동 병합. 코드 8파일 충돌 0.
검토 반려 반영. 새 env `SESSION_APPROVAL_GATED_ARCHIVE_ENABLED` 가 이 타입의 아카이브
전략 등록을 켜고, 기본값으로 `CLAUDE_CODE_ARCHIVE_ENABLED` 를 상속한다.

왜:
- 이 축은 되돌리기가 조건부 불가역(저장된 `shared` 아카이브를 되돌린 바이너리가 거절)이라
  사고 시 가장 싼 완화가 플래그 강하다. 게이트를 공유하면 그 완화가 잘 돌고 있던
  claude-code 아카이브와 유휴 리퍼(`snapshotEnabled`)까지 함께 끈다.
- 폴백 상속이라 `k8s/`·`deploy/` 는 0줄. base 가 `CLAUDE_CODE_ARCHIVE_ENABLED=true` 를
  세우고 kind 오버레이가 덮지 않으므로 두 환경 모두 상속으로 켜진 채다.
  (오버레이 patch 가 env 를 위치 인덱스 env/1~env/4 로 집으므로 base 에 env 를 더하지
  않는 것 자체가 안전 요건이다.)

- main.go: 스토어 게이트·등록·`snapshotEnabled` 를 새 플래그까지 포함하도록 가른다.
- main_test.go: 상속/강하 4조합을 `loadConfig` 테이블 테스트로 단언한다.
- workload_type_test.go: fail-closed 케이스 doc 을 새 게이트 이름으로 다시 쓴다.
- 원장 3행(#2·#6·#7) 재측정 + 게이트 분리 증분 재판정, 합계 3037 → 3047(R4 실측).
  직전 증분이 유지로 남긴 「왜 env 이름을 바꾸지 않았는가」 2줄은 전제가 사라져 걷었다.
- doc-tracker: AC-F5 착지 서술과 변경 이력 행을 새 게이트 기준으로 정정.
충돌은 `docs/comment-policy/ledger.md` 한 파일, 두 곳뿐이다(4차 행 · 합계 마커).
`docs/test/e2e.md` 는 자동 병합, 코드 충돌 0.

4차 행은 **양쪽이 각자 증분 재판정을 덧붙였다** — 이쪽은 `(5)` AC-F5 후반(아카이브),
main 은 #135 의 `ping` 표면 등재. 한쪽을 취하면 다른 쪽 판정 산문이 게이트가 초록인 채
사라지므로(R2 는 줄 수·지문만 본다) **두 증분을 날짜순으로 모두 남기고**, 줄 수·지문은
R2 실측(969 / `42c0300409bf`)으로, 합계는 R4 실측(3051)으로 재측정했다.
#135 증분의 러닝 토털만 `965 → 969` 로 다시 이었다(판정 자체는 927 위에서 잰 것 그대로).

로컬 전수 재검증 rc=0: 정적 게이트 4종 · 양 모듈 gofmt/vet(기본·e2e)/test.
코드 파일은 이 머지로 한 바이트도 움직이지 않았다.
@dlddu-k3s-homelab-k3s-mcp

Copy link
Copy Markdown
Contributor Author

⚠️ 계획 레코드(rct_20260912-0007) 기록 이후 커밋이 하나 더 붙었다 — 승인 전에 읽을 것

planner 가 planned 로 넘긴 직후 #135 가 머지되어 이 PR 이 다시 dirty 가 됐고, 같은 planner 세션이 그 충돌을 풀어 커밋 하나를 더 올렸다. 그래서 레코드의 preparation 이 적는 head 5979506 은 낡았다 — 현재 head 는 8dd8e13 이다.

코드는 한 바이트도 바뀌지 않았다. 추가 커밋은 순수 병합·재측정이다.

레코드 기록 시점 현재
head 5979506 8dd8e13
base 068e921 99c2166
mergeable_state clean clean
CI 14 success · 1 skipped · 0 failure 14 success · 1 skipped · 0 failure
kind e2e 125 PASS / 0 FAIL / 0 SKIP 125 PASS / 0 FAIL / 0 SKIP

추가 커밋(8dd8e13)이 한 일

충돌은 docs/comment-policy/ledger.md 한 파일, 두 곳뿐이었다(docs/test/e2e.md 는 자동 병합, 코드 충돌 0).

4차 판정 행에 양쪽이 각자 증분 재판정을 덧붙였다 — 이쪽은 (5) AC-F5 후반(아카이브), main 은 #135ping 표면 등재. 한쪽을 취하면 다른 쪽의 판정 산문이 게이트가 초록인 채 사라진다(R2 는 줄 수·지문만 보고 산문은 보지 않는다). 그래서 두 증분을 날짜순으로 모두 남기고 줄 수·지문은 R2 실측(969 / 42c0300409bf), 합계는 R4 실측(3051)으로 재측정했다. #135 증분의 러닝 토털만 965 → 969 로 다시 이었다(판정 자체는 927 위에서 잰 것 그대로다).

검증: 정적 게이트 4종 · 양 모듈 gofmt/go vet(기본·e2e)/go test 전부 rc=0, 그리고 위 CI 실측.

⚠️ 첫 CI 시도에서 e2e (kind + API + browser) 가 한 번 빨갰는데 변경과 무관한 인프라 flake 였다 — helm/kind-action 이 kind 바이너리를 받다 curl: (35) Recv failure: Connection reset by peer 로 죽어 클러스터가 아예 서지 않았다(Go 테스트 출력 0줄). 재실행 후 위 결과다.

승인 시 확인할 것은 바뀌지 않았다

이 PR 의 실질(독립 게이트 SESSION_APPROVAL_GATED_ARCHIVE_ENABLED + 폴백 상속, 그리고 AC-F5 후반 본체)은 검토가 본 것 그대로다. 다만 base 는 계속 흐르므로 승인 시점에 mergeable_state 를 다시 잴 것 — 이 task 는 같은 실패 모드(경합)로 이미 네 번 되돌아왔다.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants