프리플라이트 능동 플랜 재조립 (policy blocker에서 재렌더된 terraform으로)
이 설계에서는 deployment-preflight가 등록된
capability-mode 토글을 가진 policy_guardrail 또는 supply_chain_egress 차단 요인을
보고할 때 순수 함수 루프가 대응하는 방식을 설명합니다. 루프는 terraform 플랜을
능동적으로 다시 렌더링할 재정의를 계산하고 재검증할 수 있습니다. 지원되는 대체 형태는
애초에 거부되는 연산을 발행하지 않습니다. 기존
실행기를 통한 교정 PR 전달은 실제 운영 조립
배선이 완료된 뒤 활성화됩니다.
이 문서는 능동 재조립 루프, 수렴 및 중지 조건, 이를 전달하는 ActionType, 재조립할 수 있는 범위와 한계를 정의하는 기준 원본입니다. 차단 요인 분류법, 토글 매핑 표, 리포트 형태는 deployment-preflight-ko.md에 남습니다. 토글 모듈 자체는 infra/modules/preflight-toggles/에 있습니다.
고객-비종속: 어떤 denylist 값, 미러 엔드포인트, 토글 기본값도 상류에 하드코딩되지 않습니다. 업스트림은 재조립 메커니즘과 범용 토글 카탈로그를 제공합니다. 포크가 특정 가드레일 값과 토글을 사용하는 구성 요소의 연결을 제공합니다 (generic-scope.instructions.md).
왜 가능한가 (그리고 마법이 아닌가)
섹션 제목: “왜 가능한가 (그리고 마법이 아닌가)”필요한 기반은 이미 존재합니다. 능동 재조립은 다음 구성 요소를 종단 간 연결합니다:
- 탐지 -
FeasibilityProbe가 근거 있는ProbeFinding을 발행합니다 (feasibility_probe.py). - 매핑 - 발견 사항이 정확한 infra 서브 모듈과 배포를 준수하게 만드는 변수 재정의를
지정하는
ProbeResolution(kind=TERRAFORM_TOGGLE, autofix, module, set_vars)를 포함합니다. - 대체 렌더링 -
preflight-toggles 모듈이 준수하는
형태(
disk_provisioning=attach_existing,registry_source=acr_mirror, …)를 data-only Terraform으로 인코딩합니다.
설계가 추가한 두 조각의 현재 상태는 다음과 같습니다:
- 토글 제안 빌더(완료): 차단 요인이 해소된 결과의
autofix토글마다 타입이 지정된 제안을 렌더링하고 게시 전 게이트를 통과한 경우에만 제출합니다. 실제 운영 발행기는 아직 연결되지 않아 교정 PR을 열지는 않습니다 (check_publish.py). - 수렴 루프(완료): 호출자가 제공한 계획 렌더러와 재분석기를 통해 재조립된 플랜을 다시 검사하여 한 차단 요인의 수정이 다른 차단 요인을 조용히 도입하지 못하게 합니다.
재조립 루프
섹션 제목: “재조립 루프”재조립은 단발 작업이 아니라 범위가 제한된 결정론적 루프입니다. 토글이 차단 요인을 제거하지 않고 다른 곳으로 옮길 수 있으므로, 다시 렌더링한 플랜을 재검사해야 합니다.
terraform plan (JSON) -> preflight.analyze -> CLEAR -> 플랜 전달 / 머지 -> BLOCKED + 모든 blocking finding에 autofix 토글 있음 -> tfvars override 렌더 (재조립) -> 재-plan -> preflight.analyze로 복귀 (bounded) -> BLOCKED + 어떤 blocking finding에 autofix 토글 없음 -> hil (부분 autofix는 절대 적용 안 함)- 패스당 전부 적용 또는 전부 보류: 재조립은 모든 차단 발견 사항에
autofix토글이 있을 때만 진행합니다. 수동 해결이 필요한 차단 요인이 하나라도 있으면 전체 검사를hil로 라우팅합니다. 적용에 실패할 부분 수정은 실행하지 않습니다. - 검증기가 권한: 재조립된 플랜은 토글이 적용되었다는 이유로 신뢰되는 것이 아니라 동일한 결정론적 preflight(OPA 재검증 + what-if)에 의해 다시 확인됩니다. 이것은 quality-gate 규칙을 반영합니다: 실행 자격은 수정 생성기가 아니라 검증에 의해 부여됩니다.
수렴과 Stop-Condition
섹션 제목: “수렴과 Stop-Condition”루프는 반드시 종료되어야 합니다. 그 stop-condition은 최적화가 아니라 안전 불변식입니다:
| Stop-condition | 효과 |
|---|---|
max_reassembly_iterations (기본 3) 초과 | hil로 라우팅, 마지막 리포트 첨부 |
| 동일 발견 사항 id에 대해 동일 토글이 두 번 제안됨 | 비수렴 -> hil (flip-flop / 무한 루프 방지) |
| 재조립 패스가 이전 패스보다 더 많은 차단 발견 사항을 생성 | 회귀 -> hil |
| 프로브 하나라도 예외 발생 | 실패 시 차단 -> hil (일부 검사만 완료된 상태에서는 재조립하지 않음) |
반복 카운터, 발견 사항별 토글 이력, 캡은 하드코딩 리터럴이 아니라 구성이므로 포크가
core/를 편집하지 않고 튜닝할 수 있습니다.
ActionType: remediate.apply-preflight-toggle
섹션 제목: “ActionType: remediate.apply-preflight-toggle”능동 재조립은 새로운 특권 경로가 아닙니다. 일급 온톨로지 ActionType을 등록하여
기존 실행기를 재사용하므로,
7개 안전조건, 관찰 우선 게이트, 추가 전용 감사 기록이 함께 적용됩니다 (콘솔 어휘가 모든
액션을 타입드 파이프라인으로 라우팅하는 것과 같은 이유,
architecture.instructions.md
참조).
선언 (rule-catalog/action-types/ 아래에서 작성):
category: remediationtrigger_kind: both- preflight 루프가 차단 발견 사항에 대해 자동으로 개시하며, 운영자가 특정 토글을 요청할 수도 있습니다. 매개변수를 받는 액션입니다 (argument_schema:scope,finding_id,toggle_module,set_vars,reason). 따라서 정적인 리소스 상태 규칙이 아닙니다.execution_path: pr_native- 변경은 infra 리포에 대한 tfvars-override PR이며, 직접 기반 환경을 직접 변경하는 방식이 아닙니다.rollback_contract: pr_revert- PR을 revert하면 이전 tfvars가 복원됩니다. 재조립은 완전히 되돌릴 수 있으므로irreversible: false.default_mode: shadow- 첫 출시는 판단하고 PR을shadow라벨의 초안으로 렌더하며, 절대 자동 머지하지 않습니다.promotion_gate- 카테고리별로 강제 적용에 승격하기 전에 고정된 시나리오 집합에서 측정합니다 (토글 매핑의 false-positive 비율).preconditions-graph_fresh_within_seconds(플랜과 환경 프로파일이 최신이어야 함) 및no_conflicting_open_action_on_resource.stop_conditions- 위의 수렴 캡, 그리고 표준time_box_exceeded_seconds와provider_api_error_streak.blast_radius- 재정의가 건드리는 infra 변수의 집합. 캡보다 많은 토글을 뒤집을 재조립은 판단을 보류하고hil로 넘깁니다.
Autofix 자격 게이트
섹션 제목: “Autofix 자격 게이트”autofix PR은 다음이 모두 성립할 때만 자동 제안됩니다. 그렇지 않으면 발견 사항은
지침 + hil로 격하됩니다:
- 해결 방법의
kind가TERRAFORM_TOGGLE이고autofix: true입니다. - 토글이 데이터를 선언하기만 하는 결정론적 모듈이며 경로에 LLM이 없습니다.
- 재조립된 플랜이 preflight를 다시 통과합니다(검증기의 재검증).
- 재정의가 선언된
blast_radius안에 머뭅니다.
autofix: false 토글은 제안이나 차이를 제출하지 않습니다. 보고의 수동 지침으로
남고 전체 검사는 에스컬레이션되며, 운영자가 변수를 검토해 적용합니다.
게시 권한은 재검증이 부여한다
섹션 제목: “게시 권한은 재검증이 부여한다”차단 요인을 해소한 루프의 결과는 결정이지 인가가 아닙니다. 게시 전 게이트
(pre_publication_gate.py)
는 제안을 제출하기 직전에 누적된 오버라이드로 같은 분석기를 다시 실행하고, 새 리포트가
차단·오래된 증거·다른 범위이거나 루프가 에스컬레이션한 경우 패스 전체를 보류합니다. 모든
보류는 첫 제출 이전에 결정되므로 보류된 패스는 부분 제안 집합이 아니라 아무것도 제출하지
않고, 호출자는 이를 hil로 라우팅합니다
(게시 보류).
제출은 오퍼레이터 요청이 아니라 컨트롤 플레인 신호인 preflight_toggle_blocker로 인그레스를
통과합니다. Huginn은 명시적 오퍼레이터 요청에 대해서만 페이로드의 action_type을 존중하므로
이 신호는 ActionType을 싣지 않습니다. Forseti가 자체 이벤트 타입 테이블에서
remediate.apply-preflight-toggle을 바인딩하고 기본 hil 판정을 발행합니다. 따라서 차단
요인은 별개의 사람 승인 없이 provider commit에 도달할 수 없고, 어떤 인그레스 생산자도
ActionType을 위조할 수 없습니다. 같은 신뢰 게이트가 이 신호의 params도 버리므로, 조립된 실제 경로는 토글별 인수를 통제된 방식으로 실행기에 전달하는 과제를 여전히 남겨 둡니다.
액션 입도: 토글당 액션 1개
섹션 제목: “액션 입도: 토글당 액션 1개”하나의 재조립은 여러 토글을 적용할 수 있습니다(여러 발견 사항·여러 반복). 적용된 각
토글은 패스당 묶음 액션이 아니라 각자 remediate.apply-preflight-toggle 액션이
됩니다. 이렇게 하면 ActionType의 argument_schema가 단일 토글(finding_id +
toggle_module + set_vars)로 유지되어, 감사·롤백(pr_revert)·영향 범위가 토글
입도로 남고 각 토글이 해소하는 발견 사항에 1:1로 매핑됩니다. 루프는 토글별 출처 이력을
유지하며(AppliedToggle: finding_id, module, set_vars, scope), 제안
빌더(reassembly_proposals.py)가
토글당 제안 하나를 렌더링하여, 운영자 명령이 재진입하는 것과 같은 타입 지정 파이프라인
경계(ProposalSink -> Huginn -> Forseti -> Thor)을 통해 관찰 우선으로 제출합니다.
에스컬레이션된 결과는 제안을 내지 않습니다. 호출자가 hil로 라우팅합니다.
무엇을 재조립할 수 있고 없는가
섹션 제목: “무엇을 재조립할 수 있고 없는가”경계에 대한 정직함은 부수 조건이 아니라 안전 속성입니다:
- 재조립 가능 - 등록된 대체 렌더링을 가진 차단 요인: 인라인 disk 거부 ->
attach_existing; 차단된docker.ioegress ->acr_mirror; NSG 생성 거부 ->byo; PyPI egress 거부 -> 내부python_index_url; 순서 위반 ->dependency_ordering=strict. - 재조립 불가 (
hil로 라우팅) - 지원되는 대체가 없는 정책: 완전히 금지된 지역, 필수 태그 정책, 대체 SKU가 없는 거부된 SKU, 또는 범위가 지정된 예외나 거버넌스 결정만으로 해결할 수 있는 가드레일입니다. 이 경우MANUAL해결 방법을 발행하며 자동 재조립하지 않습니다.
Preflight 조립은 범위가 제한된 발견 사항 id, category, 근거 출처, 범위와 함께 반복되는
MANUAL 차단 요인을 Norns에 보고할 수 있습니다. Norns는 범위 다이제스트의 중복을 제거하고,
기본적으로 서로 다른 범위 세 개가 관측된 뒤 비활성 preflight-toggle-gap 후보 하나를 제안합니다. 후보는
검토된 새 대체 렌더링을 제안하고 표준 품질 게이트에 진입하지만 토글을
만들거나 배포 권한을 높이지 않습니다
(architecture.instructions.md § Rule 카탈로그).
안전 불변식
섹션 제목: “안전 불변식”재사용하는 실행기가 모든 재조립 액션에 7개 안전조건을 강제합니다:
- Stop-condition - ActionType에 선언된 위의 수렴 캡.
- Rollback 경로 -
pr_revert입니다. 재정의 PR의 커밋 하나를 되돌리면 이전 플랜으로 복원할 수 있으며, PR 본문에 롤백 참조를 포함합니다. - 영향 범위 한도 - 재조립은 선언된 infra 변수만 건드립니다. 캡 초과는
hil로 판단을 보류합니다. - Audit-log 항목 - 순수 함수 루프는 감사에 사용할 수 있는 최종 사유와 토글 출처 이력을 반환합니다. 실제 운영 조립이 이를 실행기에 제출할 때 해시 체인 감사 레코드를 쓰며, 아직 연결되지 않은 현재 코어 기본 요소 자체는 감사 저장소를 호출하지 않습니다.
재조립은 관찰 우선로 출시됩니다: 토글 매핑의 false-positive 비율이 측정되고 카테고리가 강제 적용으로 명시적으로 승격되기 전까지, PR은 판단·렌더되지만 머지되지 않는 초안입니다.
서브시스템 레이아웃
섹션 제목: “서브시스템 레이아웃”| 조각 | 위치 | 상태 |
|---|---|---|
| 발견 사항 위의 토글 해석 | feasibility_probe.py | 완료 |
| capability-mode 토글 모듈 | infra/modules/preflight-toggles/ | 완료 (data-only) |
| 준비 상태 리포트 + 판정 | core/deploy_preflight/report.py | 완료 |
| 리포트 -> PR 체크 게시 | core/deploy_preflight/check_publish.py | 완료 (리포트만) |
| 수렴 루프 + stop-condition | core/deploy_preflight/reassemble.py | 완료 |
remediate.apply-preflight-toggle ActionType | rule-catalog/action-types/ | 완료 |
| overrides -> 액션 제안 (토글당 하나) | core/deploy_preflight/reassembly_proposals.py | 완료 |
| 게시 전 재검증 | core/deploy_preflight/pre_publication_gate.py | 완료 |
| 반복 수동 차단 요인 -> inert 후보 | agents/norns.py | 완료 (caller-supplied 관측) |
| 참조 소비자 배선 (토글 하나) | infra/modules/preflight-toggles/reference-disk-consumer/ | 완료 (포크가 복사) |
조립 배선: ProposalSink + 라이브 트리거 | 조립 루트 + delivery/azure/preflight/ | 남음 |
core/는 FeasibilityProbe 프로토콜과 호출자가 제공한 ProposalSink 호출 가능 객체만 참조합니다.
재조립 루프는 어떤 클라우드 SDK도 구성하지 않고 PR을 직접 열지 않습니다 - 재정의를
결정하고 (ActionType을 통해) 실행기에 넘기며, 실행기가 게시와 불변식을 소유합니다.
전달 증분
섹션 제목: “전달 증분”각 증분은 개별적으로 검토할 수 있습니다:
- Docs-first (이 문서) - 루프, ActionType, 한계. (완료)
remediate.apply-preflight-toggleActionType YAML + 스키마 검증. (완료)- 범위가 제한된 수렴 루프, 관찰 모드, 속성 테스트와 함께: “동일 토글은 절대 두 번 적용 안 함”, “부분 차단 요인 -> 사람 승인”, “재조립된 플랜은 재검증됨”, “회귀 -> 사람 승인”, “raise하는 reanalyze에 실패 시 차단”. (완료)
infra/아래 참조 소비자 배선 하나(disk_provisioning토글)로 포크가 복사-붙여넣기 시작점을 갖게 함. (완료)- overrides-to-executor 단계: 적용된 각 토글을
remediate.apply-preflight-toggle제안으로 렌더하고(토글당 액션 1개, granularity A) 타입드 파이프라인 경계를 통해 제출합니다. (완료) - 조립 배선(게이트가 걸린
ProposalSink을 런타임 조립 루트에서 Huginn ingest에 연결) + 실제 정책 발견 사항을 루프에 공급하고 tfvars-override PR을 여는 라이브 Azure 어댑터 (preflight 라이브 어댑터 착지 후, 관찰 우선). 게이트가 걸린 경계와 Huginn/Forseti 통합 테스트는 존재하며, 조립 루트와 라이브 트리거는 (남음).
- deployment-preflight-ko.md - 프로브 분류법, 토글 매핑 표, 리포트 형태
- infra/modules/preflight-toggles/README.md - capability-mode 토글 모듈
- architecture.instructions.md - 컨트롤 루프, quality 게이트, 안전 불변식, 액션 온톨로지
- project-structure-ko.md - 실행기, 모듈 경계, infra 서브 모듈 패턴
- risk-classification-ko.md - 차단 발견 사항이
hil로 라우팅되는 방식 - coding-conventions.instructions.md - 7개 안전조건, 관찰 우선, ActionType 계약
관련 문서
섹션 제목: “관련 문서”| 알아볼 내용 | 읽을 문서 |
|---|---|
| 구현 상태 및 남은 작업 | 구현 원장 |